1 前记
2. 源码分析
2.1 AOP入口分析
以上就是 Spring AOP 创建代理对象的入口方法分析,过程比较简单,这里简单总结一下:
- 若 bean 是 AOP 基础设施类型,则直接返回
- 为 bean 查找合适的通知器
- 如果通知器数组不为空,则为 bean 生成代理对象,并返回该对象
- 若数组为空,则返回原始 bean
2.2 筛选合适的通知器
AspectJ 表达式进行匹配
2.2.1 查找通知器
首先,说一下配置AOP的方式,one is XML the other is 注解(最近在学英语,突然想到的用法,指:只有俩个东西可以用这个)
对于这俩种配置方式Spring的处理逻辑是不同的
2.2.1.1 findCandidateAdvisors 方法分析
- 从容器中查找所有类型为 Advisor 的 bean 对应的名称
- 遍历 advisorNames,并从容器中获取对应的 bean
2.2.1.2 buildAspectJAdvisors 方法分析
解析 @Aspect 注解
- 获取容器中所有 bean 的名称(beanName)
- 遍历上一步获取到的 bean 名称数组,并获取当前 beanName 对应的 bean 类型(beanType)
- 根据 beanType 判断当前 bean 是否是一个的 Aspect 注解类,若不是则不做任何处理
- 调用 advisorFactory.getAdvisors 获取通知器
下面我们来重点分析advisorFactory.getAdvisors(factory)
这个调用,如下:
getAdvisor 方法包含两个主要步骤
- 获取 AspectJ 表达式切点
- 创建 Advisor 实现类
获取 AspectJ 表达式切点
说完切点的获取过程,下面再来看看 Advisor 实现类的创建过程。如下:
上面是 InstantiationModelAwarePointcutAdvisorImpl 的构造方法,不过我们无需太关心这个方法中的一些初始化逻辑。我们把目光移到构造方法的最后一行代码中,即 instantiateAdvice(this.declaredPointcut),这个方法用于创建通知 Advice。
下面来总结一下获取通知器(getAdvisors)整个过程的逻辑,如下:
- 从目标 bean 中获取不包含 Pointcut 注解的方法列表
- 遍历上一步获取的方法列表,并调用 getAdvisor 获取当前方法对应的 Advisor
- 创建 AspectJExpressionPointcut 对象,并从方法中的注解中获取表达式,最后设置到切点对象中
- 创建 Advisor 实现类对象 InstantiationModelAwarePointcutAdvisorImpl
- 调用 instantiateAdvice 方法构建通知
- 调用 getAdvice 方法,并根据注解类型创建相应的通知
如上所示,上面的步骤做了一定的简化。总的来说,获取通知器的过程还是比较复杂的,并不是很容易看懂。大家在阅读的过程中,还要写一些测试代码进行调试才行。调试的过程中,一些不关心的调用就别跟进去了,不然会陷入很深的调用栈中,影响对源码主流程的理解。
现在,大家知道了通知是怎么创建的。那我们难道不要去看看这些通知的实现源码吗?显然,我们应该看一下。那接下里,我们一起来分析一下 AspectJMethodBeforeAdvice,也就是 @Before 注解对应的通知实现类。看看它的逻辑是什么样的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice {
public AspectJMethodBeforeAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
// 调用通知方法
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
@Override
public boolean isBeforeAdvice() {
return true;
}
@Override
public boolean isAfterAdvice() {
return false;
}
}
protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {
// 调用通知方法,并向其传递参数
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
actualArgs = null;
}
try {
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
// 通过反射调用通知方法
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("Mismatch on arguments to advice method [" +
this.aspectJAdviceMethod + "]; pointcut expression [" +
this.pointcut.getPointcutExpression() + "]", ex);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
如上,AspectJMethodBeforeAdvice 的源码比较简单,这里我们仅关注 before 方法。这个方法调用了父类中的 invokeAdviceMethod,然后 invokeAdviceMethod 在调用 invokeAdviceMethodWithGivenArgs,最后在 invokeAdviceMethodWithGivenArgs 通过反射执行通知方法
2.2.2 筛选合适的通知器
查找出所有的通知器,整个流程还没算完,接下来我们还要对这些通知器进行筛选。适合应用在当前 bean 上的通知器留下,不适合的就让它自生自灭吧
2.2.3 拓展筛选出通知器列表
3 总结
看到 2.2.2 的时候就放弃了,这篇主要从AOP的入口开始,通过对bean的后续处理,将bean变成代理对象放入容器
在变成代理对象时,需要根据XML或者是注解来构建通知器,完善最终的代理对象
具体逻辑先不搞了,直觉感觉面试不会问到,看的困死了