SpringAop&代理

SpringAOP

实现代码复用,保持原有代码的结构(流程)不被改变,增强功能。

静态代理

在程序运行前就以经存在代理类的字节码文件,代理对像和被代理对象在运行前已经被确定。

优点:1、业务类只需关注业务类本身,保证了业务类的重用性。

​ 2、把真实对象隐藏起来,保护真实对象。

缺点:1、代理对象的某个接口只服务于某一种类型的对象,也就是说每一个真实对象都得创建-个代理对象。
2、如果需要代理的方法很多,则要为每一种方法都进行代理处理。
3、如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

自己理解:新建一个类,被代理类作为一个成员变量,在创建代理类时,传入一个被代理类为代理类的成员变量赋值,在代理类中重写被代理类中需要代理的方法,在方法中用成员变量调用被代理类的方法,参入参数,获取返回值,在执行被代理类的方法的前后增加功能,作到不不改变业务逻辑的情况下,为系统增加功能。

例:

被代理类:

public class AdminServiceImpl implements AdminService{

    @Override
    public Admin login(String name, String pwd) {
        if ("121".equals(name) && "212".equals(pwd)){
            return new Admin("121","212");
        }
        return null;
    }

    @Override
    public Admin add(String name, String pwd) {
        return new Admin("121","212");
    }
}

代理类:

public class AdminProxy {
    private AdminService service;

    public AdminProxy(AdminService service){
        this.service=service;
    }
    public Admin login(String name, String pwd) {
        Admin login = service.login(name, pwd);
        if (login!=null){
            System.out.println("登录成功");
        }
        return login;
    }
}

测试:

@Test
public void oneTest(){
    AdminService service = new AdminServiceImpl();
    AdminProxy proxy = new AdminProxy(service);
    Admin login = proxy.login("121", "212");
    System.out.println(login);
}

动态代理

在程序运行期间由jvm通过反射等机制动态的创建出代理对象的字节码,代理对象和真实对象的关系是在程序运行时才确定的。

jdk动态代理

Java动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象

被代理类必须有一个实现de接口。

自己理解:运用Proxy.newProxyInstance(1,2,3)方法传入代理类的1ClassLoader对象,2interfaces数组和3一个实现InvocationHandler接口的自定义对象,在自定义对象中重写invoke方法,使用method.getName()过去当前调用被代理类对象的方法名,执行相应的增强操作。

例:

public class ProxyTest2 {
    //被代理类
    AdminService service = new AdminServiceImpl();
    @Test
    public void Test2(){
        //获取AdminService的ClassLoader对象
        ClassLoader classLoader = service.getClass().getClassLoader();

        //获取Class<?>[] interfaces
        Class<?>[] interfaces = new Class[]{AdminService.class};

        //代理类
        AdminService adminService = (AdminService)Proxy.newProxyInstance(classLoader, interfaces, new MyAdminProxy2());

        Admin login = adminService.login("121", "212");

        System.out.println(login);

        adminService.add("21","21");
    }

    class MyAdminProxy2 implements InvocationHandler{

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(method.getName());
            Object invoke = method.invoke(service, args);
            return invoke;
        }
    }
}

cglib动态代理

生成当前被代理类的子类

原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对fina修饰的类进行代理。
1、CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
2、要求类不能是final的,要拦截的方法要是非final、 非static、 非private的。
3、动态代理的最小单位是类(所有类中的方法都会被处理);

自己理解:为一个Enhancer对象设置superClass即被代理类,和实现MethodInterceptor的自定义回调函数相关联,重写intercept方法,执行增强操作(获取当前方法,执行对应增强操作)。调用Enhancer的creat函数创建关联对象,强转为被代理类对象,即可调用被代理类对象的方法。在执行方法时,会执行相对应的增强操作。传入参数和返回值都不会改变。

例:

public class EnhancerTest {

    @Test
    public void test1(){
        //生成Enhancer对象
        Enhancer enhancer = new Enhancer();

        //指定Enhancer的字节码对象
        enhancer.setSuperclass(AdminServiceImpl.class);

        //设置回调函数
        enhancer.setCallback(new MyMethodInterceptor());

        AdminService service = (AdminService)enhancer.create();

        Admin login = service.login("121", "212");

        System.out.println(login);
    }

    class MyMethodInterceptor implements MethodInterceptor {

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            Object invoke = methodProxy.invokeSuper(o, objects);
            if ("login".equals(method.getName())){
                System.out.println("login");
            }
            return invoke;
        }
    }
}

AOP术语

连接点:一般指方法

切入点:需要增强功能的方法集合,确定通知的执行点

通知:真实用于增强方法的操作(功能)

切面:切入点+通知

通知

执行顺序

![1595509349378](

1595509349378.png)

基于xml的aop

被代理类:

public class User {
    public boolean login(String id){
        if ("11".equals(id)){
            System.out.println("用户存在");
            //return true;
            throw new RuntimeException("test");
        }
        return false;
    }
}

代理类:

public class demo1 {
    //前置通知
    public void before(){
        System.out.println("有人查询用户是否存在");
    }
    //后置通知
    public void after(){
        System.out.println("after");
    }
    //完成通知
    public void afterRun(){
        System.out.println("afterRun");
    }
    //异常通知
    public void afterThrowing(){
        System.out.println("afterThrowing");
    }

    //环绕通知
    public void around(ProceedingJoinPoint point){
        try {
            point.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

xml配置:

<bean class="com.offcn.User" id="user"/>

<bean class="com.offcn.demo1" id="demo1"/>

<aop:config>
    <aop:pointcut id="pointcut" expression="execution(* com.hello.User.*(..))"/>
    
    <aop:aspect ref="demo1">
        <!--前置通知-->
        <aop:before method="before" pointcut-ref="pointcut"/>
        <!--后置通知-->
        <aop:after method="after" pointcut-ref="pointcut"/>
        <!--完成通知-->
        <aop:after-returning method="afterRun" pointcut-ref="pointcut"/>
        <!--异常通知-->
        <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>

基于注解的aop

编辑切面类(通知所在类):

@Aspect
@Component
public class demo2 {
    //前置通知
    public void before(){
        System.out.println("有人查询用户是否存在");
    }
    //后置通知
    public void after(){
        System.out.println("after");
    }
    //完成通知
    public void afterRun(){
        System.out.println("afterRun");
    }
    //异常通知
    public void afterThrowing(){
        System.out.println("afterThrowing");
    }

    //环绕通知
    public void around(ProceedingJoinPoint point){
        try {
            point.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

必须注解@Aspect声明当前类是切面类

在spring中配置AOP代理自动配置

开启AOP代理自动配置

xml方式

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

注解方式

在配置类上添加

@EnableAspectJAutoProxy注解

全局与局部切入点

局部切入点

切入点方法:在切面类中定义方法,注解@Before、@After、@AfterReturning、@AfterThrowing、@Around

例:

无参局部

//前置通知
@Before("execution(* com.hello.User.*(..))")
public void before(){
    System.out.println("有人查询用户是否存在");
}

有参局部

//后置通知
@After("execution(* com.hello.User.*(..)) && args(id)")
public void after(String id){
    System.out.println(id);
    System.out.println("after");
}

全局切入点

在切面类中声明一个返回值为void的空方法,注解Pointcut(“aspect表达式 && 参数列表”)

例:

 @Pointcut("execution(* com.hello.spring.service.*.*(..)) && args(userName,userAge,birth)")
    public void beforePointCut(){

    }

调用:

@Before("beforePointCut() && args(userName,userAge,birth)")
public void check(String userName, int userAge, Date birth) {
    System.out.println("权限校验......" + userName + "  " + userAge + "  " + birth);
}

作用:使check方法成为beforePointCut()的aspect表达式匹配的内容的before通知,传入参数。

注:args中的参数名要和check的参数列表的参数名相同

​ beforePointCut()方法可以在其他的饿切面类中调用

有参环绕通知:

@Around("execution(* com.hello.User.*(..)) && args(id)")
public boolean around(ProceedingJoinPoint point,String id){
    try {
        boolean proceed = (boolean) point.proceed();
        System.out.println(proceed);
        return proceed;
    } catch (Throwable throwable) {
       throwable.printStackTrace();
    }
    return false;
}

1595572790771

猜你喜欢

转载自www.cnblogs.com/ygfcoder/p/13387507.html