聊一聊在Spring中AOP切面的增强Bean工作原理
结合上一篇【@Async对Bean增强的工作原理】文章的学习,我在第一次学习的时候内心也是产生了一个疑问,那就是同样是通过注解的形式对Bean进行增强,同样是在循环依赖的前提下,AOP切面为什么可以恰好的避开呢? 而@Async为什么会避开,童鞋们可结合上一篇找到答案(@Async对Bean增强的工作原理),接下来本文主要结合AOP切面对Bean增强的原理展开论述。
1、揭秘AOP切面
首先通过如下方式,@EnableAspectJAutoProxy注解即可全局开启AOP增强注解
/*
* 通过@Import注入核心处理类
*/
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
1.1 BeanPostProcessor中针对AOP的核心处理类
核心实现类:AnnotationAwareAspectJAutoProxyCreator
1.2 AnnotationAwareAspectJAutoProxyCreator类图
1.3 三级缓存getObject核心逻辑getEarlyBeanReference()
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
// 其中BeanPostProcessor 是 SmartInstantiationAwareBeanPostProcessor 的实现类时 才会对其Bean进行增强处理,
// 即通过getEarlyBeanReference()获取到增强后的代理Bean
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
---------------------------------------------------
/**
* 用于创建代理增强后的Bean
*/
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 临时缓存用于存储原始Bean对象(键值对存储)
// 重点重点:此处用于BeanPostProcessor的postProcessAfterInitialization()增强时进行判断
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
--------------------------------------------------
/**
* 用于BeanPostProcessor初始化对Bean进行增强处理
*/
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 此处结合上边的对比,this.earlyProxyReferences.remove(cacheKey)获得的Bean 等于 原bean
// 所以当前Bean未被二次增强 相当于是同一个Bean,
// exposedObject === bean 所以未爆出Bean创建异常原因
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
// 对Bean进行增强处理
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
结合Spring中上述三处代码的运行逻辑,这下我们就清楚了,原来AOP切面在第一次Bean的增强已经完成了,并且将该Bean存储到临时容器this.earlyProxyReferences中,同时为了避免同一Bean对象被二次增强,特此结合了this.earlyProxyReferences.remove(cacheKey) != bean时,才会更进一步来处理bean对象,至于增强Bean的判断可结合 (Spring循环依赖工作原理) 得到想要的答案。
1.4 倘若在getEarlyBeanReference()不进行对Bean进行增强会怎样?
通过 (Spring循环依赖工作原理) 我们分析得到,通过getEarlyBeanReference()方法得到的Bean对象,会直接添加至二级缓存,并移除三级缓存,注意:此刻得到的Bean会直接注入到其他对象中,假如得到的Bean不是增强后的代理对象,在存在AOP切面的前提下,那么被注入到其他对象的Bean(相当于是原Bean对象,即没有被增强,调用的时候也是原始Bean的调用,而不是增强Bean之后的调用)。
2、Debug调试验证
/**
* @author Mr.Gao
* @date 2024/4/7 13:41
* @apiNote:
*/
@Service("c")
public class ServiceC {
@Autowired
private ServiceC serviceC;
@Transactional(rollbackFor = Exception.class)
public void methodC() {
System.out.println("方法ServiceC执行了...");
System.out.println(serviceC);
System.out.println("方法B执行了...");
}
}
2.1 得到增强后的Bean
2.2 initializeBean(增强Bean)
this.earlyProxyReferences存储的Bean和原始对象bean相等,因此不会执行二次增强处理。
3、小结
3.1 AOP切面除了支持自定义切面外,默认支持的注解如下
- @Transactional 注解
- @Repository (PersistenceExceptionTranslationPostProcessor)
综上所述分析,我们可以清楚的知道AOP切面对Bean对象增强处理的处理逻辑,同样也明白了,为什么AOP切面支持循环依赖,而@Async注解不支持循环依赖的原因!!!