zoukankan      html  css  js  c++  java
  • spring成神之路第三十六篇:@EnableAspectJAutoProxy、@Aspect 中通知顺序详解

    这是aop最后一篇文章了,本文带你深入理解@EnableAspectJAutoProxy,这篇文章可能会颠覆你以前所掌握的一些知识,让你醍醐灌顶,欣喜若狂!

    1、Aop相关阅读

    阅读本文之前,需要先掌握下面几篇文章内容,不然会比较吃力。

    1. Spring系列第15篇:代理详解(java动态代理&CGLIB代理)
    2. Spring系列第30篇:jdk动态代理和cglib代理
    3. Spring系列第31篇:Aop概念详解
    4. Spring系列第32篇:AOP核心源码、原理详解
    5. Spring系列第33篇:ProxyFactoryBean创建AOP代理
    6. Spring系列第34篇:@Aspect中@Pointcut 12种用法
    7. Spring系列第35篇:@Aspect中5中通知详解

    目前为止,上面的文章基本上都是硬编码的方式一个个为目标对象创建代理的,但是,我们使用spring的过程中,可能需要对大量bean创建代理,比如我们需拦截所有的service的方法,打印耗时日志,对大量service bean做权限校验,做事务处理等等,这些功能都可以通过aop的方式来实现,若采用硬编码的方式一个个创建,那是相当难受的事情。

    Spring中提供了批量的方式,为容器中符合条件的bean,自动创建代理对象,也就是我们本文要说的@EnableAspectJAutoProxy

    2、@EnableAspectJAutoProxy自动为bean创建代理对象

    @EnableAspectJAutoProxy可以自动为spring容器中符合条件的bean创建代理对象,@EnableAspectJAutoProxy需要结合@Aspect注解一起使用。用法比较简单,下面我们通过案例来看一下。

    先在com.javacode2018.aop.demo11.test1包中定义2个bean

    UserService bean

    package com.javacode2018.aop.demo11.test1;

    import org.springframework.stereotype.Component;

    @Component
    public class UserService {
        public void say(){
            System.out.println("我是UserService");
        }
    }

    CarService bean

    package com.javacode2018.aop.demo11.test1;

    import org.springframework.stereotype.Component;

    @Component
    public class CarService {
        public void say() {
            System.out.println("我是CarService");
        }
    }

    通过Aspect来定义一个前置通知,需要拦截上面2个bean的所有方法,在方法执行之前输出一行日志

    package com.javacode2018.aop.demo11.test1;

    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.springframework.stereotype.Component;

    @Component //@1
    @Aspect //@2
    public class Aspect1 {

        @Pointcut("execution(* com.javacode2018.aop.demo11.test1..*(..))"//@3
        public void pc() {
        }

        @Before("com.javacode2018.aop.demo11.test1.Aspect1.pc()"//@4
        public void before(JoinPoint joinPoint) {
            System.out.println("我是前置通知,target:" + joinPoint.getTarget()); //5
        }
    }

    Aspect1中有4个关键信息

     

    @1:使用 @Component 将这个类注册到spring容器;

     

    @2:使用 @Aspect 标注着是一个 AspectJ 来定义通知的配置类;

     

    @3:定义切入点,目前的配置,会拦截test1包及其子包中所有类的所有方法,而CarService和UserService刚好满足,所以会被拦截;

     

    @4:定义一个前置通知,这个通知会对@3定义的切入点起效;

    @5:目标方法执行执行,输出一行日志;

    下面来一个spring配置类

    package com.javacode2018.aop.demo11.test1;

    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;

    @ComponentScan //@1
    @EnableAspectJAutoProxy //@2
    public class MainConfig1 {
    }

    @1:@ComponentScan 注解的作用会扫描当前包中的类,将标注有 @Component 的类注册到spring容器;

     

    @2:@EnableAspectJAutoProxy 这个注解比较关键,用来启用自动代理的创建,简单点理解:会找到容器中所有标注有@Aspect注解的bean以及Advisor类型的bean,会将他们转换为Advisor集合,spring会通过Advisor集合对容器中满足切入点表达式的bean生成代理对象,整个都是spring容器启动的过程中自动完成的,原理稍后介绍。

    下面来测试用例代码,启动spring容器,加载配置类,验证

    package com.javacode2018.aop.demo11;

    import com.javacode2018.aop.demo11.test1.CarService;
    import com.javacode2018.aop.demo11.test1.MainConfig1;
    import com.javacode2018.aop.demo11.test1.UserService;
    import org.junit.Test;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;

    public class AspectTest11 {

        @Test
        public void test1() {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
            context.register(MainConfig1.class);
            context.refresh();
            UserService userService = context.getBean(UserService.class);
            userService.say();
            CarService carService = context.getBean(CarService.class);
            carService.say();
        }
      
    }

    运行输出

    我是前置通知,target:com.javacode2018.aop.demo11.test1.UserService@dc7df28
    我是UserService
    我是前置通知,target:com.javacode2018.aop.demo11.test1.CarService@821330f
    我是CarService

    3、通知执行顺序

    @EnableAspectJAutoProxy 允许spring容器中通过Advisor 、@Aspect 来定义通知,当spring容器中存在多个Advisor、@Aspect时,组成的拦截器调用链顺序是什么样的呢?在介绍这个之前,我们需要先回顾一下aop中4种通知相关知识。

    spring aop中4种通知(Advice)

    org.aopalliance.intercept.MethodInterceptor
    org.springframework.aop.MethodBeforeAdvice
    org.springframework.aop.AfterReturningAdvice
    org.springframework.aop.ThrowsAdvice

    所有的通知最终都需要转换为MethodInterceptor类型的通知,然后组成一个MethodInterceptor列表,我们称之为方法调用链或者拦截器链,上面列表中后面3通过下面的转换器将其包装为MethodInterceptor类型的通知:

    org.springframework.aop.MethodBeforeAdvice -> org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor
    org.springframework.aop.AfterReturningAdvice -> org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor
    org.springframework.aop.ThrowsAdvice -> org.springframework.aop.framework.adapter.ThrowsAdviceInterceptor

    下面我们再来看一下4种通知的用法和执行过程,以方便我们理解其执行顺序。

    org.aopalliance.intercept.MethodInterceptor:方法拦截器

    方法拦截器,这个比较强大,可以在方法执行前后执行一些增强操作,其他类型的通知最终都会被包装为 MethodInterceptor 来执行。

    下面我们自定义一个MethodInterceptor

    class MyMethodInterceptor implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("我是MethodInterceptor start");
            //调用invocation.proceed()执行下一个拦截器
            Object result = invocation.proceed();
            System.out.println("我是MethodInterceptor end");
            //返回结果
            return result;
        }
    }

    org.springframework.aop.MethodBeforeAdvice:方法前置通知

    方法前置通知,可以在方法之前定义增强操作。

    下面我们自定义一个MethodBeforeAdvice

    class MyMethodBeforeAdvice implements MethodBeforeAdvice {

        @Override
        public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
            System.out.println("我是MethodBeforeAdvice");
        }
    }

    MethodBeforeAdvice最终会被包装为MethodBeforeAdviceInterceptor类型,然后放到拦截器链中去执行,通过MethodBeforeAdviceInterceptor代码可以理解MethodBeforeAdvice的执行过程

    public class MethodBeforeAdviceInterceptor implements MethodInterceptorBeforeAdviceSerializable {

        private final MethodBeforeAdvice advice;

        public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
            this.advice = advice;
        }


        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            //调用MethodBeforeAdvice的before方法,执行前置通知
            this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
            //执行下一个拦截器
            return mi.proceed();
        }

    }

    org.springframework.aop.AfterReturningAdvice:方法返回通知

    方法返回通知,用来在方法执行完毕之后执行一些增强操作。

    下面我们自定义一个AfterReturningAdvice

    class MyAfterReturningAdvice implements AfterReturningAdvice {

        @Override
        public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
            System.out.println("我是AfterReturningAdvice");
        }
    }

    AfterReturningAdvice最终会被包装为AfterReturningAdviceInterceptor类型,然后放到拦截器链中去执行,通过AfterReturningAdviceInterceptor代码可以理解AfterReturningAdvice的执行过程

    public class AfterReturningAdviceInterceptor implements MethodInterceptorAfterAdviceSerializable {

        private final AfterReturningAdvice advice;

        public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
            this.advice = advice;
        }


        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            //执行下一个拦截器,可以获取目标方法的返回结果
            Object retVal = mi.proceed();
            //调用方法返回通知的afterReturning方法,会传入目标方法的返回值等信息
            this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
            return retVal;
        }

    }

    org.springframework.aop.ThrowsAdvice:异常通知

    当目标方法发生异常时,可以通过 ThrowsAdvice 来指定需要回调的方法,我们在此可以记录一些异常信息,或者将异常信息发送到监控系统等。

    下面我们自定义一个ThrowsAdvice

    /**
     * 用来定义异常通知
     * 方法名必须是afterThrowing,格式参考下面2种定义
     * 1. public void afterThrowing(Exception ex)
     * 2. public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
     */

    class MyThrowsAdvice implements ThrowsAdvice {
        public void afterThrowing(Method method, Object[] args, Object target, Exception ex) {
            System.out.println("我是ThrowsAdvice");
        }
    }

    ThrowsAdvice最终会被包装为ThrowsAdviceInterceptor类型,然后放到拦截器链中去执行,通过ThrowsAdviceInterceptor代码可以理解ThrowsAdvice的执行过程,ThrowsAdviceInterceptor 构造参数传入一个自定义的 ThrowsAdvice 对象

    public class ThrowsAdviceInterceptor implements MethodInterceptorAfterAdvice {

        private final Object throwsAdvice;

        public ThrowsAdviceInterceptor(Object throwsAdvice) {
            this.throwsAdvice = throwsAdvice;
        }

        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            try {
                return mi.proceed();
            } catch (Throwable ex) {
                //调用 ThrowsAdvice 中的 afterThrowing 方法来处理异常
                this.throwsAdvice.afterThrowing(。。。。);
                //将异常继续往外抛
                throw ex;
            }
        }
    }

    拦截器链执行过程

    假如目标方法上面有好几个通知,调用目标方法执行,spring会将所有的通知转换得到一个MethodInterceptor列表,然后依次按照下面的方式执行,会先调用第一个拦截器的MethodInterceptor#invoke(MethodInvocation invocation)方法,会传递一个MethodInvocation类型的参数,在此方法中,我们可以调用MethodInvocation#processd方法去执行第二个拦截器,然后依次按照这样的过程执行,到了最后一个MethodInterceptor中,再次调用MethodInvocation#processd时,会调用目标方法。

    4种通知的执行顺序

    结合上面的过程,假如目标方法上面依次添加了下面4种通知,我们来分析一下他们的执行过程

    class MyMethodInterceptor implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("我是MethodInterceptor start");
            //调用invocation.proceed()执行下一个拦截器
            Object result = invocation.proceed();
            System.out.println("我是MethodInterceptor end");
            //返回结果
            return result;
        }
    }

    class MyMethodBeforeAdvice implements MethodBeforeAdvice {

        @Override
        public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
            System.out.println("我是MethodBeforeAdvice");
        }
    }

    class MyAfterReturningAdvice implements AfterReturningAdvice {

        @Override
        public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
            System.out.println("我是AfterReturningAdvice");
        }
    }

    class MyThrowsAdvice implements ThrowsAdvice {
        public void afterThrowing(Method method, Object[] args, Object target, Exception ex) {
            System.out.println("我是ThrowsAdvice");
        }
    }

    根据通知的规定,非MethodInterceptor类型的通知,都会被包装为MethodInterceptor类型的,上面除了第一个之外,其他3个都会被转换为MethodInterceptor,转换之后变成了下面这样:

    class MyMethodInterceptor implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            System.out.println("我是MethodInterceptor start");
            //调用mi.proceed()执行下一个拦截器
            Object retVal = mi.proceed();
            System.out.println("我是MethodInterceptor end");
            //返回结果
            return retVal;
        }
    }

    class MyMethodBeforeAdvice implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            System.out.println("我是MethodBeforeAdvice");
            //调用mi.proceed()执行下一个拦截器
            Object retVal = mi.proceed();
            return retVal;
        }
    }

    class MyAfterReturningAdvice implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            //调用mi.proceed()执行下一个拦截器
            Object retVal = mi.proceed();
            System.out.println("我是AfterReturningAdvice");
            return retVal;
        }
    }

    class MyThrowsAdvice implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            try {
                //调用mi.proceed()执行下一个拦截器
                return mi.proceed();
            } catch (Throwable ex) {
                System.out.println("我是ThrowsAdvice");
                throw ex;
            }
        }
    }

    根据通知链的执行过程,最终变成了下面这样:

    System.out.println("我是MethodInterceptor start");
    System.out.println("我是MethodBeforeAdvice");
    Object retVal = null;
    try {
        retVal = 通过反射调用目标方法获取返回值;
    catch (Throwable ex) {
        System.out.println("我是ThrowsAdvice");
        throw ex;
    }
    System.out.println("我是AfterReturningAdvice");
    System.out.println("我是MethodInterceptor end");
    return retVal;

    将上面4个通知用到下面目标对象中

    public static class Service3 {
        public String say(String name) {
            return "你好:" + name;
        }
    }

    执行下面代码生成代理,然后通过代理调用say方法

    Service3 target = new Service3();
    Service3 proxy = 对target通过aop生成代理对象;
    System.out.println(proxy.say("路人"));

    被4个拦截器包裹之后,System.out.println(proxy.say("路人"));执行过程变成了下面这样

    System.out.println("我是MethodInterceptor start");
    System.out.println("我是MethodBeforeAdvice");
    Object retVal = null;
    try {
        retVal = target.say("路人");
    catch (Throwable ex) {
        System.out.println("我是ThrowsAdvice");
        throw ex;
    }
    System.out.println("我是AfterReturningAdvice");
    System.out.println("我是MethodInterceptor end");
    System.out.println(retVal);

    再次简化

    System.out.println("我是MethodInterceptor start");
    System.out.println("我是MethodBeforeAdvice");
    Object retVal = null;
    try {
        retVal = "你好:" + name;
    catch (Throwable ex) {
        System.out.println("我是ThrowsAdvice");
        throw ex;
    }
    System.out.println("我是AfterReturningAdvice");
    System.out.println("我是MethodInterceptor end");
    System.out.println(retVal);

    最终会输出

    我是MethodInterceptor start
    我是MethodBeforeAdvice
    我是AfterReturningAdvice
    我是MethodInterceptor end
    你好:路人

    上案例代码,我们来看一下最终的执行结果是不是和我们分析的一样,下面为需要被代理的类Service3以及需要使用的4个通知。

    package com.javacode2018.aop.demo11.test3;

    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.aop.AfterReturningAdvice;
    import org.springframework.aop.MethodBeforeAdvice;
    import org.springframework.aop.ThrowsAdvice;
    import org.springframework.lang.Nullable;

    import java.lang.reflect.Method;

    public class MoreAdvice {

        public static class Service3 {
            public String say(String name) {
                return "你好:" + name;
            }
        }

        public static class MyMethodInterceptor implements MethodInterceptor {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("我是MethodInterceptor start");
                //调用invocation.proceed()执行下一个拦截器
                Object result = invocation.proceed();
                System.out.println("我是MethodInterceptor end");
                //返回结果
                return result;
            }
        }

        public static class MyMethodBeforeAdvice implements MethodBeforeAdvice {

            @Override
            public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
                System.out.println("我是MethodBeforeAdvice");
            }
        }

        public static class MyAfterReturningAdvice implements AfterReturningAdvice {

            @Override
            public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
                System.out.println("我是AfterReturningAdvice");
            }
        }

        public static class MyThrowsAdvice implements ThrowsAdvice {
            public void afterThrowing(Method method, Object[] args, Object target, Exception ex) {
                System.out.println("我是ThrowsAdvice");
            }
        }
    }

    对应测试代码

    @Test
    public void test3() {
        //创建目标对象
        MoreAdvice.Service3 target = new MoreAdvice.Service3();
        //创建代理工厂,通过代理工厂来创建代理对象
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        //依次为目标对象添加4种通知
        proxyFactory.addAdvice(new MoreAdvice.MyMethodInterceptor());
        proxyFactory.addAdvice(new MoreAdvice.MyMethodBeforeAdvice());
        proxyFactory.addAdvice(new MoreAdvice.MyAfterReturningAdvice());
        proxyFactory.addAdvice(new MoreAdvice.MyThrowsAdvice());
        //获取到代理对象
        MoreAdvice.Service3 proxy = (MoreAdvice.Service3) proxyFactory.getProxy();
        //通过代理对象访问目标方法say
        System.out.println(proxy.say("路人"));
    }

    运行输出

    我是MethodInterceptor start
    我是MethodBeforeAdvice
    我是AfterReturningAdvice
    我是MethodInterceptor end
    你好:路人

    和我们上面分析的确实一模一样。

    4、单个@Aspect中多个通知的执行顺序

    @Aspect标注的类中可以使用下面5种注解来定义通知

    @Before
    @Around
    @After
    @AfterReturning
    @AfterThrowing

    当单个@Aspect中定义了多种类型的通知时,@EnableAspectJAutoProxy内部会对其进行排序,排序顺序如下

    @AfterThrowing
    @AfterReturning
    @After
    @Around
    @Before

    下面我们来个@Aspect类,同时定义5种通知,然后来一步步分析一下其执行的属性。

    package com.javacode2018.aop.demo11.test4;

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;

    @Component
    @Aspect
    public class Aspect4 {
        @Pointcut("execution(* com.javacode2018.aop.demo11.test4.Service4.*(..))")
        public void pc() {
        }

        @Before("pc()")
        public void before() {
            System.out.println("@Before通知!");
        }

        @Around("pc()")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("@Around通知start");
            Object result = joinPoint.proceed();
            System.out.println("@Around绕通知end");
            return result;
        }

        @After("pc()")
        public void after() throws Throwable {
            System.out.println("@After通知!");
        }

        @AfterReturning("pc()")
        public void afterReturning() throws Throwable {
            System.out.println("@AfterReturning通知!");
        }

        @AfterThrowing("pc()")
        public void afterThrowing() {
            System.out.println("@AfterThrowing通知!");
        }

    }

    上面会拦截com.javacode2018.aop.demo11.test4.Service4这个类中的所有方法,下面是Service4的源码。

    package com.javacode2018.aop.demo11.test4;

    import org.springframework.stereotype.Component;

    @Component
    public class Service4 {
        public String say(String name) {
            return "你好:" + name;
        }
    }

    来个spring的配置类,使用@EnableAspectJAutoProxy标注

    package com.javacode2018.aop.demo11.test4;

    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;

    @EnableAspectJAutoProxy
    @ComponentScan
    public class MainConfig4 {
    }

    测试代码

    @Test
    public void test4(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(MainConfig4.class);
        context.refresh();
        Service4 service4 = context.getBean(Service4.class);
        System.out.println(service4.say("路人"));
    }

    运行依次输出如下

    @Around通知start
    @Before通知!
    @Around绕通知end
    @After通知!
    @AfterReturning通知!
    你好:路人

    卧槽,这输出好像和我们上面说的不一样的,上面说的会按照下面的顺序执行,这到底是什么情况?

    @AfterThrowing
    @AfterReturning
    @After
    @Around
    @Before

    别急,排序规则和输出结果都没有问题,听我慢慢分析,下面的分析非常重要,注意看了

    5、@Aspect中5种通知回顾

    5种通知对应的Advice类

    @Aspect中通过5中注解来定义通知,这些注解最终都需要转换为Advice去执行,转换关系如下

    通知对应的Advice类
    @AfterThrowing org.springframework.aop.aspectj.AspectJAfterThrowingAdvice
    @AfterReturning org.springframework.aop.aspectj.AspectJAfterReturningAdvice
    @After org.springframework.aop.aspectj.AspectJAfterAdvice
    @Around org.springframework.aop.aspectj.AspectJAroundAdvice
    @Before org.springframework.aop.aspectj.AspectJMethodBeforeAdvice

    重点就在于表格右边的Advice类,当了解这些Advice的源码之后,他们的执行顺序大家就可以理解了,我们来看一下这些类的源码,重点看invoke方法

    @AfterThrowing:AspectJAfterThrowingAdvice

    public class AspectJAfterThrowingAdvice implements MethodInterceptor {

        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            try {
                //执行下一个拦截器
                return mi.proceed();
            } catch (Throwable ex) {
                //通过反射调用@AfterThrowing标注的方法
                //继续抛出异常
                throw ex;
            }
        }
    }

    AspectJAfterThrowingAdvice 实现了 MethodInterceptor 接口,不需要进行包装。

    @AfterReturning:AspectJAfterReturningAdvice

    AspectJAfterReturningAdvice 源码:

    public class AspectJAfterReturningAdvice implements AfterReturningAdvice {
        @Override
        public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
            // 调用@AfterReturning标注的方法
        }
    }

    AspectJAfterReturningAdvice 实现了 AfterReturningAdvice 接口,是一个方法返回通知,不是MethodInterceptor类型的,所以最终需包装为MethodInterceptor类型,变成下面这样

    public class AspectJAfterReturningAdvice implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            //执行下一个拦截器
            Object retVal = mi.proceed();
            //调用@AfterReturning标注的方法
            return retVal;
        }
    }

    @After:AspectJAfterAdvice

    AspectJAfterAdvice 源码:

    public class AspectJAfterAdvice implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            try {
                //执行下一个拦截器
                return mi.proceed();
            } finally {
                //调用@After标注的方法
            }
        }
    }

    AspectJAfterAdvice 实现了 MethodInterceptor接口,所以最终执行的时候不需要进行包装。

    注意 invoke 方法内部使用了 try...finally 的方式,@After方法的调用放在了finally中,所以不管是否有异常,@After类型的通知都会被执行。

    @Around:AspectJAroundAdvice

    AspectJAroundAdvice 源码:

    public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptorSerializable {
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            return 调用 @Around标注的方法 ;
        }
    }

    AspectJAroundAdvice  实现了 MethodInterceptor接口,最终执行的时候也不需要进行包装。

    @Before:AspectJMethodBeforeAdvice

    AspectJMethodBeforeAdvice 源码:

    public class AspectJMethodBeforeAdvice implements MethodBeforeAdviceSerializable {
        @Override
        public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
            invokeAdviceMethod(getJoinPointMatch(), nullnull);
        }
    }

    AspectJMethodBeforeAdvice 实现了 MethodBeforeAdvice接口,是一个前置通知,不是MethodInterceptor类型的,所以最终需包装为MethodInterceptor类型,变成下面这样

    public class AspectJMethodBeforeAdvice implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation mi) throws Throwable {
            //调用@Before标注的方法
            //执行下一个拦截器
            return mi.proceed();
        }
    }

    6、分析单个@Aspect中多个通知执行顺序

    大家对@Aspect中5种通知内容理解之后,我们再回头看一下代码Aspect4中定义的5个通知

    public class Aspect4 {
        @Pointcut("execution(* com.javacode2018.aop.demo11.test4.Service4.*(..))")
        public void pc() {
        }

        @Before("pc()")
        public void before() {
            System.out.println("@Before通知!");
        }

        @Around("pc()")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("@Around通知start");
            Object result = joinPoint.proceed();
            System.out.println("@Around绕通知end");
            return result;
        }

        @After("pc()")
        public void after() throws Throwable {
            System.out.println("@After通知!");
        }

        @AfterReturning("pc()")
        public void afterReturning() throws Throwable {
            System.out.println("@AfterReturning通知!");
        }

        @AfterThrowing("pc()")
        public void afterThrowing() {
            System.out.println("@AfterThrowing通知!");
        }

    }

    我们给出的结论是,会按照下面的顺序执行

    @AfterThrowing
    @AfterReturning
    @After
    @Around
    @Before

    按照上面的顺序,一步步来分析。

    先执行第1个通知@AfterThrowing,变成下面这样

    try {
        //执行下一个拦截器
        return mi.proceed();
    catch (Throwable ex) {
        System.out.println("@AfterThrowing通知!");
        //继续抛出异常
        throw ex;
    }

    mi.processed()会执行第2个通知@AfterReturning,变成了下面这样

    try {
        //执行下一个拦截器
        Object retVal = mi.proceed();
        System.out.println("@AfterReturning通知!");
        return retVal;
    catch (Throwable ex) {
        System.out.println("@AfterThrowing通知!");
        //继续抛出异常
        throw ex;
    }

    继续mi.proceed()执行第3个通知@After,变成了下面这样

    try {
        Object result = null;
        try {
            //执行下一个拦截器
            result = mi.proceed();
        } finally {
            System.out.println("@After通知!");
        }
        System.out.println("@AfterReturning通知!");
        return retVal;
    catch (Throwable ex) {
        System.out.println("@AfterThrowing通知!");
        //继续抛出异常
        throw ex;
    }

    继续mi.proceed()执行第4个通知@Around,变成了下面这样

    try {
        Object result = null;
        try {
            System.out.println("@Around通知start");
            result = joinPoint.proceed();
            System.out.println("@Around绕通知end");
            return result;
        } finally {
            System.out.println("@After通知!");
        }
        System.out.println("@AfterReturning通知!");
        return retVal;
    catch (Throwable ex) {
        System.out.println("@AfterThrowing通知!");
        //继续抛出异常
        throw ex;
    }

    继续joinPoint.proceed()执行第5个通知@Before,变成了下面这样

    try {
        Object result = null;
        try {
            System.out.println("@Around通知start");
            System.out.println("@Before通知!");
            result = mi.proceed();
            System.out.println("@Around绕通知end");
            return result;
        } finally {
            System.out.println("@After通知!");
        }
        System.out.println("@AfterReturning通知!");
        return retVal;
    catch (Throwable ex) {
        System.out.println("@AfterThrowing通知!");
        //继续抛出异常
        throw ex;
    }

    继续joinPoint.proceed()会调用目标方法,变成了下面这样

    try {
        Object result = null;
        try {
            System.out.println("@Around通知start");
            System.out.println("@Before通知!");
            result = // 通过反射调用目标方法; //@1
            System.out.println("@Around绕通知end");
            return result;
        } finally {
            System.out.println("@After通知!");
        }
        System.out.println("@AfterReturning通知!");
        return retVal;
    catch (Throwable ex) {
        System.out.println("@AfterThrowing通知!");
        //继续抛出异常
        throw ex;
    }

    将上面的@1替换为目标方法的调用,就变成下面这样了

    try {
        Object result = null;
        try {
            System.out.println("@Around通知start");
            System.out.println("@Before通知!");
            result = service4.say("路人");
            System.out.println("@Around绕通知end");
            return result;
        } finally {
            System.out.println("@After通知!");
        }
        System.out.println("@AfterReturning通知!");
        return retVal;
    catch (Throwable ex) {
        System.out.println("@AfterThrowing通知!");
        //继续抛出异常
        throw ex;
    }

    所以最终输出

    @Around通知start
    @Before通知!
    @Around绕通知end
    @After通知!
    @AfterReturning通知!
    你好:路人

    7、@EnableAspectJAutoProxy中为通知指定顺序

    @EnableAspectJAutoProxy用在spring环境中,可以通过@Aspect以及Advisor来定义多个通知,当spring容器中有多个@Aspect、Advisor时,他们的顺序是什么样的呢?

    我们先看一下如何为@Aspect自定义Advisor指定顺序。

    为@Aspect指定顺序:用@Order注解

    需要在@Aspect标注的类上使用@org.springframework.core.annotation.Order注解,值越小,通知的优先级越高。

    @Aspect
    @Order(1)
    public class AspectOrder1{}

    为Advisor指定顺序:实现Ordered接口

    自定义的Advisor通过org.springframework.core.Ordered接口来指定顺序,这个接口有个public int getOrder()方法,用来返回通知的顺序。

    spring为我们提供了一个Advisor类型的抽象类org.springframework.aop.support.AbstractPointcutAdvisor,这个类实现了Ordered接口,spring中大部分Advisor会是继承AbstractPointcutAdvisor,若需要自定义Advisor,也可以继承这个类,这个类的getOrder方法比较关键,来看一下

    public abstract class AbstractPointcutAdvisor implements PointcutAdvisorOrderedSerializable {

        @Nullable
        private Integer order;

        public void setOrder(int order) {
            this.order = order;
        }

        @Override
        public int getOrder() {
            //若当前Advisor指定了order,则直接返回
            if (this.order != null) {
                return this.order;
            }
            //获取当前类中配置的通知对象Advice
            Advice advice = getAdvice();
            //若advice实现了Ordered接口,这从advice中获取通知的顺序
            if (advice instanceof Ordered) {
                return ((Ordered) advice).getOrder();
            }
            //否则通知的优先级最低,Integer.MAX_VALUE
            return Ordered.LOWEST_PRECEDENCE;
        }
    }

    Spring为我们提供了一个默认的Advisor类:DefaultPointcutAdvisor,这个类就继承了AbstractPointcutAdvisor,通常我们可以直接使用DefaultPointcutAdvisor来自定义通知。

    8、多个@Aspect、Advisor排序规则

    排序规则

    1、在spring容器中获取@Aspect、Advisor类型的所有bean,得到一个列表 list1

    2、对list1按照order的值升序排序,得到结果list2

    3、然后再对list2中@Aspect类型的bean内部的通知进行排序,规则

    @AfterThrowing
    @AfterReturning
    @After
    @Around
    @Before

    4、最后运行的时候会得到上面排序产生的方法调用链列表去执行。

    案例

    下面我们定义2个@Aspect类,一个Advisor类,并且给这3个都指定,然后来验证一下通知执行的顺序。

    先定义目标类

    package com.javacode2018.aop.demo11.test2;

    import org.springframework.stereotype.Component;

    @Component
    public class Service2 {
        public String say(String name) {
            return "你好:" + name;
        }
    }

    Aspect1:第2个@Aspect

    package com.javacode2018.aop.demo11.test2;

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;

    @Aspect
    @Order(1)
    @Component
    public class MyAspect1 {

        @Pointcut("execution(* com.javacode2018.aop.demo11.test2.Service2.*(..))")
        public void pc() {
        }

        @Before("pc()")
        public void before() {
            System.out.println("MyAspect1 @Before通知!");
        }

        @Around("pc()")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("MyAspect1 @Around通知start");
            Object result = joinPoint.proceed();
            System.out.println("MyAspect1 @Around绕通知end");
            return result;
        }

        @After("pc()")
        public void after() throws Throwable {
            System.out.println("MyAspect1 @After通知!");
        }

        @AfterReturning("pc()")
        public void afterReturning() throws Throwable {
            System.out.println("MyAspect1 @AfterReturning通知!");
        }

        @AfterThrowing("pc()")
        public void afterThrowing() {
            System.out.println("MyAspect1 @AfterThrowing通知!");
        }

    }

    Aspect1:第2个@Aspect

    package com.javacode2018.aop.demo11.test2;

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;

    @Aspect
    @Order(3)
    @Component
    public class MyAspect2 {

        @Pointcut("execution(* com.javacode2018.aop.demo11.test2.Service2.*(..))")
        public void pc() {
        }

        @Before("pc()")
        public void before() {
            System.out.println("MyAspect2 @Before通知!");
        }

        @Around("pc()")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("MyAspect2 @Around通知start");
            Object result = joinPoint.proceed();
            System.out.println("MyAspect2 @Around绕通知end");
            return result;
        }

        @After("pc()")
        public void after() throws Throwable {
            System.out.println("MyAspect2 @After通知!");
        }

        @AfterReturning("pc()")
        public void afterReturning() throws Throwable {
            System.out.println("MyAspect2 @AfterReturning通知!");
        }

        @AfterThrowing("pc()")
        public void afterThrowing() {
            System.out.println("MyAspect2 @AfterThrowing通知!");
        }

    }

    自定义一个Advisor

    package com.javacode2018.aop.demo11.test2;

    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    import org.springframework.aop.support.DefaultPointcutAdvisor;
    import org.springframework.stereotype.Component;

    @Component
    public class Advisor1 extends DefaultPointcutAdvisor {

        public Advisor1() {
            MethodInterceptor methodInterceptor = new MethodInterceptor() {
                @Override
                public Object invoke(MethodInvocation invocation) throws Throwable {
                    System.out.println("Advisor1 start");
                    Object result = invocation.proceed();
                    System.out.println("Advisor1 end");
                    return result;
                }
            };
            this.setAdvice(methodInterceptor);
        }

        @Override
        public int getOrder() {
            return 2;
        }
    }

    来个spring配置类

    标注@EnableAspectJAutoProxy来启用自动化的aop功能

    package com.javacode2018.aop.demo11.test2;

    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;

    @ComponentScan
    @EnableAspectJAutoProxy
    public class MainConfig2 {
    }

    测试代码

    @Test
    public void test2() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(MainConfig2.class);
        context.refresh();
        Service2 service2 = context.getBean(Service2.class);
        System.out.println(service2.say("路人"));
    }

    运行输出

    MyAspect1 @Around通知start
    MyAspect1 @Before通知!
    Advisor1 start
    MyAspect2 @Around通知start
    MyAspect2 @Before通知!
    MyAspect2 @Around绕通知end
    MyAspect2 @After通知!
    MyAspect2 @AfterReturning通知!
    Advisor1 end
    MyAspect1 @Around绕通知end
    MyAspect1 @After通知!
    MyAspect1 @AfterReturning通知!
    你好:路人

    结果分析

    下面我们一步步来推出结果为什么是上面这样。

    先获取spring容器中@Aspect、Advisor类型的所有bean,根据其order升序排序,得到:

    Aspect1:顺序是1
    Advisor1:顺序是2
    MyAspect2:顺序是3

    然后对每个Aspect内部的通知进行排序,根据单个@Aspect内部通知排序规则,可以得到:

    Aspect1:顺序是1
     @AfterThrowing
        @AfterReturning
        @After
        @Around
        @Before

    Advisor1:顺序是2

    MyAspect2:顺序是3
     @AfterThrowing
        @AfterReturning
        @After
        @Around
        @Before

    下面将代码拿过来一步步填充。

    先对Aspect1进行填充,得到:

    try {
        Object result = null;
        try {
            System.out.println("MyAspect1 @Around通知start");
            System.out.println("MyAspect1 @Before通知!");
            result = mi.proceed(); //@1
            System.out.println("MyAspect1 @Around绕通知end");
            return result;
        } finally {
            System.out.println("MyAspect1 @After通知!");
        }
        System.out.println("MyAspect1 @AfterReturning通知!");
        return retVal;
    catch (Throwable ex) {
        System.out.println("MyAspect1 @AfterThrowing通知!");
        //继续抛出异常
        throw ex;
    }

    @1执行mi.proceed()会调用下一个拦截器,即Advisor1中定义的拦截器,然后会得到下面代码:

    try {
        Object result = null;
        try {
            System.out.println("MyAspect1 @Around通知start");
            System.out.println("MyAspect1 @Before通知!");
            System.out.println("Advisor1 start");
            result = invocation.proceed(); //@2
            System.out.println("Advisor1 end");
            System.out.println("MyAspect1 @Around绕通知end");
            return result;
        } finally {
            System.out.println("MyAspect1 @After通知!");
        }
        System.out.println("MyAspect1 @AfterReturning通知!");
        return retVal;
    catch (Throwable ex) {
        System.out.println("MyAspect1 @AfterThrowing通知!");
        //继续抛出异常
        throw ex;
    }

    @2执行mi.proceed()会调用下一个拦截器,即Aspect2中定义的拦截器,而Aspect2Aspect1类似,然后会得到下面代码:

    try {
        Object result = null;
        try {
            System.out.println("MyAspect1 @Around通知start");
            System.out.println("MyAspect1 @Before通知!");
            System.out.println("Advisor1 start");
            try {
                try {
                    System.out.println("MyAspect2 @Around通知start");
                    System.out.println("MyAspect2 @Before通知!");
                    result = mi.proceed(); //@3
                    System.out.println("MyAspect2 @Around绕通知end");
                    return result;
                } finally {
                    System.out.println("MyAspect2 @After通知!");
                }
                System.out.println("MyAspect2 @AfterReturning通知!");
            } catch (Throwable ex) {
                System.out.println("MyAspect2 @AfterThrowing通知!");
                //继续抛出异常
                throw ex;
            }
            System.out.println("Advisor1 end");
            System.out.println("MyAspect1 @Around绕通知end");
            return result;
        } finally {
            System.out.println("MyAspect1 @After通知!");
        }
        System.out.println("MyAspect1 @AfterReturning通知!");
        return retVal;
    catch (Throwable ex) {
        System.out.println("MyAspect1 @AfterThrowing通知!");
        //继续抛出异常
        throw ex;
    }

    @3继续执行mi.proceed(),此时会调用目标方法say("路人"),然后就进化成下面这样了

    try {
        Object result = null;
        try {
            System.out.println("MyAspect1 @Around通知start");
            System.out.println("MyAspect1 @Before通知!");
            System.out.println("Advisor1 start");
            try {
                try {
                    System.out.println("MyAspect2 @Around通知start");
                    System.out.println("MyAspect2 @Before通知!");
                    result = "你好:路人";
                    System.out.println("MyAspect2 @Around绕通知end");
                    return result;
                } finally {
                    System.out.println("MyAspect2 @After通知!");
                }
                System.out.println("MyAspect2 @AfterReturning通知!");
            } catch (Throwable ex) {
                System.out.println("MyAspect2 @AfterThrowing通知!");
                //继续抛出异常
                throw ex;
            }
            System.out.println("Advisor1 end");
            System.out.println("MyAspect1 @Around绕通知end");
            return result;
        } finally {
            System.out.println("MyAspect1 @After通知!");
        }
        System.out.println("MyAspect1 @AfterReturning通知!");
        return retVal;
    catch (Throwable ex) {
        System.out.println("MyAspect1 @AfterThrowing通知!");
        //继续抛出异常
        throw ex;
    }

    再来和输出结果对比一下,是完全一致的。

    MyAspect1 @Around通知start
    MyAspect1 @Before通知!
    Advisor1 start
    MyAspect2 @Around通知start
    MyAspect2 @Before通知!
    MyAspect2 @Around绕通知end
    MyAspect2 @After通知!
    MyAspect2 @AfterReturning通知!
    Advisor1 end
    MyAspect1 @Around绕通知end
    MyAspect1 @After通知!
    MyAspect1 @AfterReturning通知!
    你好:路人

    9、@EnableAspectJAutoProxy另外2个功能

    这个注解还有2个参数,大家看一下下面的注释,比较简单,就不用案例演示了。

    public @interface EnableAspectJAutoProxy {

     /**
      * 是否基于类来创建代理,而不是基于接口来创建代理
      * 当为true的时候会使用cglib来直接对目标类创建代理对象
      * 默认为 false:即目标bean如果有接口的会采用jdk动态代理来创建代理对象,没有接口的目标bean,会采用cglib来创建代理对象
      */

     boolean proxyTargetClass() default false;

     /**
      * 是否需要将代理对象暴露在ThreadLocal中,当为true的时候
      * 可以通过org.springframework.aop.framework.AopContext#currentProxy获取当前代理对象
      */

     boolean exposeProxy() default false;

    }

    10、@EnableAspectJAutoProxy原理

    @EnableAspectJAutoProxy会在spring容器中注册一个bean

    org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator

    AnnotationAwareAspectJAutoProxyCreatorBeanPostProcessor类型的,BeanPostProcessor大家应该比较熟悉了,bean后置处理器,可以在bean声明周期中对bean进行操作,比如对bean生成代理等;而AnnotationAwareAspectJAutoProxyCreator就是对符合条件的bean,自动生成代理对象,源码就这里就不细说了,有兴趣的可以从postProcessAfterInitialization方法看,比较简单。

    11、总结

    今天内容还是挺多的,大家好好消化一下。

    主要要掌握@EnableAspectJAutoProxy中多个@Aspect、Advisor时,通知的执行顺序,这个多看看,要理解其原理,记起来才会更容易,用起来也会更顺手。

    如发现文章有错误、对内容有疑问,都可以在文章下面留言,或者加我微信(itsoku)交流,每周会挑选出一位热心小伙伴,送上一份精美的小礼品,快来关注我吧!

    12、案例源码

    https://gitee.com/javacode2018/spring-series

    路人甲java所有案例代码以后都会放到这个上面,大家watch一下,可以持续关注动态。

    来源:https://mp.weixin.qq.com/s?__biz=MzA5MTkxMDQ4MQ==&mid=2648935500&idx=2&sn=5fb794139e476a275963432948e29362&scene=21#wechat_redirect

  • 相关阅读:
    poj 1743 Musical Theme 后缀数组
    poj 1743 Musical Theme 后缀数组
    cf 432D Prefixes and Suffixes kmp
    cf 432D Prefixes and Suffixes kmp
    hdu Data Structure? 线段树
    关于position和anchorPoint之间的关系
    ios POST 信息
    CALayers的代码示例
    CALayers详解
    ios中得sqlite使用基础
  • 原文地址:https://www.cnblogs.com/konglxblog/p/15501703.html
Copyright © 2011-2022 走看看