type
status
date
slug
summary
tags
category
icon
password

一、AnnotationAwareAspectJAutoProxyCreator 简介

AnnotationAwareAspectJAutoProxyCreator 是 Spring AOP 的核心类,该类及其父类完成 Spring AOP 整个流程的执行。在介绍 AOP 原理之前先介绍下 AnnotationAwareAspectJAutoProxyCreator 的类结构。
notion image
AnnotationAwareAspectJAutoProxyCreator 实现了几个重要的扩展接口:
  • 实现了 BeanPostProcessor 接口:实现了 postProcessAfterInitialization() 方法。该方法在目标对象初始化之后被调用。
  • 实现了 InstantiationAwareBeanPostProcessor 接口:实现了 postProcessBeforeInstantiation() 方法。该方法在目标对象实例化之前调用。
  • 实现了 SmartInstantiationAwareBeanPostProcessor 接口:实现了 predictBeanType() 方法;getEarlyBeanReference()方法。
  • 实现了 BeanFactoryAware 接口,实现了 setBeanFactory()方法。
💡
注意:AnnotationAwareAspectJAutoProxyCreator 实现 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 来执行前置处理,该方法在 Bean 实例化之前被调用;实现 BeanPostProcessor#postProcessAfterInitialization 来执行后置处理,该方法在 Bean 初始化之后被调用。而不是单纯的只实现了 BeanPostProcessor 接口来进行前后置处理。
所以,我们需要关注 AnnotationAwareAspectJAutoProxyCreator 各个间接父类中方法的实现过程:
AnnotationAwareAspectJAutoProxyCreator
==> AspectJAwareAdvisorAutoProxyCreator
==> AbstractAdvisorAutoProxyCreator
==> AbstractAutoProxyCreator

二、AOP 源码解析

AOP 整体流程

notion image
Spring AOP 源码涉及的几个重要类如上图所示。为了方便大家能更好看懂后面的源码,我先整体介绍一下源码的执行流程,让大家有一个整体的认识,否则容易被绕进去。假如存在切面如下:
整个 Spring AOP 源码,其实分为 3 块
notion image
第一块就是前置处理,我们在创建 Louzai Bean 的前置处理中,会遍历程序所有的切面信息,然后将切面信息保存在缓存中,比如示例中 LouzaiAspect 的所有切面信息。
第二块就是后置处理,我们在创建 Louzai Bean 的后置处理器中,里面会做两件事情:
  • 获取 Louzai 的切面方法:首先会从缓存中拿到所有的切面信息,和 Louzai 的所有方法进行匹配,然后找到 Louzai 所有需要进行 AOP 的方法。
  • 创建 AOP 代理对象:结合 Louzai 需要进行 AOP 的方法,选择 Cglib 或 JDK,创建 AOP 代理对象。
notion image
第三块就是执行切面,通过“责任链 + 递归”,去执行切面。
下面开始真正介绍代码流程。

1、@EnableAspectJAutoProxy

通过@EnableAspectJAutoProxy注解向容器中注册一个 AnnotationAwareAspectJAutoProxyCreator 的 BeanDefinition,它本身也是一个 BeanPostProcessor,这个 BeanDefinition 会在 AbstractApplicationContext#registerBeanPostProcessors这个方法中完成创建,如下图所示
根据 Bean 的生命周期(如果不了解可以看下这篇文章 Imspring 源码介绍(一):基本概念),在 Bean 实例化之前,也就是执行 doGetBean 方法之前会调用 InstantiationAwareBeanPostProcessor 接口的 postProcessBeforeInstantiation() ;在 Bean 初始化的时候,也就是 initializeBean 方法里面会执行 BeanPostProcessor 的后置处理 postProcessAfterInitialization
下面先介绍 postProcessBeforeInstantiation 方法的执行流程。

2、postProcessBeforeInstantiation()

下面正式进入 AnnotationAwareAspectJAutoProxyCreator 的前置处理流程 postProcessBeforeInstantiation() ,该方法在父类 AbstractAutoProxyCreator 里面实现。
AbstractAutoProxyCreator#postProcessBeforeInstantiation() 处理流程大体如下:
  1. 根据beanClass和beanName获取缓存Key,通常就是beanName,如果是FactoryBean的话,则是"&"+beanNmae;
  1. 判断当前正在创建的Bean是否已经被解析过,如果已解析,则直接返回null;
  1. 判断当前正在创建的Bean是否是基础的Bean、是否需要跳过等;
  1. 用户自定义TargetSource(目标源)的处理;
AbstractAutoProxyCreator#postProcessBeforeInstantiation() 中核心就是下面两个方法:
  • isInfrastructureClass(beanClass) 方法
此方法主要判断当前正在创建的Bean是否是基础的Bean,即当前bean是否是Advice、PointCut、Advisor、AopInfrastructureBean。
注意 AnnotationAwareAspectJAutoProxyCreator 类重写了isInfrastructureClass()方法,真正执行的是下面的代码。
可以看到,AnnotationAwareAspectJAutoProxyCreator的isInfrastructureClass()方法除了判断当前正在创建的Bean是否是基础的Bean之外,还判断了当前bean是否是切面。
  • shouldSkip(beanClass, beanName) 方法
判断是否需要跳过。
注意,AbstractAutoProxyCreator的子类----AspectJAwareAdvisorAutoProxyCreator重写了shouldSkip()方法,而AnnotationAwareAspectJAutoProxyCreator又继承自AspectJAwareAdvisorAutoProxyCreator类。
下面我们看下 AspectJAwareAdvisorAutoProxyCreator#shouldSkip() 方法的源码:
shouldSkip() 方法的处理流程大体如下:
  1. 获取所有候选的Advisor增强器(即切面里面的通知方法),每个封装的通知方法的增强器的类型为InstantiationModelAwarePointcutAdvisor;
  1. 循环所有的增强器(通知方法),判断是否是AspectJPointcutAdvisor类型的,如果是的话,则需要跳过。(实际上就是跳过切面类);
  1. 调用父类(AbstractAutoProxyCreator)的shouldSkip()方法;
下面我们看一下Spring是如何获取所有候选的Advisor增强器的(即切面里面的通知方法),具体在findCandidateAdvisors()方法,源码如下:
AnnotationAwareAspectJAutoProxyCreator 类重写了此方法:
AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors() 处理过程大体如下:
  1. 调用父类AbstractAdvisorAutoProxyCreator.findCandidateAdvisors()方法找到所有候选的增强器(通知方法);
  1. 为bean工厂中的所有的AspectJ切面构建Advisors增强器;
首先来看下如何找到所有候选的增强器(通知方法)的,具体源码在 BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans()
findAdvisorBeans() 方法的整体流程如下:
  1. 获取到cachedAdvisorBeanNames缓存;
  1. 如果cachedAdvisorBeanNames缓存为空,则从bean工厂中获取class类型为Advisor的所有bean名称;
  1. 将上一步获取的bean名称存入缓存中;循环
  1. 遍历前面确定好的增强器对应的beanName集合,确定切面与当前beanName是否符合;
  1. 如果当前bean正在创建中的话,什么都不处理;
  1. 如果当前bean不是正在创建,则根据beanName从工厂中获取对应的bean对象,并添加到advisors集合中;
  1. 返回advisors增强器集合;
当所有候选的增强器找到之后,就需要为bean工厂中的所有的AspectJ切面构建Advisor增强器,具体是在this.aspectJAdvisorsBuilder.buildAspectJAdvisors()完成的。
buildAspectJAdvisors() 方法的处理流程:
  1. 获取aspectBeanNames缓存,如果为空,则会触发切面的解析工作;
  1. 获取工厂中所有的beanNames(bean名称集合);
  1. 循环所有的beanNames(bean名称集合),找到增强器;
  1. 如果存在@Aspect注解的话,则进行解析;
  1. 获取标记了@AspectJ相关注解的增强方法;
  1. 如果当前bean是单例的,则将对应的增强方法加入advisorsCache缓存中;
  1. 如果当前bean不是单例的,则将对应的工厂factory加入到aspectFactoryCache缓存中,后面可以通过factory来解析增强方法;
  1. 循环遍历所有解析到的切面名称,根据切面名称从缓存advisorsCache中查找对应的增强器;
  1. 如果缓存中已经存在解析好的Advisor,则直接添加到advisors中;
  1. 缓存中没有,则根据切面beanName从缓存aspectFactoryCache中查找对应的工厂,然后解析出增强器,并添加到advisors中;
上述流程中,比较复杂的方法是this.advisorFactory.getAdvisors(factory):真正的去获取Advisor增强器。
getAdvisors() 方法核心逻辑有两个:
  • 获取切面类中的所有通知方法,并按照规则排序--- getAdvisorMethods(aspectClass) 方法
也就是被@Around、@After等注解修饰的方法,但是会跳过@Pointcut修饰的通知方法,源码如下:
notion image
  • 将通知方法包装成 Advisor 增强器--- getAdvisor() 方法
getAdvisor() 核心就是实例化了一个 InstantiationModelAwarePointcutAdvisorImpl 切面通知对象,我们查看其构造方法:
在 InstantiationModelAwarePointcutAdvisorImpl 的构造方法中,首先是初始化了一些属性值,然后判断当前的切面对象是否需要延时加载,分别做不同的操作。
如果切面对象是非延迟加载的情况,则会执行实例化Advice通知对象:instantiateAdvice(this.declaredPointcut)
继续跟踪到 this.aspectJAdvisorFactory.getAdvice() 方法:
到这里,postProcessBeforeInstantiation() 方法整个前置处理流程执行完成。

4、postProcessAfterInitialization

实际上也是执行父类 AbstractAutoProxyCreator 中的方法,对应源码如下:
wrapIfNecessary 方法执行
getAdvicesAndAdvisorsForBean 方法执行
findEligibleAdvisors 获取合适的通知增强器
findCandidateAdvisors 获取到所有的通知
buildAspectJAdvisors 构建所有的通知增强器
findAdvisorsThatCanApply 找到适配的通知增强器
至此已经为目标方法找到了匹配的 Advisor,后面就是为目标对象和 Advisor 创建 AOP 代理对象,在目标方法被调用的时候,调用 AOP 代理对象中的目标对象的方法和 Advisor 增强器的方法。

三、总结

最后,通过一张图总结一下AnnotationAwareAspectJAutoProxyCreator 底层做的一些工作:
notion image

参考

Spring系列:使用JdbcTemplateSpring MVC源码:DispatcherServlet
mcbilla
mcbilla
一个普通的干饭人🍚
Announcement
type
status
date
slug
summary
tags
category
icon
password
🎉欢迎来到飙戈的博客🎉
-- 感谢您的支持 ---
👏欢迎学习交流👏