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

  • 20
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Java Spring AOP (Aspect Oriented Programming) 是一种编程范式,它通过将应用的业务逻辑和系统关注点(如日志、事务管理等)分离,提高了代码的可维护性和复用性。下面是一个简单的Spring AOP切面(Aspect)的Demo示例: 首先,你需要在Spring配置文件启用AOP支持,并定义一个切面(Aspect): ```xml <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aspectj="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 启用AOP --> <aop:aspectj-autoproxy/> <!-- 定义切面 --> <bean id="myAspect" class="com.example.MyAspect"> <!-- 配置通知(advice) --> <property name="beforeAdvice" ref="beforeAdvice"/> </bean> <!-- 定义通知 --> <bean id="beforeAdvice" class="com.example.BeforeAdvice"/> </beans> ``` 然后,创建一个`MyAspect`切面类,通常包含通知(advice),例如前置通知(BeforeAdvice): ```java import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class MyAspect { @Before("execution(* com.example.service.*.*(..))") public void beforeAdvice(JoinPoint joinPoint) { // 在方法执行前添加的操作,如日志记录 System.out.println("Method " + joinPoint.getSignature() + " is about to execute."); } } ``` 在这个例子,`@Before`注解定义了一个前置通知,它将在`com.example.service`包下的所有方法执行前执行。 接下来,创建`BeforeAdvice`类,这是一个具体的通知实现: ```java public class BeforeAdvice { // 可能包含一些自定义逻辑,比如参数检查或资源获取 } ``` 相关问题--: 1. Spring AOP的通知有哪些类型? 2. `@Aspect`注解在Spring AOP的作用是什么? 3. 如何在Spring配置切点(execution表达式)?
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值