聊一聊在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注解不支持循环依赖的原因!!!
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值