在上篇文章SpringAop源码分析(基于注解)一中,我们分析了Spring是怎样把专门处理AOP的类进行注册的,本篇文章我们将分析这个类是怎么对AOP起作用的。
一、入口
我们已经知道BeanPostProcessors
是在Bean实例化前后起作用的,如果看过前面的文章Spring Ioc源码分析 之 Bean的加载(八):初始化,应该知道Spring是在AbstractAutowireCapableBeanFactory#doCreateBean()
方法中有一个初始化Bean的方法:
1 | 复制代码exposedObject = initializeBean(beanName, exposedObject, mbd) |
继续深入:
1 | 复制代码protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { |
其中第<2>步就是触发我们BeanPostProcessors
的地方。
我们再回过头来看AnnotationAwareAspectJAutoProxyCreator
有一个上层父类AbstractAutoProxyCreator
,它实现了SmartInstantiationAwareBeanPostProcessor接口,来看下它的主要方法。
1 | 复制代码//AbstractAutoProxyCreator.java |
可以看到AbstractAutoProxyCreator
类里实现了postProcessAfterInitialization()
方法,该方法将在Bean初始化之后调用。
接着看wrapIfNecessary方法:
1 | 复制代码//AbstractAutoProxyCreator.java |
这里看起来逻辑不复杂:
- 找到匹配该Bean的所有通知器
- 创建代理对象
但是这两步具体细节就很复杂了,我们一个一个来看,先看第一步。
二、找到匹配该Bean的所有通知器
1 | 复制代码//AbstractAdvisorAutoProxyCreator |
继续深入:
1 | 复制代码//AbstractAdvisorAutoProxyCreator.java |
2.1、获取有所通知器
接上面的代码:
1 | 复制代码//AnnotationAwareAspectJAutoProxyCreator.java |
2.1.1、调用父类方法从容器中获取所有的通知器
先看一下调用父类的方法
1 | 复制代码//AbstractAdvisorAutoProxyCreator.java |
继续深入:
1 | 复制代码//BeanFactoryAdvisorRetrievalHelper.java |
这段代码虽然很长,但并不复杂:
- 先从缓存中获取,获取不到就从IOC容器中获取类型为
Advisor
的BeanName - 遍历获取到的BeanName,调用getBean()方法获取实例,并加入到通知器集合中
2.1.2、解析 @Aspect 注解,并构建通知器
代码如下:
1 | 复制代码//BeanFactoryAspectJAdvisorsBuilder.java |
代码很长,但我们在只需要关注关键步骤即可:
- 从容器中获取所有 bean 的名称
- 遍历,根据 beanName 获取 bean 的类型
- 检测 beanType 是否包含 Aspect 注解
- 从工厂中获取通知器
这里也可以和我们前面的demo对应起来,我们之前定义了一个LogAspect
的类,然后用注解@Component
和@Aspect
声明了。
上面这段代码的逻辑就是:找到这个标注@Aspect
的类,并找到里面定义的通知器,如@Before
、@After
等。
同时这也回答了上篇文章的一个问题:Spring是怎么找到我们定义的切面的?
1 | 复制代码@Aspect |
接着看从工厂获取通知器的方法this.advisorFactory.getAdvisors(factory)
1 | 复制代码//ReflectiveAspectJAdvisorFactory.java |
接着追踪getAdvisor()
方法:
1 | 复制代码public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, |
这里的逻辑其实也不复杂,。
- <1>,获取切面中的所有方法,排除@Pointcut修饰的方法
- <2>,遍历所有方法
- <3>,获取该方法的切点
- <4>,根据AspectJ相关注解,包括 @Before,@After、@Pointcut等获取切点
- <5>,设置切点表达式到AspectJExpressionPointcut
封装结果如下:
- <6>,创建Advisor,封装切点表达式、通知名称、方法名称等
封装结果如下:
this.advisorFactory.getAdvisors(factory)
这段代码的最终目的,就是获取该切面所有的通知方法、它们的切点,并把它们都封装成一个个Advisor
。
但其实每个Advisor
里的Advice
都是不同的,我们来看下创建Advisor
的过程,即第<6>步:
1 | 复制代码//InstantiationModelAwarePointcutAdvisorImpl.java |
上面是 InstantiationModelAwarePointcutAdvisorImpl
的构造方法,不过我们无需太关心这个方法中的一些初始化逻辑。我们把目光移到构造方法的最后一行代码中,即 instantiateAdvice(this.declaredPointcut)
,这个方法用于创建通知 Advice。
1 | 复制代码private Advice instantiateAdvice(AspectJExpressionPointcut pcut) { |
可见,根据注解的不同,创建不同的Advice
,并封装到Advisor
中。
2.2、筛选合适的通知器
现在我们已经拿到了所有通知器,接下来就要筛选出匹配当前Bean的通知器。
代码List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
中:
1 | 复制代码//AbstractAdvisorAutoProxyCreator.java |
继续深入:
1 | 复制代码//AbstractAdvisorAutoProxyCreator.java |
1 | 复制代码//AopUtils.java |
1 | 复制代码//AopUtils.java |
1 | 复制代码//AopUtils.java |
上面就是筛选通知器的过程,筛选的工作主要由 ClassFilter 和 MethodMatcher 来完成。关于 ClassFilter 和 MethodMatcher,在 AOP 中,切点 Pointcut 是用来匹配连接点的,以 AspectJExpressionPointcut 类型的切点为例。该类型切点实现了ClassFilter 和 MethodMatcher 接口,匹配的工作则是由 AspectJ 表达式解析器负责。除了使用 AspectJ 表达式进行匹配,Spring 还提供了基于正则表达式的切点类,以及更简单的根据方法名进行匹配的切点类。大家有兴趣的话,可以自己去了解一下,这里就不多说了。
现在,我们知道了通知是怎么创建和筛选的。那下篇文章,我们一起来分析一下AOP是怎么创建代理对象的。
总结
这篇文章花了比较大的功夫,受个人能力限制,很遗憾没有对里面的源码作非常详细的分析,只理解了主流程,希望朋友们发现文章中的错误或不妥之处,还请指出,互相交流~
参考:
www.tianxiaobo.com/2018/06/20/…
本文转载自: 掘金