1. 从整体视角学习Bean是如何被AOP代理的
为了全面理解Bean
是如何被AOP
代理的,我们把前面几篇文章串联一下,需要关注以下几点,并针对每个关键点学习相应的源码部分:
1. AOP代理的触发机制(本章需要讲解的)
理解Spring
如何决定哪些Bean
需要被代理。
关键点:
- BeanPostProcessor接口:
Spring AOP
的自动代理创建器实现了这个接口,通过它在Bean
初始化的前后进行处理。
源码部分:AbstractAutoProxyCreator
(位于org.springframework.aop.framework.autoproxy
包中)
- 方法:
postProcessAfterInitialization
- 方法:
wrapIfNecessary
2. 确定哪些Bean需要代理(前面已讲)
理解Spring
如何确定哪些Bean
需要被代理,这通常涉及到扫描和匹配切面。
这部分内容在Spring高手之路21——深入剖析Spring AOP代理对象的创建的第
2
节“匹配增强器Advisors
(源码分析+时序图说明)”已经讲解,可自行前往翻阅学习。
关键点:
- Advisor和Advice:这些是
Spring AOP
中的切面和通知。
源码部分:
- AbstractAdvisorAutoProxyCreator的方法:
getAdvicesAndAdvisorsForBean
(位于org.springframework.aop.framework.autoproxy
包中)
3. 创建代理对象(前面已讲)
理解Spring
如何创建代理对象,包括使用JDK
动态代理和CGLIB
代理。
这部分内容在Spring高手之路22——AOP切面类的封装与解析的第
3.2
节“TargetSource 的构建”有提到,可自行前往翻阅学习。
关键点:
- 代理工厂(ProxyFactory):用于创建代理对象。
- 动态代理实现(
JDK
动态代理和CGLIB
)。
源码部分:
- ProxyFactory的方法:
getProxy
(位于org.springframework.aop.framework
包中)
- JdkDynamicAopProxy的方法:
invoke
(位于org.springframework.aop.framework
包中)
- CglibAopProxy的方法:
getProxy
(位于org.springframework.aop.framework
包中)
4. 代理对象的配置(前面已讲)
理解如何配置代理对象的属性,如切入点和通知。
这部分内容在Spring高手之路22——AOP切面类的封装与解析的第
3.2
节“TargetSource 的构建”有提到,可自行前往翻阅学习。
关键点:
- AdvisedSupport类:包含了代理配置的相关信息。
源码部分:
AdvisedSupport
(位于org.springframework.aop.framework
包中)
5. 执行代理逻辑(本章需要讲解的)
理解代理对象在运行时如何拦截方法调用并执行通知逻辑。
关键点:
- 拦截器链:代理对象通过拦截器链来执行切面逻辑。
源码部分:
- ReflectiveMethodInvocation的方法:
proceed
(位于org.springframework.aop.framework
包中)
推荐学习路径
- 从高层理解代理触发机制
- 查看
AbstractAutoProxyCreator
类,特别是wrapIfNecessary
和postProcessAfterInitialization
方法,理解代理触发的整体流程。
- 深入理解切面和通知的获取
- 查看
AbstractAdvisorAutoProxyCreator
类,特别是getAdvicesAndAdvisorsForBean
方法,理解如何获取适用于目标Bean
的切面和通知。
- 代理对象的创建细节
- 查看
ProxyFactory
类,特别是getProxy
方法,理解代理对象的具体创建过程。
- 理解动态代理的实现
- 查看
JdkDynamicAopProxy
和CglibAopProxy
类,理解JDK
动态代理和CGLIB
代理的具体实现细节。
- 配置代理对象
- 查看
AdvisedSupport
类,理解代理对象的配置和管理。
- 执行代理逻辑
- 查看
ReflectiveMethodInvocation
类,特别是proceed
方法,理解代理对象在运行时如何拦截方法调用并执行切面逻辑。
2. AOP代理的触发机制
还记得BeanPostProcessor
接口吗?这在“Spring高手之路13——BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor解析”有讲到。
BeanPostProcessor
接口提供了两个方法:
- postProcessBeforeInitialization:在
Bean
初始化之前调用。 - postProcessAfterInitialization:在
Bean
初始化之后调用。
Spring AOP
利用postProcessAfterInitialization
方法在Bean
初始化完成后,检查并决定是否需要对这个Bean
进行代理。
2.1 postProcessAfterInitialization方法源码分析
本节源码基于 spring-aop-5.3.16
。
代码提出来分析
/**
* 如果该bean被子类标识为需要代理的bean,则使用配置的拦截器创建一个代理。
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
// 如果bean不为空
if (bean != null) {
// 获取缓存键,通常是bean的类和名称的组合,用于在缓存中存取与该bean相关的元数据
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 从earlyProxyReferences集合中移除缓存键,如果当前bean不在该集合中,表示需要代理该bean
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 如果需要的话,对bean进行包装(代理)
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
// 如果bean为null或不需要代理,直接返回原始bean
return bean;
}
这个方法主要功能:
- 确定代理需求:该方法的主要目的是确定是否需要为
Bean
创建代理。如果需要,wrapIfNecessary
方法将负责实际的代理创建过程。 - 优化性能:通过缓存键和
earlyProxyReferences
集合,可以避免重复处理同一个Bean
,提高性能。 - 集成AOP功能:为需要代理的
Bean
创建代理对象,使得AOP
切面能够在Bean
的方法调用前后执行。
2.2 wrapIfNecessary方法源码分析
代码提出来分析
/**
* 如果需要,对给定的bean进行包装(代理),即如果它符合被代理的条件。
* @param bean 原始的bean实例
* @param beanName bean的名称
* @param cacheKey 用于元数据访问的缓存键
* @return 包装(代理)后的bean,或原始的bean实例
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 如果beanName不为空且targetSourcedBeans集合包含这个beanName,直接返回原始bean
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 如果advisedBeans缓存中显示这个bean不需要代理,直接返回原始bean
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 如果bean的类是基础设施类(如Spring内部使用的类)或应该跳过代理
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
// 将这个bean标记为不需要代理
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// 获取适用于这个bean的通知和切面
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 如果有通知和切面,则创建代理
if (specificInterceptors != DO_NOT_PROXY) {
// 将这个bean标记为需要代理
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 创建代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
// 缓存代理对象的类型
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
// 如果没有适用于这个bean的通知和切面,将这个bean标记为不需要代理
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
wrapIfNecessary
方法的主要功能是检查给定的Bean
是否需要进行AOP
代理,并在必要时为其创建代理对象。该方法在Bean
初始化后被调用,以确保符合条件的Bean
能够被AOP
代理,从而使AOP
切面能够在Bean
的方法调用前后执行。
2.3 时序图演示触发机制
详细分析和说明:
- Caller请求Bean实例:
Caller
调用BeanFactory
来获取Bean
实例。- 这一步表示应用程序或其他
Bean
调用者需要获取一个Bean
实例。
- BeanFactory创建Bean实例:
BeanFactory
创建Bean
实例,准备进行初始化。- 这包括通过
Spring
的依赖注入机制构造Bean
实例。
- 调用postProcessAfterInitialization:
BeanFactory
调用AOPProxy
的postProcessAfterInitialization
方法。- 这是
Spring AOP
框架用于在Bean
初始化后进行处理的关键步骤。
- 获取缓存键:
AOPProxy
获取缓存键(cacheKey
),通常是Bean
的类和名称的组合。- 缓存键用于在缓存中存取与
Bean
相关的元数据。
- 检查earlyProxyReferences集合:
AOPProxy
检查earlyProxyReferences
集合,判断当前Bean
是否已经处理过。- 这是为了避免重复处理同一个
Bean
,提高性能。
- 判断Bean是否需要代理:
- 通过检查
earlyProxyReferences
集合,决定是否需要继续代理。 - 如果
Bean
需要代理,进入wrapIfNecessary
方法。
- 调用wrapIfNecessary:
wrapIfNecessary
方法负责实际的代理创建过程。- 包括进一步的检查和代理对象的创建。
- 检查targetSourcedBeans集合:
- 判断
Bean
是否在targetSourcedBeans集合中,如果是,直接返回原始Bean
。 targetSourcedBeans
集合用于存储一些特殊的Bean
,不需要代理。
targetSourcedBeans
集合主要用于管理那些使用动态目标源的Bean
。在创建代理时,如果一个Bean
在这个集合中,Spring
会进行特殊处理,通常直接返回原始Bean
,而不是创建新的代理对象。这种机制确保了特殊Bean
的正确处理和高效运行。
- 检查advisedBeans集合:
- 检查缓存中是否已经标记了该
Bean
不需要代理,如果是,直接返回原始Bean
。 - 提高处理效率,避免重复检查。
- 检查基础设施类和跳过条件:
- 判断
Bean
是否为Spring
的基础设施类或是否有其他跳过条件。 - 基础设施类通常不需要代理,因为它们是框架内部使用的。
- 获取通知和切面:
- 调用
getAdvicesAndAdvisorsForBean
方法,获取适用于该Bean
的通知(Advice
)和切面(Advisor
)。 Advisor
对象包含了切面逻辑,Advice
对象包含了实际的通知逻辑。
- 检查是否有specificInterceptors:
- 判断是否有适用于该
Bean
的通知和切面。 - 如果有,继续创建代理对象。
- 创建代理对象:
- 调用
createProxy
方法,通过ProxyFactory
创建代理对象。 - 代理对象将拦截方法调用,并应用切面逻辑。
- 返回代理对象:
- 代理对象创建完成后,返回给
AOPProxy
。 AOPProxy
将代理对象返回给BeanFactory
。
- 返回原始
Bean
或代理对象:
- 如果
Bean
不需要代理,返回原始Bean
。 - 如果需要代理,返回代理对象。
BeanFactory
返回结果:
BeanFactory
将最终的Bean
实例(可能是原始Bean
或代理对象)返回给Caller
。
3. AOP代理逻辑的执行
3.1 AOP代理如何使用拦截器
当AOP
代理拦截方法调用时,它会依次调用配置的拦截器链。每个拦截器都通过invoke
方法处理方法调用。整个流程如下:
- 方法调用被代理拦截:当一个方法被调用时,
AOP
代理会首先拦截这个调用。 - 拦截器链处理:调用会被传递给拦截器链中的第一个拦截器。
- 执行拦截器链:每个拦截器的
invoke
方法都会执行其逻辑,并决定是否调用下一个拦截器。 - 目标方法执行:如果所有拦截器都允许,最终会调用目标方法本身。
- 返回结果或处理异常:目标方法执行完成后,结果或异常会逐个传递回拦截器链,直到返回给最初调用方法的客户端。
比如给个简单拦截器的例子:
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 在目标方法执行前
System.out.println("Before method: " + invocation.getMethod().getName());
// 调用目标方法
Object result = invocation.proceed();
// 在目标方法执行后
System.out.println("After method: " + invocation.getMethod().getName());
return result;
}
}
在这个示例中,invoke
方法是拦截器的核心。
- 当方法被调用时,
invoke
方法会先打印“方法调用前”,然后调用invocation.proceed()
执行目标方法,最后在方法执行后或发生异常时打印相应的信息。 invocation.proceed()
方法用于继续调用拦截器链中的下一个拦截器,直到最终调用目标方法。
3.2 proceed方法源码分析
代码提出来分析:
/**
* 逐个调用拦截器链中的拦截器,并在最后执行目标方法(连接点)。
* @return 调用链中最后一个拦截器或目标方法的返回值
* @throws Throwable 如果拦截器或目标方法抛出异常
*/
@Override
@Nullable
public Object proceed() throws Throwable {
// 我们从索引 -1 开始,并提前递增索引。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 如果当前拦截器索引等于拦截器链的最后一个索引,则调用目标方法
return invokeJoinpoint();
}
// 获取当前索引的拦截器或拦截建议
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 检查是否是 InterceptorAndDynamicMethodMatcher 类型
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// 如果是,评估动态方法匹配器
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
// 获取目标类的 Class 对象
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
// 检查方法是否匹配
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
// 如果匹配成功,调用拦截器的 invoke 方法
return dm.interceptor.invoke(this);
} else {
// 动态匹配失败,跳过这个拦截器并调用链中的下一个
return proceed();
}
} else {
// 如果是普通拦截器,直接调用其 invoke 方法
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
/**
* 使用反射调用连接点。
* 子类可以重写此方法以使用自定义调用。
*
* @return 连接点的返回值
* @throws Throwable 如果调用连接点导致异常
*/
@Nullable
protected Object invokeJoinpoint() throws Throwable {
// 使用反射调用目标对象的目标方法,并传递参数
return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
}
proceed
方法和invokeJoinpoint
方法共同实现了AOP
拦截器链的执行逻辑。proceed
方法依次执行拦截器链中的每个拦截器,并在链的末尾调用目标方法。invokeJoinpoint
方法通过反射调用目标方法并返回其结果。这两个方法结合起来,使得AOP
能够在目标方法执行前后插入各种横切关注点,如事务管理、日志记录等。
这里判断动态方法匹配器是干嘛的?
- 静态方法匹配器:在编译时或配置时确定方法是否匹配,例如方法名、参数类型等。这种匹配是静态的,无法根据实际调用时的参数值进行判断。
- 动态方法匹配器:在方法调用时动态评估方法匹配条件,例如根据实际传递的参数值判断是否匹配。这种匹配是动态的,可以提供更灵活的控制。
普通的AOP
通常使用静态匹配,这里不用深究动态匹配器。
3.3 时序图
时序图详细说明:
- Caller调用代理方法:
Caller
调用代理对象的方法,开始AOP
的执行过程。- 代理对象是通过
AOP
创建的,包含了拦截器链。
- 创建ReflectiveMethodInvocation实例:
- 在代理对象内部,
Spring AOP
创建一个ReflectiveMethodInvocation
实例。该实例封装了目标方法、拦截器链以及方法参数。 ReflectiveMethodInvocation
负责处理方法调用的整个过程。
- 调用proceed():
- 代理对象调用
ReflectiveMethodInvocation
的proceed()
方法,开始执行拦截器链。
- 遍历拦截器链:
ReflectiveMethodInvocation
遍历拦截器链中的每个拦截器。- 对每个拦截器,
ReflectiveMethodInvocation
调用其invoke(this)
方法。 - 拦截器在调用目标方法之前和之后执行自定义逻辑。
- 调用invokeJoinpoint():
- 如果拦截器链中没有拦截器或所有拦截器已经执行完毕,
ReflectiveMethodInvocation
直接调用invokeJoinpoint()
方法。 invokeJoinpoint()
通过反射调用目标方法。
- 调用目标方法:
ReflectiveMethodInvocation
通过反射调用实际的目标方法。- 目标方法执行并返回结果。
- 返回结果:
- 目标方法执行完成后,结果返回给
ReflectiveMethodInvocation
。 ReflectiveMethodInvocation
将结果返回给所有拦截器,依次向上返回。
- 代理对象返回最终结果:
ReflectiveMethodInvocation
最终将结果返回给代理对象。- 代理对象将结果返回给
Caller
。
欢迎一键三连~
有问题请留言,大家一起探讨学习
----------------------Talk is cheap, show me the code-----------------------