Spring AOP 实现
AOP概述
为什么需要AOP?
问题一:如何减少重复的代码?
将重复的代码抽离出来,在多个地方显示地声明调用
问题二:如何减少“声明调用”的代码重复?
再抽离,再显示地声明?
问题三:根据需要,每个地方的“声明调用”可能不完全相同,这是就无法统一抽离,并且这些“重复”可能是业务无关的,不应该在业务中进行“声明调用”
为了解决问题三,提出了AOP
AOP概念抽象:
- 连接点:业务代码块的边界处,可以使用声明进行功能的增强
- 切点:可以认为是连接点筛选的一个条件
- 通知:增强的相关逻辑
- 通知器:将切点和通知结合起来
- 织入:将通知写入到连接点的过程
根据织入的时间点可以分为:
- 编译器织入:生成已增强的.class
- 加载器织入:加载.class时完成增强
- 运行期织入:运行时动态生成增强代理类
spring AOP 和 AspectJ 的区别与联系?
AspectJ 主要的织入方式是编译器织入和加载器织入
spring AOP 主要的织入方式是运行期织入,使用JDK代理或者Cglib代理
spring AOP 可以基于 xml 实现通知器的配置,也可以基于 AspectJ 的注解实现通知器的配置,但最终都会适配为对拦截器的处理(详细见后)
Spring AOP 设计与实现
接口体系
ProxyConfig:代理的配置类,为其 AOP 代理对象工厂实现类提供基本的配置属性
Adivised:接口,定义了代理配置信息的获取方法,以及对 AOP 中通知、通知器、目标对象等的装配方法
AdivisedSupport:Adivised 的实现类,基于 ProxyConfig
ProxyCreatorSupport:基于 AdivisedSupport 中 AOP 配置和代理配置,提供生成代理类的功能(使用默认的代理对象工厂实现DefaultAopProxyFactory,该工厂基于 jdk 和 cglib)
- ProxyCreatorSupport 子类:提供 aop 的装配
- ProxyFactoryBean:基于 xml 进行装配
- ProxyFactory:基于编程的方式进行装配
- AspectJProxyFactory:基于 AspectJ 注解进行装配
ProxyConfig详情可见:https://www.jianshu.com/p/b38b1a8cb0a4
ProxyFactoryBean
接下来以 ProxyFactoryBean 为代表,对 spring AOP 的源码逻辑进行分析:
XML装配方式:
1 |
|
由于 ProxyFactoryBean 是 spring 中管理的一个 FactoryBean 类,所以可以从他的 getObject() 方法着手:
1 | class ProxyFactoryBean { |
先看 ProxyFactoryBean#initializeAdvisorChain() 方法
1 | class ProxyFactoryBean{ |
接着看 ProxyFactoryBean#addAdvisorOnChainCreation
1 | class ProxyFactoryBean{ |
在回到 getObject() 中看 getSingletonInstance() 的逻辑,代理类的生成逻辑可追溯到 DefaultAopProxyFactory#createAopProxy()
1 | class DefaultAopProxyFactory{ |
以 JdkDynamicAopProxy 为例进行分析,JDK 的代理主要的代理逻辑在 InvocationHandler#invoke 中,因此我们查看 JdkDynamicAopProxy#invoke 方法
1 | class JdkDynamicAopProxy implements InvocationHandler{ |
通知的形容很多,如before、afterReturn、afterThrow,这些不同的形式如何统一成拦截器链进行工作?连接点如何工作?
我们进入连接点中查看 ReflectiveMethodInvocation#proceed 方法
1 | class ReflectiveMethodInvocation{ |
由此可见,这里关键的类是 InterceptorAndDynamicMethodMatcher 和 MethodInterceptor
MethodInterceptor:Interceptor的子类,Interceptor又是Advice的子类,所以拦截器是一种特殊的通知
InterceptorAndDynamicMethodMatcher :包装了 MethodInterceptor 和 MethodMatcher,MethodMatcher 是切点 PointCut 判断的方法
1 | class InterceptorAndDynamicMethodMatcher { |
这里有两个问题:
- 我们定义的是通知器,为何变成了 MethodInterceptor ?
- MethodInterceptor 里的 invoke 到底做什么什么事情?
我们大致可以猜到,某个地方采用了适配器,在 JdkDynamicAopProxy#invoke 中关键的一步:
1 | //2. 获取拦截器链(关键步骤) |
追溯下去可以来到 DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice 中:
1 | class DefaultAdvisorChainFactory implements AdvisorChainFactory{ |
GlobalAdvisorAdapterRegistry 是一个单例模式,可以获取 AdvisorAdapterRegistry 的单例对象,默认是 DefaultAdvisorAdapterRegistry
DefaultAdvisorAdapterRegistry 内注册了 各种 AdvisorAdapter ,我们接着进入 DefaultAdvisorAdapterRegistry#getInterceptors 中
1 | class DefaultAdvisorAdapterRegistry { |
AdvisorAdapter 是真正进行适配的类
1 | public interface AdvisorAdapter { |
spring 中默认的有三类:
- AfterReturningAdviceAdapter:适配 AfterReturningAdvice
- MethodBeforeAdviceAdapter:适配 MethodBeforeAdvice
- ThrowsAdviceAdapter:适配 ThrowsAdvice
以 AfterReturningAdviceAdapter 为代表进行分析:
1 | class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable { |
可以看到,将 AfterReturningAdvice 转换成 AfterReturningAdviceInterceptor,到此我们解决了问题1,那么分析问题2(MethodInterceptor 里的 invoke 到底做什么什么事情?)
以 AfterReturningAdviceInterceptor 为代表进行分析:
1 | public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable { |
ProxyFactory
上面就是对ProxyFactoryBean的分析,我们说了是基于XML对通知器的进行装配,接着看看 ProxyFactory 编程式装配:
1 | class Test{ |
可以看到 ProxyFactory 就是用代码进行装配,其底层都是一样的,再看最后的 AspectJProxyFactory
AspectJProxyFactory
AspectJProxyFactory 其实和真正的 AspectJ 关系不大,直接借用了 AspectJ 的注解来装配通知器
使用如下:
1 |
|
有兴趣的可以继续查看其源码
总结:
- 无论是 Advice 还是 Interceptor,最终都会处理成 Interceptor链,在动态代理中进行增强