一.调试环境搭建
- pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
- Calculator类
@Component
public class Calculator {
public int divide(int a,int b) {
return a/b;
}
}
- AppConfig类
@Configuration
@ComponentScan("com.xiaqi")
@EnableAspectJAutoProxy
public class AppConfig {
}
- LogAspect类
@Aspect
@Component
public class LogAspect {
@Pointcut("execution(* com.xiaqi.service.Calculator.*(..))")
public void pointCut() {}
@Before("pointCut()")
public void before() {
System.out.println("我是前置通知!");
}
@After("pointCut()")
public void after() {
System.out.println("我是finally通知!");
}
@AfterReturning("pointCut()")
public void afterReturning() {
System.out.println("我是成功执行完之后通知!");
}
@AfterThrowing("pointCut()")
public void afterThrowing() {
System.out.println("我是异常之后的通知!");
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("我是环绕前置通知");
Object result = joinPoint.proceed();
System.out.println("我是环绕后置通知");
return result;
}
}
- AopEntry类(测试类)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=AppConfig.class)
public class AopEntry {
@Autowired
private Calculator calculator;
@Test
public void test() {
System.out.println(calculator.divide(4, 2));
}
}
二.aop源码探析
aop核心是aop的自动代理创建器,所有有关的源码主要是与这个组件有关,所以aop需要弄清楚下面四个问题。
- Aop自动代理创建器的注册
- Aop自动代理创建器的创建
- Aop自动代理创建器的作用时机
- 产生的代理对象是如何发生作用的
1.Aop自动代理创建器的注册
- 进入@EnableAspectJAutoProxy注解,发现这个注解通过@Import导入了一个类—>AspectJAutoProxyRegistrar,这个类实现了ImportBeanDefinitionRegistrar接口
- 这个类的第45行代码向容器中注册了aop的自动代理创建器,跟进这个方法,发现这个方法调用了它下面的那个方法,向容器中注册了AnnotationAwareAspectJAutoProxyCreator这个类,这个类就是Aop的自动代理创建器,跟进这个注册方法
- 在这个方法的第138,142行,将这个创建器注册到容器中,自此Aop的自动代理创建器完成了注册
2.Aop自动代理创建器的创建
-
跟踪Aop自动代理创建器的创建之前,先看下它的继承体系,发现它实现BeanPostProcessor,BeanFactory和Ordered接口,可以看成这个创建器本质上是一个bean后置处理器
-
进入到容器的refresh方法,这是IOC容器的启动的核心方法,详细可参考上一篇文章,初探Spring-IOC源码之普通单例bean的生命周期
-
在refresh方法的第535行,调用registerBeanPostProcessors方法,跟进这个方法
-
在跟进第710行代码,方法spring将BeanPostProcessor分成了三类,即实现了PriorityOrdered接口的,实现了Ordered接口的以及其他剩余的bean后置处理器,由于aop的自动代理创建器实例Ordered接口,所以这个类第二轮注册中被注册。
在第226行调用了BeanFactory的getBean方法,最终创建这个AnnotationAwareAspectJAutoProxyCreator这个组件(详细过程可参考上一篇文章,初探Spring-IOC源码之普通单例bean的生命周期),最后被添加到BeanFactory中
3.Aop自动代理创建器的作用时机
- 上面已经讲述到了aop的自动代理创建器本质上为BeanPostProcessor,所以它产生代理对象的核心方法应该为BeanPostProcessor两个后置处理方法中一个,先看postProcessBeforeInitialization方法(注意,AnnotationAwareAspectJAutoProxyCreator类中没有重写这两个方法,需要进入其父类AbstractAutoProxyCreator中)
- 发现postProcessBeforeInitialization方法并没有做任何处理,直接返回了bean,再查看postProcessAfterInitialization方法
跟进这个wrapIfNecessary方法 - 在第355行,调用了createProxy方法,跟进
- 在第473行,调用getProxy方法,跟进
- 在第110行,跟进createAopProxy方法
- 在第105行,调用createAopProxy方法,跟进
- 在这个方法中,根据一些条件判断,产生了基于jdk的动态代理或者cglib的代理,至此aop的自动代理创建器完成了代理对象的创建
4.通过代理对象调用目标方法
- 在调用目标方法出打一个断点,按下F8将代码执行到这一行,再按F5跟进
- 发现进入到代理对象的拦截方法,在第674行获取了拦截器链,可以查看下这个拦截器链,之前的五个通知方法被分别对应着五个对象
- 在第688行,开始调用拦截器链,跟进
- 发现currentInterceptorIndex从-1开始,先自增,然后从拦截链中去对应索引的拦截器,即第166行,最后在第185行进行调用,跟进第185行
- 发现在每个拦截器中都调用了mi.proceed()方法,回到了上一层中,继续获取下一个拦截器进行同样的调用,这里挑出几个通知方法来看看内部调用
- afterThrowing通知(发现其实在方法外层加上了try-catch):
- afterReturning通知:
- after通知(其实就是在方法外层增加了try-finally):
- afterThrowing通知(发现其实在方法外层加上了try-catch):
- 当最终所有的拦截器被调用后,即currentInterceptorIndex == 拦截器数-1时,调用目标方法。这时可以查看debug栈,发现将所有的拦截器的调用方法都压栈了。
- 最终,通过代理对象调用目标方法的执行流程可总结为下图:
执行顺序:
- 正常执行(不发生异常,around通知调用ProceedingJoinPoint.proceed()方法放行):
环绕前置通知---->前置通知---->目标方法---->环绕后置通知---->后置通知(after)---->返回之后的通知(afterReturning) - 发生异常:
环绕前置通知---->前置通知---->目标方法---->后置通知(after)---->异常通知(afterThrowing) - around通知不调用ProceedingJoinPoint.proceed()方法:
环绕前置通知---->环绕后置通知---->后置通知(after)---->返回之后的通知(afterReturning)