1. 前记
在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程
当目标方法被多个通知匹配到时,Spring 通过引入拦截器链来保证每个通知的正常执行
在本文中,我们将会通过源码了解到 Spring 是如何支持 expose-proxy 属性的,以及通知与拦截器之间的关系,拦截器链的执行过程等
2. 背景知识
expose-proxy 怎么用
举个例子
1
2
3
4
5
6
7
8
9
10
11
<bean id="hello" class="xyz.coolblog.aop.Hello"/>
<bean id="aopCode" class="xyz.coolblog.aop.AopCode"/>
<aop:aspectj-autoproxy expose-proxy="true" />
<aop:config expose-proxy="true"> // 在这里
<aop:aspect id="myaspect" ref="aopCode">
<aop:pointcut id="helloPointcut" expression="execution(* xyz.coolblog.aop.*.hello*(..))" />
<aop:before method="before" pointcut-ref="helloPointcut" />
</aop:aspect>
</aop:config>
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Hello implements IHello {
@Override
public void hello() {
System.out.println("hello");
this.hello("world");
}
@Override
public void hello(String hello) {
System.out.println("hello " + hello);
}
}
hello()
方法调用了同类中的另一个方法hello(String)
,此时hello(String)
上的切面逻辑就无法执行了。这里,我们要对hello()
方法进行改造,强制它调用代理对象中的hello(String)
。改造结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Hello implements IHello {
@Override
public void hello() {
System.out.println("hello");
((IHello) AopContext.currentProxy()).hello("world");
}
@Override
public void hello(String hello) {
System.out.println("hello " + hello);
}
}
如上,AopContext.currentProxy()
用于获取当前的代理对象。当 expose-proxy 被配置为 true 时,该代理对象会被放入 ThreadLocal 中。关于 expose-proxy,这里先说这么多,后面分析源码时会再次提及
3. 源码分析
3.1 JDK 动态代理逻辑分析
- 检测 expose-proxy 是否为 true,若为 true,则暴露代理对象
- 获取适合当前方法的拦截器
- 如果拦截器链为空,则直接通过反射执行目标方法
- 若拦截器链不为空,则创建方法调用 ReflectiveMethodInvocation 对象
- 调用 ReflectiveMethodInvocation 对象的 proceed() 方法启动拦截器链
- 处理返回值,并返回该值
3.2 获取所有的拦截器
- 从缓存中获取当前方法的拦截器链
- 若缓存未命中,则调用 getInterceptorsAndDynamicInterceptionAdvice 获取拦截器链
- 遍历通知器列表
- 对于 PointcutAdvisor 类型的通知器,这里要调用通知器所持有的切点(Pointcut)对类和方法进行匹配,匹配成功说明应向当前方法织入通知逻辑
- 调用 getInterceptors 方法对非 MethodInterceptor 类型的通知进行转换
- 返回拦截器数组,并在随后存入缓存中
3.3 启动拦截器链
4. 总结
感觉不太理解,就不贴过来了
看到这里,感觉切面什么的和这个拦截器有了关联,但说不清在哪里,
是JDK中的invoke(CGLIB中的interceptor) 这是拦截器吧?
然后从aspect中获取到的拦截器
这个后续再看吧