为了全面理解Bean
是如何被AOP
代理的,我们把前面几篇文章串联一下,需要关注以下几点,并针对每个关键点学习相应的源码部分:
1. AOP代理的触发机制(本章需要讲解的)理解Spring
如何决定哪些Bean
需要被代理。
关键点:
Spring AOP
的自动代理创建器实现了这个接口,通过它在Bean
初始化的前后进行处理。源码部分:AbstractAutoProxyCreator
(位于org.springframework.aop.framework.autoproxy
包中)
postProcessAfterInitialization
wrapIfNecessary
2. 确定哪些Bean需要代理(前面已讲)理解Spring
如何确定哪些Bean
需要被代理,这通常涉及到扫描和匹配切面。
这部分内容在Spring高手之路21——深入剖析Spring AOP代理对象的创建的第
2
节“匹配增强器Advisors
(源码分析+时序图说明)”已经讲解,可自行前往翻阅学习。
关键点:
Spring AOP
中的切面和通知。源码部分:
getAdvicesAndAdvisorsForBean
(位于org.springframework.aop.framework.autoproxy
包中)3. 创建代理对象(前面已讲)理解Spring
如何创建代理对象,包括使用JDK
动态代理和CGLIB
代理。
这部分内容在Spring高手之路22——AOP切面类的封装与解析的第
3.2
节“TargetSource 的构建”有提到,可自行前往翻阅学习。
关键点:
JDK
动态代理和CGLIB
)。源码部分:
getProxy
(位于org.springframework.aop.framework
包中)invoke
(位于org.springframework.aop.framework
包中)getProxy
(位于org.springframework.aop.framework
包中)4. 代理对象的配置(前面已讲)理解如何配置代理对象的属性,如切入点和通知。
这部分内容在Spring高手之路22——AOP切面类的封装与解析的第
3.2
节“TargetSource 的构建”有提到,可自行前往翻阅学习。
关键点:
源码部分:
AdvisedSupport
(位于org.springframework.aop.framework
包中)5. 执行代理逻辑(本章需要讲解的)理解代理对象在运行时如何拦截方法调用并执行通知逻辑。
关键点:
源码部分:
proceed
(位于org.springframework.aop.framework
包中)推荐学习路径
1. 从高层理解代理触发机制
AbstractAutoProxyCreator
类,特别是wrapIfNecessary
和postProcessAfterInitialization
方法,理解代理触发的整体流程。2. 深入理解切面和通知的获取
AbstractAdvisorAutoProxyCreator
类,特别是getAdvicesAndAdvisorsForBean
方法,理解如何获取适用于目标Bean
的切面和通知。3. 代理对象的创建细节
ProxyFactory
类,特别是getProxy
方法,理解代理对象的具体创建过程。4. 理解动态代理的实现
JdkDynamicAopProxy
和CglibAopProxy
类,理解JDK
动态代理和CGLIB
代理的具体实现细节。5. 配置代理对象
AdvisedSupport
类,理解代理对象的配置和管理。6. 执行代理逻辑
ReflectiveMethodInvocation
类,特别是proceed
方法,理解代理对象在运行时如何拦截方法调用并执行切面逻辑。 还记得BeanPostProcessor
接口吗?这在“Spring高手之路13——BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor解析”有讲到。
BeanPostProcessor
接口提供了两个方法:
Bean
初始化之前调用。Bean
初始化之后调用。Spring AOP
利用postProcessAfterInitialization
方法在Bean
初始化完成后,检查并决定是否需要对这个Bean
进行代理。
本节源码基于 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
,提高性能。Bean
创建代理对象,使得AOP
切面能够在Bean
的方法调用前后执行。代码提出来分析
/**
* 如果需要,对给定的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
的方法调用前后执行。
详细分析和说明:
1. Caller请求Bean实例:
Caller
调用BeanFactory
来获取Bean
实例。Bean
调用者需要获取一个Bean
实例。2. BeanFactory创建Bean实例:
BeanFactory
创建Bean
实例,准备进行初始化。Spring
的依赖注入机制构造Bean
实例。3. 调用postProcessAfterInitialization:
BeanFactory
调用AOPProxy
的postProcessAfterInitialization
方法。Spring AOP
框架用于在Bean
初始化后进行处理的关键步骤。4. 获取缓存键:
AOPProxy
获取缓存键(cacheKey
),通常是Bean
的类和名称的组合。Bean
相关的元数据。5. 检查earlyProxyReferences集合:
AOPProxy
检查earlyProxyReferences
集合,判断当前Bean
是否已经处理过。Bean
,提高性能。6. 判断Bean是否需要代理:
earlyProxyReferences
集合,决定是否需要继续代理。Bean
需要代理,进入wrapIfNecessary
方法。7. 调用wrapIfNecessary:
wrapIfNecessary
方法负责实际的代理创建过程。8. 检查targetSourcedBeans集合:
Bean
是否在targetSourcedBeans集合中,如果是,直接返回原始Bean
。targetSourcedBeans
集合用于存储一些特殊的Bean
,不需要代理。
targetSourcedBeans
集合主要用于管理那些使用动态目标源的Bean
。在创建代理时,如果一个Bean
在这个集合中,Spring
会进行特殊处理,通常直接返回原始Bean
,而不是创建新的代理对象。这种机制确保了特殊Bean
的正确处理和高效运行。
9. 检查advisedBeans集合:
Bean
不需要代理,如果是,直接返回原始Bean
。10. 检查基础设施类和跳过条件:
Bean
是否为Spring
的基础设施类或是否有其他跳过条件。11. 获取通知和切面:
getAdvicesAndAdvisorsForBean
方法,获取适用于该Bean
的通知(Advice
)和切面(Advisor
)。Advisor
对象包含了切面逻辑,Advice
对象包含了实际的通知逻辑。12. 检查是否有specificInterceptors:
Bean
的通知和切面。13. 创建代理对象:
createProxy
方法,通过ProxyFactory
创建代理对象。14. 返回代理对象:
AOPProxy
。AOPProxy
将代理对象返回给BeanFactory
。15. 返回原始Bean
或代理对象:
Bean
不需要代理,返回原始Bean
。16. BeanFactory
返回结果:
BeanFactory
将最终的Bean
实例(可能是原始Bean
或代理对象)返回给Caller
。当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()
方法用于继续调用拦截器链中的下一个拦截器,直到最终调用目标方法。代码提出来分析:
/**
* 逐个调用拦截器链中的拦截器,并在最后执行目标方法(连接点)。
* @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
通常使用静态匹配,这里不用深究动态匹配器。
时序图详细说明:
1. Caller调用代理方法:
Caller
调用代理对象的方法,开始AOP
的执行过程。AOP
创建的,包含了拦截器链。2. 创建ReflectiveMethodInvocation实例:
Spring AOP
创建一个ReflectiveMethodInvocation
实例。该实例封装了目标方法、拦截器链以及方法参数。ReflectiveMethodInvocation
负责处理方法调用的整个过程。3. 调用proceed():
ReflectiveMethodInvocation
的proceed()
方法,开始执行拦截器链。4. 遍历拦截器链:
ReflectiveMethodInvocation
遍历拦截器链中的每个拦截器。ReflectiveMethodInvocation
调用其invoke(this)
方法。5. 调用invokeJoinpoint():
ReflectiveMethodInvocation
直接调用invokeJoinpoint()
方法。invokeJoinpoint()
通过反射调用目标方法。6. 调用目标方法:
ReflectiveMethodInvocation
通过反射调用实际的目标方法。7. 返回结果:
ReflectiveMethodInvocation
。ReflectiveMethodInvocation
将结果返回给所有拦截器,依次向上返回。8. 代理对象返回最终结果:
ReflectiveMethodInvocation
最终将结果返回给代理对象。Caller
。欢迎一键三连~
有问题请留言,大家一起探讨学习
----------------------Talk is cheap, show me the code-----------------------
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。