深入Spring AOP源码, 在Spring AOP的设计实现中主要使用了 JDK 动态代理,在特定场景下(被代理对象没有实现的接口)也用到了 CGLIB 生成代理。通过源码设计可以看到,首先是为目标对象建立了代理对象(是 JDK 动态代理或 CGLIB 实现)。然后启动为代理对象配置的拦截器,对横切面(目标方法集合)进行相应的增强,将 AOP 的横切面设计和 Proxy 模式有效地结合起来,实现了在 AOP 中定义好的各种织入方式。
主要基础接口
在Spring AOP框架中,Advice
、Pointcut
和Advisor
是三个核心接口,共同定义了切面的行为。
1. Advice 通知接口:增强逻辑的载体
本接口定义了切面的增强方式,封装在连接点执行的横切逻辑;如:前置增强 BeforeAdvice
,后置增强 AfterAdvice
,异常增强 ThrowsAdvice
,环绕增强MethodInterceptor(AOP Alliance 标准)
等。看两个主要的子接口的源码:
2. Pointcut 切点接口:精准定位需要增强的方法
本接口用来定义需要增强的目标方法的集合,一般使用正则表达式去匹配筛选指定范围内的所有满足条件的目标方法。作用是确定在哪些连接点(Joinpoint
)(如方法执行)应用增强逻辑。Pointcut
接口有很多实现,我们主要看一下 JdkRegexpMethodPointcut
和 NameMatchMethodPointcut
的实现原理:
JdkRegexpMethodPointcut 的实现源码
NameMatchMethodPointcut 的实现源码
3. Advisor 通知器接口:组合 Pointcut 和 Advice
将 Pointcut
和 Advice
有效地结合在一起组合成一个完整的切面定义。它定义了在哪些方法(Pointcut
)上执行哪些动作(Advice
)。下面看一下 DefaultPointcutAdvisor
的源码实现,它通过持有 Pointcut
和 Advice
属性来将两者有效地结合在一起。
DefaultPointcutAdvisor 实现源码
创建代理对象的触发入口
创建代理的触发点在 Spring AOP 中确实是多地方, Spring AOP 的代理创建机制被设计得相当灵活,可以通过不同层次和方式的 API 或配置来触发。
- 编程式创建 - 最底层/最灵活 (
ProxyFactory
):开发者直接实例化ProxyFactory
- 声明式创建 - IoC 容器集成 (
ProxyFactoryBean
):当 Spring 容器实例化并配置定义在 XML 或 Java Config 中的 ProxyFactoryBean Bean 时。是早期 Spring AOP 配置的主要方式之一。 - 自动代理创建器 (
AbstractAutoProxyCreator
及其子类 - 容器级自动化):Spring 容器在 Bean 初始化之后(postProcessAfterInitialization
方法) 调用这些自动代理创建器。创建器检查当前 Bean 是否匹配配置的Pointcut
(即是否需要被代理)。
这里主要以 ProxyFactoryBean
的实现为例,对 AOP 的实现原理进行分析。
ProxyFactoryBean
1. 生成代理对象的源码入口
ProxyFactoryBean
的 getObject()
方法先对通知器链进行了初始化,然后根据被代理对象类型的不同,生成代理对象。
2. 初始化 Advisor 链
生成单例(singleton)的代理对象在 getSingletonInstance()
方法中完成,这是 ProxyFactoryBean
生成 AopProxy
代理对象的调用入口。代理对象会封装对 target
对象的调用,针对 target
对象的方法调用会被这里生成的代理对象所拦截。
3. 生成单例代理对象
上面的 createAopProxy()
方法,调用了 ProxyFactoryBean
的父类 ProxyCreatorSupport
中的实现。
接着就进入并比较重要的核心源码类DefaultAopProxyFactory
,它是AopProxyFactory
接口的实现类;主要看createAopProxy(AdvisedSupport config)
方法。
动态代理选择机制
Spring AOP的核心代理选择逻辑由DefaultAopProxyFactory
实现,其决策流程如下:
代理选择策略:
- 强制CGLIB场景:
- 显式设置
proxyTargetClass=true
- 目标类未实现任何接口(除
SpringProxy
外) - 历史遗留的optimize=true(Spring 5+已废弃)
- 显式设置
- 强制JDK代理场景:
- 目标对象是接口类型
- 目标对象已是JDK代理类
- 默认策略:目标类实现接口时使用JDK代理
设计启示:该策略模式实现使Spring能灵活应对不同代理需求,同时屏蔽底层技术差异。
代理对象创建过程
1. JDK动态代理实现(JdkDynamicAopProxy)
JDK 动态代理 生成 AopProxy
代理对象
通过 JdkDynamicAopProxy
的源码可以非常清楚地看到,其使用了 JDK 动态代理 的方式生成了 代理对象。JdkDynamicAopProxy
实现了 InvocationHandler
接口,并通过 java.lang.reflect.Proxy
的 newProxyInstance()
静态方法 生成代理对象并返回。
2. CGLIB代理实现(ObjenesisCglibAopProxy)
CGLIB 生成 AopProxy
代理对象
在为目标对象生成代理对象之后,在调用 代理对象 的目标方法时,目标方法会进行 invoke()
回调(JDK 动态代理) 或 callbacks()
回调(CGLIB),然后就可以在回调方法中对目标对象的目标方法进行拦截和增强处理了。
Spring AOP 拦截器调用的实现
代理执行流程(核心拦截器链机制)
在通过 JDK 的 Proxy
类生成代理对象时,相关的拦截器已经配置到了代理对象内部持有的 InvocationHandler
实例 (对于 JDK 代理是 JdkDynamicAopProxy
) 所引用的 AdvisedSupport
配置对象中。拦截器最后起作用,是通过调用代理对象的目标方法时,代理机制会触发其关联的处理器 (InvocationHandler.invoke()
),这些处理器回调方法会从 AdvisedSupport
中获取拦截器链并执行。
在通过 CGLIB 生成代理对象时,相关的拦截器已经配置到了代理对象内部持有的 CGLIB Callback
实例 (核心是 DynamicAdvisedInterceptor
) 所引用的 AdvisedSupport
配置对象中。当调用代理对象的目标方法时,代理机制会触发其关联的处理器 (MethodInterceptor.intercept()
),这些处理器回调方法会从 AdvisedSupport
中获取拦截器链并执行。
图解关系:
+---------------------+ (配置阶段)
| ProxyFactoryBean | -------------------+
| (or ProxyFactory) | |
+----------+----------+ |
| Configures | Holds
v v
+---------------------+ +--------------------------+
| AdvisedSupport | <---- | JdkDynamicAopProxy | (JDK Handler)
| - TargetSource | | - advised: AdvisedSupport |------------------+
| - List<Advice> | +--------------------------+ |
| - List<Advisor> | |
| - ... | +--------------------------+ |
+---------------------+ | DynamicAdvisedInterceptor | (CGLIB Callback) |
| - advised: AdvisedSupport |------------------+
+--------------------------+
^ ^
| Associated with | Associated with
| |
+---------------------+ +----+--------+ +-----+--------+
| Proxy Object | | JDK Proxy | | CGLIB Proxy |
| (Implements | | Object | | (Subclass) |
| interface(s)) | +-------------+ +--------------+
| OR | | |
| (Subclass) | | Holds | Holds
+---------------------+ v v
+------------------+ +-------------------+
| InvocationHandler| | Callback (e.g. |
| (JdkDynamicAopProxy)| | DynamicAdvised..) |
+------------------+ +-------------------+
| |
| (invoke called) | (intercept called)
v v
[Gets interceptor chain from AdvisedSupport]
[Creates MethodInvocation]
[Runs interceptor chain -> calls target method]
前面已经通过两种不同的方式生成了 AopProxy
代理对象,下面我们先看一下 JdkDynamicAopProxy
中的 invoke()
回调方法 中对拦截器调用的实现。
1. JdkDynamicAopProxy 的 invoke() 拦截
2. CglibAopProxy 的 intercept() 拦截
CglibAopProxy
的 intercept()
回调方法实现和 JdkDynamicAopProxy
的 invoke()
非常相似,只是在 CglibAopProxy
中构造 CglibMethodInvocation
对象来完成拦截器链的调用,而在 JdkDynamicAopProxy
中则是通过构造 ReflectiveMethodInvocation
对象来完成的。
3. 目标对象中目标方法的调用
对目标对象中目标方法的调用,JdkDynamicAopProxy
是在 AopUtils
工具类中利用反射机制完成的,具体代码如下。
对目标对象中目标方法的调用,CglibAopProxy
是直接通过调用 MethodProxy
的invoke()
,MethodProxy
的 invoke
方法用于在相同类型的另一个对象上调用原始方法(非拦截方法)。它允许直接调用被代理的方法,绕过 CGLIB 应用的任何拦截器或增强。具体代码如下。
4. AOP 拦截器链的调用
JdkDynamicAopProxy
和 CglibAopProxy
虽然使用了不同的代理对象,但对 AOP 拦截的处理却是相同的,都是通过 ReflectiveMethodInvocation
的 proceed()
方法实现的。
责任链模式:通过递归调用实现拦截器链的顺序执行,每个拦截器控制是否向下传递
思考点:为什么拦截器链为空时,JDK 代理:直接通过 AopUtils.invokeJoinpointUsingReflection(target, method, args)
调用;而CGLIB 代理:直接通过 methodProxy.invoke(target, args)
调用;拦截器链不为空时,两种代理统一使用 AopUtils.invokeJoinpointUsingReflection(target, method, args)
调用原始方法?
解答: 这种设计是 Spring 在架构一致性、维护成本和性能优化之间做出的合理权衡。虽然理论上 CGLIB 在拦截链场景也能用 methodProxy.invoke()
,但实际收益微乎其微,不值得为此破坏架构的纯净性。
到此, Spring AOP的过程就结束了, 下面在看下拦截器链(配置的通知器)的获取源码细节。
5. 配置通知器
由源码可知配置的通知器是在AdvisedSupport
类中获取的;AdvisedSupport
中实现了获取拦截器链的方法,并使用了缓存。
获取拦截器链的工作是由 AdvisorChainFactory
完成的,他是一个拦截器链的生成工厂。由于 AdvisorChainFactory
接口只有一个默认实现类 DefaultAdvisorChainFactory
,所以我们直接看这个类中的实现。
由上面源码可看出 advisor
通知器(拦截器链)是从 AdvisedSupport
中获取的,而 advisor
的初始化则是在 ProxyFactoryBean
的 getObject()
方法中完成的,在前面我们已经看过源码了。
注意,
Advisor
本身就被配置为 bean,所以它的获取也是通过 IoC 容器 获得的。
6. Advice 通知的实现
从 DefaultAdvisorChainFactory
类中的 getInterceptorsAndDynamicInterceptionAdvice()
方法我们可以看到,其通过 AdvisorAdapterRegistry
实例对象的 getInterceptors()
方法,利用配置的 advisor
完成了对拦截器的适配和注册。
DefaultAdvisorAdapterRegistry
的 getInterceptors()
方法 封装了 advice
织入实现的入口。
从 DefaultAdvisorAdapterRegistry
的实现中可以看到,其使用了MethodBeforeAdviceAdapter
、AfterReturningAdviceAdapter
、ThrowsAdviceAdapter
等一系列的 AdviceAdapter
适配器;它们完全和 Advice
的类型一一对应,它们都是实现了 AdviceAdapter
接口的同一层次类,各自承担着不同的适配任务,一对一地服务于不同的 Advice
实现。
下面我们主要看下 MethodBeforeAdviceAdapter
的源码实现。
可以看到,其中的 getInterceptor()
方法 把 Advice
从 Advisor
中取出来,然后创建了一个 MethodBeforeAdviceInterceptor
对象,并返回,这个对象中持有对 Advice
的引用。下面我们看一下 MethodBeforeAdviceInterceptor
拦截器的源码实现。
可以看到,MethodBeforeAdviceInterceptor
的 invoke()
方法 先是触发了 advice 的 before()方法
,然后才是 MethodInvocation 的 proceed()方法
调用。
简单回顾一下之前的代码,在 AopProxy
代理对象 触发的 ReflectiveMethodInvocation
的 proceed()
中,在取得 拦截器 interceptor 后调用了其 invoke()
方法。按照 AOP 的配置规则,ReflectiveMethodInvocation
触发的拦截器 invoke()
回调,最终会根据 Advice
类型的不同,触发 Spring 对不同的 Advice
的拦截器封装,比如 MethodBeforeAdvice
最终会触发 MethodBeforeAdviceInterceptor
的 invoke()
回调,其它两个拦截器与此类似。
代理执行流程(核心拦截器链机制)
执行链构建流程(以JDK代理实现为例的图示)
源码分析实践建议
- 调试技巧:
# 启用代理类保存
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
# 开启AOP调试日志
logging.level.org.springframework.aop=DEBUG
- 性能优化点:
- 避免宽泛切点表达式(如
execution(* *.*(..))
) - 优先使用
@annotation
精确匹配注解方法 - 自调用问题通过
AopContext.currentProxy()
解决
- 避免宽泛切点表达式(如
- 核心源码路径:
- 代理创建:
org.springframework.aop.framework.DefaultAopProxyFactory
- 执行链:
org.springframework.aop.framework.ReflectiveMethodInvocation
- 注解解析:
org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
- 代理创建:
最佳实践:结合
@EnableAspectJAutoProxy(exposeProxy=true)
解决内部调用代理失效问题,深度理解Spring AOP需把握代理创建时机、执行链机制、注解适配原理三大核心环节。
通过源码分析可见,Spring AOP通过精妙的责任链模式与动态代理技术的结合,实现了声明式切面编程能力,其设计充分体现了开闭原则和单一职责原则,是框架设计中模式应用的典范。很值得我们学习借鉴,与诸君共勉吧!
End!