36.@EnableAspectJAutoProxy、@Aspect中通知顺序详解

目前为止,上面的文章基本上都是硬编码的方式一个个为目标对象创建代理的,但是,我们使用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

 
  1. package com.javacode2018.aop.demo11.test1;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class UserService {
  5. public void say(){
  6. System.out.println("我是UserService");
  7. }
  8. }

CarService bean

 
  1. package com.javacode2018.aop.demo11.test1;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class CarService {
  5. public void say() {
  6. System.out.println("我是CarService");
  7. }
  8. }

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

 
  1. package com.javacode2018.aop.demo11.test1;
  2. import org.aspectj.lang.JoinPoint;
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.aspectj.lang.annotation.Before;
  5. import org.aspectj.lang.annotation.Pointcut;
  6. import org.springframework.stereotype.Component;
  7. @Component //@1
  8. @Aspect //@2
  9. public class Aspect1 {
  10. @Pointcut("execution(* com.javacode2018.aop.demo11.test1..*(..))") //@3
  11. public void pc() {
  12. }
  13. @Before("com.javacode2018.aop.demo11.test1.Aspect1.pc()") //@4
  14. public void before(JoinPoint joinPoint) {
  15. System.out.println("我是前置通知,target:" + joinPoint.getTarget()); //5
  16. }
  17. }

Aspect1中有4个关键信息

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

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

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

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

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

下面来一个spring配置类

 
  1. package com.javacode2018.aop.demo11.test1;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.EnableAspectJAutoProxy;
  4. @ComponentScan //@1
  5. @EnableAspectJAutoProxy //@2
  6. public class MainConfig1 {
  7. }

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

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

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

 
  1. package com.javacode2018.aop.demo11;
  2. import com.javacode2018.aop.demo11.test1.CarService;
  3. import com.javacode2018.aop.demo11.test1.MainConfig1;
  4. import com.javacode2018.aop.demo11.test1.UserService;
  5. import org.junit.Test;
  6. import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  7. public class AspectTest11 {
  8. @Test
  9. public void test1() {
  10. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  11. context.register(MainConfig1.class);
  12. context.refresh();
  13. UserService userService = context.getBean(UserService.class);
  14. userService.say();
  15. CarService carService = context.getBean(CarService.class);
  16. carService.say();
  17. }
  18. }

运行输出

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

3、通知执行顺序

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

spring aop中4种通知(Advice)

 
  1. org.aopalliance.intercept.MethodInterceptor
  2. org.springframework.aop.MethodBeforeAdvice
  3. org.springframework.aop.AfterReturningAdvice
  4. org.springframework.aop.ThrowsAdvice

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

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

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

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

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

下面我们自定义一个MethodInterceptor

 
  1. class MyMethodInterceptor implements MethodInterceptor {
  2. @Override
  3. public Object invoke(MethodInvocation invocation) throws Throwable {
  4. System.out.println("我是MethodInterceptor start");
  5. //调用invocation.proceed()执行下一个拦截器
  6. Object result = invocation.proceed();
  7. System.out.println("我是MethodInterceptor end");
  8. //返回结果
  9. return result;
  10. }
  11. }
org.springframework.aop.MethodBeforeAdvice:方法前置通知

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

下面我们自定义一个MethodBeforeAdvice

 
  1. class MyMethodBeforeAdvice implements MethodBeforeAdvice {
  2. @Override
  3. public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
  4. System.out.println("我是MethodBeforeAdvice");
  5. }
  6. }

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

 
  1. public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
  2. private final MethodBeforeAdvice advice;
  3. public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
  4. this.advice = advice;
  5. }
  6. @Override
  7. public Object invoke(MethodInvocation mi) throws Throwable {
  8. //调用MethodBeforeAdvice的before方法,执行前置通知
  9. this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
  10. //执行下一个拦截器
  11. return mi.proceed();
  12. }
  13. }
org.springframework.aop.AfterReturningAdvice:方法返回通知

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

下面我们自定义一个AfterReturningAdvice

 
  1. class MyAfterReturningAdvice implements AfterReturningAdvice {
  2. @Override
  3. public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
  4. System.out.println("我是AfterReturningAdvice");
  5. }
  6. }

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

 
  1. public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
  2. private final AfterReturningAdvice advice;
  3. public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
  4. this.advice = advice;
  5. }
  6. @Override
  7. public Object invoke(MethodInvocation mi) throws Throwable {
  8. //执行下一个拦截器,可以获取目标方法的返回结果
  9. Object retVal = mi.proceed();
  10. //调用方法返回通知的afterReturning方法,会传入目标方法的返回值等信息
  11. this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
  12. return retVal;
  13. }
  14. }
org.springframework.aop.ThrowsAdvice:异常通知

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

下面我们自定义一个ThrowsAdvice

 
  1. /**
  2. * 用来定义异常通知
  3. * 方法名必须是afterThrowing,格式参考下面2种定义
  4. * 1. public void afterThrowing(Exception ex)
  5. * 2. public void afterThrowing(Method method, Object[] args, Object target, Exception ex)
  6. */
  7. class MyThrowsAdvice implements ThrowsAdvice {
  8. public void afterThrowing(Method method, Object[] args, Object target, Exception ex) {
  9. System.out.println("我是ThrowsAdvice");
  10. }
  11. }

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

 
  1. public class ThrowsAdviceInterceptor implements MethodInterceptor, AfterAdvice {
  2. private final Object throwsAdvice;
  3. public ThrowsAdviceInterceptor(Object throwsAdvice) {
  4. this.throwsAdvice = throwsAdvice;
  5. }
  6. @Override
  7. public Object invoke(MethodInvocation mi) throws Throwable {
  8. try {
  9. return mi.proceed();
  10. } catch (Throwable ex) {
  11. //调用 ThrowsAdvice 中的 afterThrowing 方法来处理异常
  12. this.throwsAdvice.afterThrowing(。。。。);
  13. //将异常继续往外抛
  14. throw ex;
  15. }
  16. }
  17. }

拦截器链执行过程

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

4种通知的执行顺序

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

 
  1. class MyMethodInterceptor implements MethodInterceptor {
  2. @Override
  3. public Object invoke(MethodInvocation invocation) throws Throwable {
  4. System.out.println("我是MethodInterceptor start");
  5. //调用invocation.proceed()执行下一个拦截器
  6. Object result = invocation.proceed();
  7. System.out.println("我是MethodInterceptor end");
  8. //返回结果
  9. return result;
  10. }
  11. }
  12. class MyMethodBeforeAdvice implements MethodBeforeAdvice {
  13. @Override
  14. public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
  15. System.out.println("我是MethodBeforeAdvice");
  16. }
  17. }
  18. class MyAfterReturningAdvice implements AfterReturningAdvice {
  19. @Override
  20. public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
  21. System.out.println("我是AfterReturningAdvice");
  22. }
  23. }
  24. class MyThrowsAdvice implements ThrowsAdvice {
  25. public void afterThrowing(Method method, Object[] args, Object target, Exception ex) {
  26. System.out.println("我是ThrowsAdvice");
  27. }
  28. }

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

 
  1. class MyMethodInterceptor implements MethodInterceptor {
  2. @Override
  3. public Object invoke(MethodInvocation mi) throws Throwable {
  4. System.out.println("我是MethodInterceptor start");
  5. //调用mi.proceed()执行下一个拦截器
  6. Object retVal = mi.proceed();
  7. System.out.println("我是MethodInterceptor end");
  8. //返回结果
  9. return retVal;
  10. }
  11. }
  12. class MyMethodBeforeAdvice implements MethodInterceptor {
  13. @Override
  14. public Object invoke(MethodInvocation mi) throws Throwable {
  15. System.out.println("我是MethodBeforeAdvice");
  16. //调用mi.proceed()执行下一个拦截器
  17. Object retVal = mi.proceed();
  18. return retVal;
  19. }
  20. }
  21. class MyAfterReturningAdvice implements MethodInterceptor {
  22. @Override
  23. public Object invoke(MethodInvocation mi) throws Throwable {
  24. //调用mi.proceed()执行下一个拦截器
  25. Object retVal = mi.proceed();
  26. System.out.println("我是AfterReturningAdvice");
  27. return retVal;
  28. }
  29. }
  30. class MyThrowsAdvice implements MethodInterceptor {
  31. @Override
  32. public Object invoke(MethodInvocation mi) throws Throwable {
  33. try {
  34. //调用mi.proceed()执行下一个拦截器
  35. return mi.proceed();
  36. } catch (Throwable ex) {
  37. System.out.println("我是ThrowsAdvice");
  38. throw ex;
  39. }
  40. }
  41. }

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

 
  1. System.out.println("我是MethodInterceptor start");
  2. System.out.println("我是MethodBeforeAdvice");
  3. Object retVal = null;
  4. try {
  5. retVal = 通过反射调用目标方法获取返回值;
  6. } catch (Throwable ex) {
  7. System.out.println("我是ThrowsAdvice");
  8. throw ex;
  9. }
  10. System.out.println("我是AfterReturningAdvice");
  11. System.out.println("我是MethodInterceptor end");
  12. return retVal;

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

 
  1. public static class Service3 {
  2. public String say(String name) {
  3. return "你好:" + name;
  4. }
  5. }

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

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

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

 
  1. System.out.println("我是MethodInterceptor start");
  2. System.out.println("我是MethodBeforeAdvice");
  3. Object retVal = null;
  4. try {
  5. retVal = target.say("路人");
  6. } catch (Throwable ex) {
  7. System.out.println("我是ThrowsAdvice");
  8. throw ex;
  9. }
  10. System.out.println("我是AfterReturningAdvice");
  11. System.out.println("我是MethodInterceptor end");
  12. System.out.println(retVal);

再次简化

 
  1. System.out.println("我是MethodInterceptor start");
  2. System.out.println("我是MethodBeforeAdvice");
  3. Object retVal = null;
  4. try {
  5. retVal = "你好:" + name;
  6. } catch (Throwable ex) {
  7. System.out.println("我是ThrowsAdvice");
  8. throw ex;
  9. }
  10. System.out.println("我是AfterReturningAdvice");
  11. System.out.println("我是MethodInterceptor end");
  12. System.out.println(retVal);

最终会输出

 
  1. 我是MethodInterceptor start
  2. 我是MethodBeforeAdvice
  3. 我是AfterReturningAdvice
  4. 我是MethodInterceptor end
  5. 你好:路人

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

 
  1. package com.javacode2018.aop.demo11.test3;
  2. import org.aopalliance.intercept.MethodInterceptor;
  3. import org.aopalliance.intercept.MethodInvocation;
  4. import org.springframework.aop.AfterReturningAdvice;
  5. import org.springframework.aop.MethodBeforeAdvice;
  6. import org.springframework.aop.ThrowsAdvice;
  7. import org.springframework.lang.Nullable;
  8. import java.lang.reflect.Method;
  9. public class MoreAdvice {
  10. public static class Service3 {
  11. public String say(String name) {
  12. return "你好:" + name;
  13. }
  14. }
  15. public static class MyMethodInterceptor implements MethodInterceptor {
  16. @Override
  17. public Object invoke(MethodInvocation invocation) throws Throwable {
  18. System.out.println("我是MethodInterceptor start");
  19. //调用invocation.proceed()执行下一个拦截器
  20. Object result = invocation.proceed();
  21. System.out.println("我是MethodInterceptor end");
  22. //返回结果
  23. return result;
  24. }
  25. }
  26. public static class MyMethodBeforeAdvice implements MethodBeforeAdvice {
  27. @Override
  28. public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
  29. System.out.println("我是MethodBeforeAdvice");
  30. }
  31. }
  32. public static class MyAfterReturningAdvice implements AfterReturningAdvice {
  33. @Override
  34. public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
  35. System.out.println("我是AfterReturningAdvice");
  36. }
  37. }
  38. public static class MyThrowsAdvice implements ThrowsAdvice {
  39. public void afterThrowing(Method method, Object[] args, Object target, Exception ex) {
  40. System.out.println("我是ThrowsAdvice");
  41. }
  42. }
  43. }

对应测试代码

 
  1. @Test
  2. public void test3() {
  3. //创建目标对象
  4. MoreAdvice.Service3 target = new MoreAdvice.Service3();
  5. //创建代理工厂,通过代理工厂来创建代理对象
  6. ProxyFactory proxyFactory = new ProxyFactory();
  7. proxyFactory.setTarget(target);
  8. //依次为目标对象添加4种通知
  9. proxyFactory.addAdvice(new MoreAdvice.MyMethodInterceptor());
  10. proxyFactory.addAdvice(new MoreAdvice.MyMethodBeforeAdvice());
  11. proxyFactory.addAdvice(new MoreAdvice.MyAfterReturningAdvice());
  12. proxyFactory.addAdvice(new MoreAdvice.MyThrowsAdvice());
  13. //获取到代理对象
  14. MoreAdvice.Service3 proxy = (MoreAdvice.Service3) proxyFactory.getProxy();
  15. //通过代理对象访问目标方法say
  16. System.out.println(proxy.say("路人"));
  17. }

运行输出

 
  1. 我是MethodInterceptor start
  2. 我是MethodBeforeAdvice
  3. 我是AfterReturningAdvice
  4. 我是MethodInterceptor end
  5. 你好:路人

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

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

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

 
  1. @Before
  2. @Around
  3. @After
  4. @AfterReturning
  5. @AfterThrowing

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

 
  1. @AfterThrowing
  2. @AfterReturning
  3. @After
  4. @Around
  5. @Before

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

 
  1. package com.javacode2018.aop.demo11.test4;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.*;
  4. import org.springframework.stereotype.Component;
  5. @Component
  6. @Aspect
  7. public class Aspect4 {
  8. @Pointcut("execution(* com.javacode2018.aop.demo11.test4.Service4.*(..))")
  9. public void pc() {
  10. }
  11. @Before("pc()")
  12. public void before() {
  13. System.out.println("@Before通知!");
  14. }
  15. @Around("pc()")
  16. public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
  17. System.out.println("@Around通知start");
  18. Object result = joinPoint.proceed();
  19. System.out.println("@Around绕通知end");
  20. return result;
  21. }
  22. @After("pc()")
  23. public void after() throws Throwable {
  24. System.out.println("@After通知!");
  25. }
  26. @AfterReturning("pc()")
  27. public void afterReturning() throws Throwable {
  28. System.out.println("@AfterReturning通知!");
  29. }
  30. @AfterThrowing("pc()")
  31. public void afterThrowing() {
  32. System.out.println("@AfterThrowing通知!");
  33. }
  34. }

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

 
  1. package com.javacode2018.aop.demo11.test4;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service4 {
  5. public String say(String name) {
  6. return "你好:" + name;
  7. }
  8. }

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

 
  1. package com.javacode2018.aop.demo11.test4;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.EnableAspectJAutoProxy;
  4. @EnableAspectJAutoProxy
  5. @ComponentScan
  6. public class MainConfig4 {
  7. }

测试代码

 
  1. @Test
  2. public void test4(){
  3. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  4. context.register(MainConfig4.class);
  5. context.refresh();
  6. Service4 service4 = context.getBean(Service4.class);
  7. System.out.println(service4.say("路人"));
  8. }

运行依次输出如下

 
  1. @Around通知start
  2. @Before通知!
  3. @Around绕通知end
  4. @After通知!
  5. @AfterReturning通知!
  6. 你好:路人

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

 
  1. @AfterThrowing
  2. @AfterReturning
  3. @After
  4. @Around
  5. @Before

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

5、@Aspect中5种通知回顾

5种通知对应的Advice类

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

通知对应的Advice类
@AfterThrowingorg.springframework.aop.aspectj.AspectJAfterThrowingAdvice
@AfterReturningorg.springframework.aop.aspectj.AspectJAfterReturningAdvice
@Afterorg.springframework.aop.aspectj.AspectJAfterAdvice
@Aroundorg.springframework.aop.aspectj.AspectJAroundAdvice
@Beforeorg.springframework.aop.aspectj.AspectJMethodBeforeAdvice

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

@AfterThrowing:AspectJAfterThrowingAdvice

 
  1. public class AspectJAfterThrowingAdvice implements MethodInterceptor {
  2. @Override
  3. public Object invoke(MethodInvocation mi) throws Throwable {
  4. try {
  5. //执行下一个拦截器
  6. return mi.proceed();
  7. } catch (Throwable ex) {
  8. //通过反射调用@AfterThrowing标注的方法
  9. //继续抛出异常
  10. throw ex;
  11. }
  12. }
  13. }

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

@AfterReturning:AspectJAfterReturningAdvice

AspectJAfterReturningAdvice 源码:

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

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

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

@After:AspectJAfterAdvice

AspectJAfterAdvice 源码:

 
  1. public class AspectJAfterAdvice implements MethodInterceptor {
  2. @Override
  3. public Object invoke(MethodInvocation mi) throws Throwable {
  4. try {
  5. //执行下一个拦截器
  6. return mi.proceed();
  7. } finally {
  8. //调用@After标注的方法
  9. }
  10. }
  11. }

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

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

@Around:AspectJAroundAdvice

AspectJAroundAdvice 源码:

 
  1. public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {
  2. @Override
  3. public Object invoke(MethodInvocation mi) throws Throwable {
  4. return 调用 @Around标注的方法 ;
  5. }
  6. }

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

@Before:AspectJMethodBeforeAdvice

AspectJMethodBeforeAdvice 源码:

 
  1. public class AspectJMethodBeforeAdvice implements MethodBeforeAdvice, Serializable {
  2. @Override
  3. public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
  4. invokeAdviceMethod(getJoinPointMatch(), null, null);
  5. }
  6. }

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

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

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

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

 
  1. public class Aspect4 {
  2. @Pointcut("execution(* com.javacode2018.aop.demo11.test4.Service4.*(..))")
  3. public void pc() {
  4. }
  5. @Before("pc()")
  6. public void before() {
  7. System.out.println("@Before通知!");
  8. }
  9. @Around("pc()")
  10. public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
  11. System.out.println("@Around通知start");
  12. Object result = joinPoint.proceed();
  13. System.out.println("@Around绕通知end");
  14. return result;
  15. }
  16. @After("pc()")
  17. public void after() throws Throwable {
  18. System.out.println("@After通知!");
  19. }
  20. @AfterReturning("pc()")
  21. public void afterReturning() throws Throwable {
  22. System.out.println("@AfterReturning通知!");
  23. }
  24. @AfterThrowing("pc()")
  25. public void afterThrowing() {
  26. System.out.println("@AfterThrowing通知!");
  27. }
  28. }

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

 
  1. @AfterThrowing
  2. @AfterReturning
  3. @After
  4. @Around
  5. @Before

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

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

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

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

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

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

 
  1. try {
  2. Object result = null;
  3. try {
  4. //执行下一个拦截器
  5. result = mi.proceed();
  6. } finally {
  7. System.out.println("@After通知!");
  8. }
  9. System.out.println("@AfterReturning通知!");
  10. return retVal;
  11. } catch (Throwable ex) {
  12. System.out.println("@AfterThrowing通知!");
  13. //继续抛出异常
  14. throw ex;
  15. }

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

 
  1. try {
  2. Object result = null;
  3. try {
  4. System.out.println("@Around通知start");
  5. result = joinPoint.proceed();
  6. System.out.println("@Around绕通知end");
  7. return result;
  8. } finally {
  9. System.out.println("@After通知!");
  10. }
  11. System.out.println("@AfterReturning通知!");
  12. return retVal;
  13. } catch (Throwable ex) {
  14. System.out.println("@AfterThrowing通知!");
  15. //继续抛出异常
  16. throw ex;
  17. }

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

 
  1. try {
  2. Object result = null;
  3. try {
  4. System.out.println("@Around通知start");
  5. System.out.println("@Before通知!");
  6. result = mi.proceed();
  7. System.out.println("@Around绕通知end");
  8. return result;
  9. } finally {
  10. System.out.println("@After通知!");
  11. }
  12. System.out.println("@AfterReturning通知!");
  13. return retVal;
  14. } catch (Throwable ex) {
  15. System.out.println("@AfterThrowing通知!");
  16. //继续抛出异常
  17. throw ex;
  18. }

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

 
  1. try {
  2. Object result = null;
  3. try {
  4. System.out.println("@Around通知start");
  5. System.out.println("@Before通知!");
  6. result = // 通过反射调用目标方法; //@1
  7. System.out.println("@Around绕通知end");
  8. return result;
  9. } finally {
  10. System.out.println("@After通知!");
  11. }
  12. System.out.println("@AfterReturning通知!");
  13. return retVal;
  14. } catch (Throwable ex) {
  15. System.out.println("@AfterThrowing通知!");
  16. //继续抛出异常
  17. throw ex;
  18. }

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

 
  1. try {
  2. Object result = null;
  3. try {
  4. System.out.println("@Around通知start");
  5. System.out.println("@Before通知!");
  6. result = service4.say("路人");
  7. System.out.println("@Around绕通知end");
  8. return result;
  9. } finally {
  10. System.out.println("@After通知!");
  11. }
  12. System.out.println("@AfterReturning通知!");
  13. return retVal;
  14. } catch (Throwable ex) {
  15. System.out.println("@AfterThrowing通知!");
  16. //继续抛出异常
  17. throw ex;
  18. }

所以最终输出

 
  1. @Around通知start
  2. @Before通知!
  3. @Around绕通知end
  4. @After通知!
  5. @AfterReturning通知!
  6. 你好:路人

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

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

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

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

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

 
  1. @Aspect
  2. @Order(1)
  3. 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方法比较关键,来看一下

 
  1. public abstract class AbstractPointcutAdvisor implements PointcutAdvisor, Ordered, Serializable {
  2. @Nullable
  3. private Integer order;
  4. public void setOrder(int order) {
  5. this.order = order;
  6. }
  7. @Override
  8. public int getOrder() {
  9. //若当前Advisor指定了order,则直接返回
  10. if (this.order != null) {
  11. return this.order;
  12. }
  13. //获取当前类中配置的通知对象Advice
  14. Advice advice = getAdvice();
  15. //若advice实现了Ordered接口,这从advice中获取通知的顺序
  16. if (advice instanceof Ordered) {
  17. return ((Ordered) advice).getOrder();
  18. }
  19. //否则通知的优先级最低,Integer.MAX_VALUE
  20. return Ordered.LOWEST_PRECEDENCE;
  21. }
  22. }

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

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

排序规则

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

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

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

 
  1. @AfterThrowing
  2. @AfterReturning
  3. @After
  4. @Around
  5. @Before

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

案例

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

先定义目标类
 
  1. package com.javacode2018.aop.demo11.test2;
  2. import org.springframework.stereotype.Component;
  3. @Component
  4. public class Service2 {
  5. public String say(String name) {
  6. return "你好:" + name;
  7. }
  8. }
Aspect1:第2个@Aspect
 
  1. package com.javacode2018.aop.demo11.test2;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.*;
  4. import org.springframework.core.annotation.Order;
  5. import org.springframework.stereotype.Component;
  6. @Aspect
  7. @Order(1)
  8. @Component
  9. public class MyAspect1 {
  10. @Pointcut("execution(* com.javacode2018.aop.demo11.test2.Service2.*(..))")
  11. public void pc() {
  12. }
  13. @Before("pc()")
  14. public void before() {
  15. System.out.println("MyAspect1 @Before通知!");
  16. }
  17. @Around("pc()")
  18. public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
  19. System.out.println("MyAspect1 @Around通知start");
  20. Object result = joinPoint.proceed();
  21. System.out.println("MyAspect1 @Around绕通知end");
  22. return result;
  23. }
  24. @After("pc()")
  25. public void after() throws Throwable {
  26. System.out.println("MyAspect1 @After通知!");
  27. }
  28. @AfterReturning("pc()")
  29. public void afterReturning() throws Throwable {
  30. System.out.println("MyAspect1 @AfterReturning通知!");
  31. }
  32. @AfterThrowing("pc()")
  33. public void afterThrowing() {
  34. System.out.println("MyAspect1 @AfterThrowing通知!");
  35. }
  36. }
Aspect1:第2个@Aspect
 
  1. package com.javacode2018.aop.demo11.test2;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.*;
  4. import org.springframework.core.annotation.Order;
  5. import org.springframework.stereotype.Component;
  6. @Aspect
  7. @Order(3)
  8. @Component
  9. public class MyAspect2 {
  10. @Pointcut("execution(* com.javacode2018.aop.demo11.test2.Service2.*(..))")
  11. public void pc() {
  12. }
  13. @Before("pc()")
  14. public void before() {
  15. System.out.println("MyAspect2 @Before通知!");
  16. }
  17. @Around("pc()")
  18. public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
  19. System.out.println("MyAspect2 @Around通知start");
  20. Object result = joinPoint.proceed();
  21. System.out.println("MyAspect2 @Around绕通知end");
  22. return result;
  23. }
  24. @After("pc()")
  25. public void after() throws Throwable {
  26. System.out.println("MyAspect2 @After通知!");
  27. }
  28. @AfterReturning("pc()")
  29. public void afterReturning() throws Throwable {
  30. System.out.println("MyAspect2 @AfterReturning通知!");
  31. }
  32. @AfterThrowing("pc()")
  33. public void afterThrowing() {
  34. System.out.println("MyAspect2 @AfterThrowing通知!");
  35. }
  36. }
自定义一个Advisor
 
  1. package com.javacode2018.aop.demo11.test2;
  2. import org.aopalliance.intercept.MethodInterceptor;
  3. import org.aopalliance.intercept.MethodInvocation;
  4. import org.springframework.aop.support.DefaultPointcutAdvisor;
  5. import org.springframework.stereotype.Component;
  6. @Component
  7. public class Advisor1 extends DefaultPointcutAdvisor {
  8. public Advisor1() {
  9. MethodInterceptor methodInterceptor = new MethodInterceptor() {
  10. @Override
  11. public Object invoke(MethodInvocation invocation) throws Throwable {
  12. System.out.println("Advisor1 start");
  13. Object result = invocation.proceed();
  14. System.out.println("Advisor1 end");
  15. return result;
  16. }
  17. };
  18. this.setAdvice(methodInterceptor);
  19. }
  20. @Override
  21. public int getOrder() {
  22. return 2;
  23. }
  24. }
来个spring配置类

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

 
  1. package com.javacode2018.aop.demo11.test2;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.EnableAspectJAutoProxy;
  4. @ComponentScan
  5. @EnableAspectJAutoProxy
  6. public class MainConfig2 {
  7. }
测试代码
 
  1. @Test
  2. public void test2() {
  3. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
  4. context.register(MainConfig2.class);
  5. context.refresh();
  6. Service2 service2 = context.getBean(Service2.class);
  7. System.out.println(service2.say("路人"));
  8. }
运行输出
 
  1. MyAspect1 @Around通知start
  2. MyAspect1 @Before通知!
  3. Advisor1 start
  4. MyAspect2 @Around通知start
  5. MyAspect2 @Before通知!
  6. MyAspect2 @Around绕通知end
  7. MyAspect2 @After通知!
  8. MyAspect2 @AfterReturning通知!
  9. Advisor1 end
  10. MyAspect1 @Around绕通知end
  11. MyAspect1 @After通知!
  12. MyAspect1 @AfterReturning通知!
  13. 你好:路人
结果分析

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

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

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

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

 
  1. Aspect1:顺序是1
  2. @AfterThrowing
  3. @AfterReturning
  4. @After
  5. @Around
  6. @Before
  7. Advisor1:顺序是2
  8. MyAspect2:顺序是3
  9. @AfterThrowing
  10. @AfterReturning
  11. @After
  12. @Around
  13. @Before

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

先对Aspect1进行填充,得到:

 
  1. try {
  2. Object result = null;
  3. try {
  4. System.out.println("MyAspect1 @Around通知start");
  5. System.out.println("MyAspect1 @Before通知!");
  6. result = mi.proceed(); //@1
  7. System.out.println("MyAspect1 @Around绕通知end");
  8. return result;
  9. } finally {
  10. System.out.println("MyAspect1 @After通知!");
  11. }
  12. System.out.println("MyAspect1 @AfterReturning通知!");
  13. return retVal;
  14. } catch (Throwable ex) {
  15. System.out.println("MyAspect1 @AfterThrowing通知!");
  16. //继续抛出异常
  17. throw ex;
  18. }

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

 
  1. try {
  2. Object result = null;
  3. try {
  4. System.out.println("MyAspect1 @Around通知start");
  5. System.out.println("MyAspect1 @Before通知!");
  6. System.out.println("Advisor1 start");
  7. result = invocation.proceed(); //@2
  8. System.out.println("Advisor1 end");
  9. System.out.println("MyAspect1 @Around绕通知end");
  10. return result;
  11. } finally {
  12. System.out.println("MyAspect1 @After通知!");
  13. }
  14. System.out.println("MyAspect1 @AfterReturning通知!");
  15. return retVal;
  16. } catch (Throwable ex) {
  17. System.out.println("MyAspect1 @AfterThrowing通知!");
  18. //继续抛出异常
  19. throw ex;
  20. }

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

 
  1. try {
  2. Object result = null;
  3. try {
  4. System.out.println("MyAspect1 @Around通知start");
  5. System.out.println("MyAspect1 @Before通知!");
  6. System.out.println("Advisor1 start");
  7. try {
  8. try {
  9. System.out.println("MyAspect2 @Around通知start");
  10. System.out.println("MyAspect2 @Before通知!");
  11. result = mi.proceed(); //@3
  12. System.out.println("MyAspect2 @Around绕通知end");
  13. return result;
  14. } finally {
  15. System.out.println("MyAspect2 @After通知!");
  16. }
  17. System.out.println("MyAspect2 @AfterReturning通知!");
  18. } catch (Throwable ex) {
  19. System.out.println("MyAspect2 @AfterThrowing通知!");
  20. //继续抛出异常
  21. throw ex;
  22. }
  23. System.out.println("Advisor1 end");
  24. System.out.println("MyAspect1 @Around绕通知end");
  25. return result;
  26. } finally {
  27. System.out.println("MyAspect1 @After通知!");
  28. }
  29. System.out.println("MyAspect1 @AfterReturning通知!");
  30. return retVal;
  31. } catch (Throwable ex) {
  32. System.out.println("MyAspect1 @AfterThrowing通知!");
  33. //继续抛出异常
  34. throw ex;
  35. }

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

 
  1. try {
  2. Object result = null;
  3. try {
  4. System.out.println("MyAspect1 @Around通知start");
  5. System.out.println("MyAspect1 @Before通知!");
  6. System.out.println("Advisor1 start");
  7. try {
  8. try {
  9. System.out.println("MyAspect2 @Around通知start");
  10. System.out.println("MyAspect2 @Before通知!");
  11. result = "你好:路人";
  12. System.out.println("MyAspect2 @Around绕通知end");
  13. return result;
  14. } finally {
  15. System.out.println("MyAspect2 @After通知!");
  16. }
  17. System.out.println("MyAspect2 @AfterReturning通知!");
  18. } catch (Throwable ex) {
  19. System.out.println("MyAspect2 @AfterThrowing通知!");
  20. //继续抛出异常
  21. throw ex;
  22. }
  23. System.out.println("Advisor1 end");
  24. System.out.println("MyAspect1 @Around绕通知end");
  25. return result;
  26. } finally {
  27. System.out.println("MyAspect1 @After通知!");
  28. }
  29. System.out.println("MyAspect1 @AfterReturning通知!");
  30. return retVal;
  31. } catch (Throwable ex) {
  32. System.out.println("MyAspect1 @AfterThrowing通知!");
  33. //继续抛出异常
  34. throw ex;
  35. }

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

 
  1. MyAspect1 @Around通知start
  2. MyAspect1 @Before通知!
  3. Advisor1 start
  4. MyAspect2 @Around通知start
  5. MyAspect2 @Before通知!
  6. MyAspect2 @Around绕通知end
  7. MyAspect2 @After通知!
  8. MyAspect2 @AfterReturning通知!
  9. Advisor1 end
  10. MyAspect1 @Around绕通知end
  11. MyAspect1 @After通知!
  12. MyAspect1 @AfterReturning通知!
  13. 你好:路人

9、@EnableAspectJAutoProxy另外2个功能

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

 
  1. public @interface EnableAspectJAutoProxy {
  2. /**
  3. * 是否基于类来创建代理,而不是基于接口来创建代理
  4. * 当为true的时候会使用cglib来直接对目标类创建代理对象
  5. * 默认为 false:即目标bean如果有接口的会采用jdk动态代理来创建代理对象,没有接口的目标bean,会采用cglib来创建代理对象
  6. */
  7. boolean proxyTargetClass() default false;
  8. /**
  9. * 是否需要将代理对象暴露在ThreadLocal中,当为true的时候
  10. * 可以通过org.springframework.aop.framework.AopContext#currentProxy获取当前代理对象
  11. */
  12. boolean exposeProxy() default false;
  13. }

10、@EnableAspectJAutoProxy原理

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

 
  1. org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator

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

11、总结

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

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

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

12、案例源码

 
  1. https://gitee.com/javacode2018/spring-series
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值