本文重点分析 Spring AOP 实践一文中,通过ProxyFactoryBean
手动配置动态代理的实现原理。
原理分析将基于以下示例代码。
1 | public class UserService { |
1 |
|
从启动入口开始分析
1 | public class CodingInActionApplication{ |
1.ProxyFactoryBean 实例化
1 | new ClassPathXmlApplicationContext("applicationContext.xml") |
该行代码对应Spring IOC 容器的启动过程
如图中所示,应用上下文启动后,IOC 容器 beanFactory 需要初始化5个bean, 都是在XML 文件中定义的数据,其实前4个都是我们非常熟悉的普通业务bean。userServiceProxy
是配置的ProxyFactoryBean
, 当实例化userServiceProxy时,与常见流程略有不同,主要是以下2点
1.1 isFactoryBean
userServiceProxy
会在isFactoryBean
判断中结果为true, 需要加上FactoryBean
前缀 & 后才开始进行实例化,
在介绍FactoryBean
时,已经提到 FactoryBean也是一个bean , 所以在IOC 容器启动过程中,也会进行实例化
针对一个FactoryBean
- 使用
getBean("&beanName")
可以获取FactoryBean
实例本身,而不是FactoryBean
创建的对象。 - 使用
getBean("beanName")
则获取由FactoryBean
创建的对象。
基于以上2点,我们可以得到,在IOC 容器启动,实例化bean的过程中, 经过getBean(“&userServiceProxy”), 最终放入IOC 容器中的userServiceProxy
这个 FactoryBean
本身。
1.2 getObjectForBeanInstance
进入我们应该非常熟悉的getBean 流程
这是IOC 容器在进行bean 的初始化流程,对于userServiceProxy
来讲,此时三级缓存中肯定不存在它的缓存, 所以会进入到单例bean 的创建流程,创建流程就不进去细看了,可以去参考文章Spring bean 实例化。只重点看getObjectForBeanInstance
的处理逻辑 。
在Spring bean 实例化一文中重点讲过的用于处理FactoryBean
的getObjectForBeanInstance
方法, getBean
方法传入的参数name 是&userServiceProxy
, 说明经过getObjectForBeanInstance
方法处理后,最终返回了FactoryBean
本身, 在这次debug 过程中,即是ProxyFactoryBean
2.创建动态代理对象-FactoryBean.getObject
Spring IOC 容器启动完成后,就可以从容器中获取需要的bean 了1
UserService userService = (UserService) context.getBean("userServiceProxy");
由于userServiceProxy
是FactoryBean
, 现在执行getBean("userServiceProxy")
, 说明需要获取由它创建的动态代理对象
2.1 getObjectForBeanInstance
进入源码,再次来到getBean 流程,
此时getSingleton
,一级缓存中已经有了IOC 容器启动过程的中创建userServiceProxy
实例, 直接获取即可。再次进入getObjectForBeanInstance
方法
2.2 FactoryBean.getObject
在getObjectForBeanInstance
方法中, 逐层深入源码来到ProxyFactoryBean.getObject
方法。
实现 FactoryBean
接口的类,可以通过重写 getObject
方法来创建并返回实际的对象实例。
分析ProxyFactoryBean.getObject()
,主要代码可以分成2个步骤
- initializeAdvisorChain
- getSingletonInstance
2.3 initializeAdvisorChain
1 | private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException { |
该方法的主要逻辑是根据配置的interceptorNames
初始化AdvisorChain
,确保在执行切面逻辑时,所有的Advisor和Interceptor都已正确配置和初始化。
AdvisorChain
就是一个List数据结构,存储根据interceptorName
获取对应实例1
advice = this.beanFactory.getBean(name);
对于单例bean来讲,就是从前面启动完成的IOC 容器中,根据name 通过getBean 方法取出对应的实例
2.4 getSingletonInstance
1 | public class ProxyFactoryBean extends ProxyCreatorSupport |
以上代码删除了部分细节,只保留重点逻辑
- createAopProxy: 确定动态代理的类型, JDK 动态代理 or CGlib, 判断的逻辑也非常简单,可以简单认为就是有没有实现接口
- 实现了接口,用JDK 动态代理
- 没有实现接口,用CGlib 动态代理
- getProxy:根据确定的动态代理,创建target 的代理对象
2.4.1 createAopProxy
确定动态代理的类型, JDK动态代理 or CGlib动态代理
关于动态代理,可以点击阅读Java 动态代理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
public class ProxyCreatorSupport extends AdvisedSupport {
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
}
// 确定动态代理的类型
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 判断目标类是否是接口。如果是接口,使用JDK动态代理。
// 如果是代理类,也使用JDK动态代理。
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 如果目标类既不是接口,也不是代理类,则使用CGLIB代理。
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
}
传进来的config 就是userServiceProxy
这个ProxyFactoryBean
, 里面有各种信息,可以根据这些信息帮助判断动态代理的类型。
2.4.2 getProxy
advised 指的就是前面示例代码中配置的ProxyFactoryBean
, 它的targetSource
是要被代理的userService
3.Cglib动态代理对象-CglibAopProxy
3.1 getProxy
1 |
|
3.2 ObjenesisCglibAopProxy
从2.4.1-createAopProxy
代码中可以看出,如果是cglib 动态代理,最终返回的是ObjenesisCglibAopProxy
,而不是直接返回CglibAopProxy
在Spring AOP框架中,CglibAopProxy
和ObjenesisCglibAopProxy
都用于创建CGLIB代理,二者的区别与联系如下
- 实例化方式
- CglibAopProxy: 直接使用CGLIB库的
Enhancer
类来创建代理类,通常会调用目标类的构造函数。 - ObjenesisCglibAopProxy: 利用Objenesis库来实例化代理对象,避免了直接调用目标类的构造函数,提供了一种更灵活的实例化方式。
- CglibAopProxy: 直接使用CGLIB库的
- 应用场景
- CglibAopProxy: 适用于一般的CGLIB代理场景,当目标类的构造函数没有特殊要求时,是Spring AOP的默认选择。
- ObjenesisCglibAopProxy: 适用于需要在不调用构造函数的情况下创建代理的场景,特别是当目标类的构造函数较为复杂或有特殊限制时,提供了一种更灵活的解决方案。
3.3 getCallbacks-自动实现 MethodInterceptor
在Java 动态代理一文, 关于 CGLIB 动态代理,我们手动实现了一个MethodInterceptor
, 并将其设置为Enchaner
的Callback
1 | public class HelloWorldInterceptor implements MethodInterceptor { |
3.3.1 Callback
1 | package org.springframework.cglib.proxy; |
Callback
是一个接口,MethodInterceptor
是其常用的实现之一。MethodInterceptor
其核心方法是 intercept
,负责拦截代理对象的方法调用,应用相应的Advice 增强逻辑,并最终调用目标对象的方法。
3.3.2 MethodInceptor 在CglibAopProxy的实现
在此次XML 配置形式的AOP 中, 并没有手动实现MethodInceptor
的逻辑。 MethodInceptor
的具体实现 由CglibAopProxy
背后帮我们偷偷做了, 实现的逻辑这行代码中, 进去看下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
50Callback[] callbacks = getCallbacks(rootClass);
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
// Parameters used for optimization choices...
// 是否需要公开代理对象。
boolean exposeProxy = this.advised.isExposeProxy();
// 代理配置是否被冻结。
boolean isFrozen = this.advised.isFrozen();
// 目标源是否是静态的(即目标对象在创建时已经确定,不会变化)。
boolean isStatic = this.advised.getTargetSource().isStatic();
// Choose an "aop" interceptor (used for AOP calls).
// 创建一个DynamicAdvisedInterceptor实例,用于处理AOP调用。这是主要的AOP拦截器。
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
// Choose a "straight to target" interceptor. (used for calls that are
// unadvised but can return this). May be required to expose the proxy.
Callback targetInterceptor;
if (exposeProxy) {
targetInterceptor = (isStatic ?
new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
}
else {
targetInterceptor = (isStatic ?
new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
Callback targetDispatcher = (isStatic ?
new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());
Callback[] mainCallbacks = new Callback[] {
aopInterceptor, // for normal advice 处理常规的AOP增强。
// 优化情况下直接调用目标对象的方法。
targetInterceptor, // invoke target without considering advice, if optimized
// 对于没有增强的方法,什么也不做。
new SerializableNoOp(), // no override for methods mapped to this
// 直接调用静态目标的方法。
targetDispatcher,
// 代理配置的调度器。
this.advisedDispatcher,
// 处理equals方法的拦截器。
new EqualsInterceptor(this.advised),
// 处理equals方法的拦截器。
new HashCodeInterceptor(this.advised)
};
Callback[] callbacks;
return callbacks;
}
省略了部分代码只看重点,可以看到最终返回的Callback
数组中包含6个数据,其中包括多个MethodInceptor
的实现类。提前看下创建完成的userService 实例debug 信息, Callback
信息和代码中的数据是可以一一对应上的
MethodInterceptor
在CglibAopProxy
有多个实现,均已私有类的形式存在,下面重点分析一下DynamicAdvisedInterceptor
,常规的AOP 增强逻辑的织入均是由它实现。
3.4 执行目标方法-DynamicAdvisedInterceptor.intercept
3. 4 方法执行-intercept
经过getProxy获取Cglib 动态代理对象后, 将通过代理对象执行业务逻辑1
2userService.createUser("john");
userService.deleteUser("john");
下面将跟随debug 信息来分析 代理对象方法的执行。
执行userService.createUser("john");
代码会来到CglibAopProxy
中DynamicAdvisedInterceptor.intercept
方法
1 | private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable { |
MethodIntercept.intercept
, 在本示例中指的是具体实现类DynamicAdvisedInterceptor.intercept
是目标方法执行的拦截入口,拦截后,重点逻辑有2处
getInterceptorsAndDynamicInterceptionAdvice
获取当前类当前方法上可用的advice,CglibMethodInvocation.proceed
, 通过ReflectiveMethodInvocation.proceed
递归Advice
增强逻辑 和目标方法
3.4.1 getInterceptorsAndDynamicInterceptionAdvice
在增强逻辑和目标方法执行前,,需要先把Spring 容器中所有Advice 进行筛选,获取在当前类当前方法上可用的advice。
关于筛选逻辑,使用的是Advisor
、Pointcut
和Advice
。
Advisor
是一个复合概念,包括配套的Pointcut
和Advice
。Pointcut
中包含了对类的过滤条件ClassFilter
、方法的过滤条件MethodMatcher
Advice
是符合Pointcut
切点条件的方法上需要执行的增强逻辑。
看图中高亮的66行代码,就是我们在自定义的 LoggingAdvisor、LoggingPointcut重写的方法
最终可以用在UserService.createUser
上的Advice/Interceptor
有3个
- 一个自定义的
LoggingAdvisor
- 2个直接定义的Advice,
LoggingBeforeAdvice
、LoggingAfterAdvice
, 它们会被处理成DefaulrPointcutAdvisor
, 默认对所有类的所有方法均有效
当执行delete方法是,符合条件的interceptor 只有2个, 符合预期结果
缓存的应用
针对每个方法可用的advice 列表, 这里使用了缓存来提升性能1
2
3
4
5
6
7
8
9
10public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, { Class<?> targetClass)
MethodCacheKey cacheKey = new MethodCacheKey(method);
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
this.methodCache.put(cacheKey, cached);
}
return cached;
}
3.4.2 CglibMethodInvocation.proceed
CglibMethodInvocation
是CglibAopProxy
中的一个内部类,它继承了`ReflectiveMethodInvocation
并重写了 proceed
方法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
41class CglibAopProxy implements AopProxy, Serializable {
private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
public Object proceed() throws Throwable {
return super.proceed();
}
}
````
#### ReflectiveMethodInvocation
`ReflectiveMethodInvocation.proceed` 是CGLIB 动态代理处理目标方法调用 核心逻辑。
它根据`currentInterceptorIndex`来判断是执行Advice 增强逻辑,还是执行目标方法的调用
```java
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// 检查当前拦截器索引是否已经到达拦截器链的末尾。如果是,则调用invokeJoinpoint方法,直接执行目标方法。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// 增加当前拦截器索引,并获取下一个拦截器或增强逻辑对象
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
return proceed();
}
}
else {
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
}
ReflectiveMethodInvocation.proceed 的递归调用
ReflectiveMethodInvocation.proceed
代码中并没有使用for 循环来执行多个Advice, 而是使用递归调用的方式 实现Advice
增强逻辑 和目标方法的执行
如何理解其中递归调用
- 如果Advice增强 还未全部执行,则取下一个执行Advice, 通过
MethodInterceptor.invoke
执行增强逻辑,同时在MethodInterceptor.invoke
同样有对ReflectiveMethodInvocation.proceed
,继续判断如果是增强,执行同样的操作。 - 如果所有Advice增强 均已被访问过,则调用
invokeJoinpoint()
执行目标方法
所以当DynamicAdvisedInterceptor.intercept
中执行到new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed()
,
- 首先来到父类
ReflectiveMethodInvocation.proceed
, 通过currentInterceptorIndex
判断执行LoggingAdviorLoggingMethodInterceptor.invoke
的增强逻辑。 - 进入
LoggingAdviorLoggingMethodInterceptor.invoke
后,运行增强逻辑,再次调用ReflectiveMethodInvocation.proceed
, 通过currentInterceptorIndex
判断执行增强LoggingBeforeAdvice
, 程序将LoggingBeforeAdvice
包装成了MethodBeforeAdviceIntercept
, 调用MethodBeforeAdviceIntercept.invoke
- 进入
MethodBeforeAdviceInceptor.invoke
后,再次调用ReflectiveMethodInvocation.proceed
, 通过currentInterceptorIndex
判断执行增强LoggingAfterAdvice
,程序将LoggingAfterAdvice
包装成了AfterReturningAdviceIntercept
, 调用AfterReturningAdviceIntercept.invoke
- 进入
MethodBeforeAdviceInceptor.invoke
后,再次调用ReflectiveMethodInvocation.proceed
, 通过currentInterceptorIndex
判断需要执行invokeJoinpoint
执行new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed()
,来到父类 ReflectiveMethodInvocation.proceed
通过currentInterceptorIndex
判断执行LoggingAdviorLoggingMethodInterceptor.invoke
的增强逻辑。
再次调用 ReflectiveMethodInvocation.proceed
,通过currentInterceptorIndex
判断执行增强LoggingBeforeAdvice
再次调用 ReflectiveMethodInvocation.proceed
定义好的before 逻辑执行完成后,再次进入ReflectiveMethodInvocation.proceed, 执行afterAdvice
通过currentInterceptorIndex
判断执行增强LoggingAfterAdvice
再次进入ReflectiveMethodInvocation.proceed
,可以看到advice 已经全部执行了,可以去调用invokeJoinpoint
,即实际的业务方法了
后面一层层出栈将Advice 的全部执行完成即可。
4. JDK动态代理对象-JdkDynamicAopProxy
如果想要使用JDK 动态代理来实现,需要修改下代码,涉及到的代码修改后如下
1 | public interface UserI { |
XML 文件修改如下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<bean id="userService" class="com.example.codingInAction.service.UserService"/>
<!-- 定义 前置增强-->
<bean id="loggingBeforeAdvice" class="com.example.codingInAction.aop.LoggingBeforeAdvice"/>
<!-- 定义 后置增强 -->
<bean id="loggingAfterAdvice" class="com.example.codingInAction.aop.LoggingAfterAdvice"/>
<!-- 定义 环绕增强 -->
<bean id="loggingAdvisor" class="com.example.codingInAction.aop.LoggingAdvisor"/>
<bean id="userServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 目标对象 -->
<property name="target" ref="userService"/>
<!-- 指定使用的接口 -->
<property name="proxyInterfaces">
<list>
<value>com.example.codingInAction.service.UserI</value>
</list>
</property>
<!-- 拦截器 -->
<property name="interceptorNames">
<list>
<value>loggingAdvisor</value>
<value>loggingBeforeAdvice</value>
<value>loggingAfterAdvice</value>
</list>
</property>
<!-- 强制使用JDK动态代理 -->
<property name="proxyTargetClass" value="false"/>
</bean>
4.1 getProxy
通过Proxy类,利用反射创建代理对象,整体比较简单,可以参考Java 动态代理中对Proxy 类的介绍,这里不再赘述
1 | final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable { |
4.2 实现 InvocationHandler
观察JdkDynamicAopProxy
这个类的定义,可以看到这个类本身就实现了InvocationHandler1
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {}
在java 动态代理中说到,通过Proxy 类获取了一个参数为InvocationHandler
的构造函数创建代理对象,并将自定义的InvocationHandler
传进去
观察JdkDynamicAopProxy
创建代理对象的过程,遵循这一原则,且传进入的参数就是JdkDynamicAopProxy
本身。
4.2 执行目标方法-InvocationHandler.invoke
根据在java 动态代理中学习的源码,对于JDK 动态代理,当执行代理对象的方法时,首先会被自定义InvocationHandler.invoke
方法。 所以这里就是进入到JdkDynamicAopProxy.invoke
方法中
InvocationHandler.invoke
和CGLIB 动态代理中的MethodInterceptor.intercept
一样,负责拦截代理对象的方法调用,应用相应的Advice 增强逻辑,并最终调用目标对象的方法。 所以其重点逻辑也是一样的
getInterceptorsAndDynamicInterceptionAdvice
获取当前类当前方法上可用的adviceReflectiveMethodInvocation.proceed
, 通过ReflectiveMethodInvocation.proceed
递归Advice
增强逻辑 和目标方法,JdkDynamicAopProxy
并没有继承ReflectiveMethodInvocation
实现一个新的子类,而是直接使用的ReflectiveMethodInvocation
1 | final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable { |
4.3.1 getInterceptorsAndDynamicInterceptionAdvice
和3.4.1 小节中的内容一样
4.3.2 ReflectiveMethodInvocation.proceed
和3.4.1 小节中的逻辑一致, 都是通过ReflectiveMethodInvocation.proceed
的递归调用实现增强逻辑和目标方法的执行
5. MethodInterceptor.invoke & MethodInterceptor.intercpt
在前面的内容中, 多次提到MethodInterceptor
, MethodInterceptor.invoke
和MethodInterceptor.intercpt
这2个方法,要注意区分一下, 这2个MethodInterceptor
指的不是同一个,虽然二者的功能类似
5.1 org.springframework.cglib.proxy.MethodInterceptor
org.springframework.cglib.proxy.MethodInterceptor
是 CGLIB(Code Generation Library)库中的接口,
1 | package org.springframework.cglib.proxy; |
在Java 动态代理 CGLIB 动态部分,使用的是这个MethodInterceptor
1 | public class HelloWorldInterceptor implements MethodInterceptor { |
5.2 org.aopalliance.intercept.MethodInterceptor
org.aopalliance.intercept.MethodInterceptor
是 AOP Alliance 规范中的一个接口,Spring AOP 采用了这一规范来提供方法拦截功能。AOP Alliance 是一个标准化的 AOP API 规范,旨在提供统一的 AOP 编程接口。
1 | public interface MethodInterceptor extends Interceptor { |
在Spring AOP 实践, 实现环绕增强时,使用的是这个MethodInterceptor
1 | // 定义一个 环绕增强 |
5.3 区别
org.springframework.cglib.proxy.MethodInterceptor
和 org.aopalliance.intercept.MethodInterceptor
都是强大的方法拦截器接口,允许在方法调用前后添加自定义逻辑。各自有不同的适用场景和实现机制。
- 适用范围:
org.springframework.cglib.proxy.MethodInterceptor
适用于基于类的代理,尤其是无接口的类。org.aopalliance.intercept.MethodInterceptor
适用于基于接口的代理,广泛用于 Spring AOP 框架中。
- 实现机制:
- CGLIB 基于字节码操作,通过生成目标类的子类实现代理。
- AOP Alliance 基于接口,通过代理接口实现方法拦截。
- 依赖库:
org.springframework.cglib.proxy.MethodInterceptor
是 CGLIB 库的一部分,需要引入 CGLIB 依赖。org.aopalliance.intercept.MethodInterceptor
是 AOP Alliance 规范的一部分,Spring AOP 默认支持这一规范。
- 方法签名:
- CGLIB 的
MethodInterceptor
使用intercept
方法,其参数包括目标对象、方法对象、参数数组和方法代理。 - AOP Alliance 的
MethodInterceptor
使用invoke
方法,其参数是一个封装了方法调用信息的MethodInvocation
对象。
- CGLIB 的