归志宁无五亩园,读书本意在元元。这篇文章主要讲述#yyds干货盘点# Spring 源码三千问同样是AOP代理bean,为什么@Async标记的bean循环依赖时会报错?相关的知识,希望能为你提供帮助。
@TOC
前言前面在分析 哪些循环依赖问题Spring解决不了 时,我们讲过,AOP 代理 bean 在被循环依赖时分两种情况:
- 普通的 AOP 代理 bean 被循环依赖,Spring 是支持的
- @Async 产生的 AOP 代理 bean 被循环依赖时,Spring 是不支持的,需要通过添加 @Lazy 来解决
版本约定【#yyds干货盘点# Spring 源码三千问同样是AOP代理bean,为什么@Async标记的bean循环依赖时会报错()】Spring 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)
正文先回顾一下 Spring 是如何解决循环依赖的: Spring 是通过三级缓存来解决循环依赖的问题的。
Spring 解决循环依赖核心原理是:
当 beanX 被循环依赖时,这时一级缓存中还没有 beanX,就会通过 beanX 对应的三级缓存
Map&
lt;
String, ObjectFactory&
lt;
?&
gt;
&
gt;
singletonFactories
来获取 bean 的早期引用。获取 bean 的早期引用的代码实现如下:
// AbstractAutowireCapableBeanFactory#getEarlyBeanReference()
/**
* Obtain a reference for early access to the specified bean, typically for the purpose of resolving a circular reference.
* 获取指定 bean 的早期引用,通常用于解析循环引用。
*/
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean)
Object exposedObject = bean;
if (!mbd.isSynthetic() &
&
hasInstantiationAwareBeanPostProcessors())
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware)
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
return exposedObject;
可以看出,通过三级缓存
Map&
lt;
String, ObjectFactory&
lt;
?&
gt;
&
gt;
singletonFactories
来获取 bean 的早期引用时,如果当前容器中有 SmartInstantiationAwareBeanPostProcessor
,那么就通过 SmartInstantiationAwareBeanPostProcessor
来获取 bean 的早期引用。SmartInstantiationAwareBeanPostProcessor
SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference
只被 AbstractAutoProxyCreator#getEarlyBeanReference
实现了。代码如下:
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference
public Object getEarlyBeanReference(Object bean, String beanName)
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
// 是否需要创建代理类
return wrapIfNecessary(bean, beanName, cacheKey);
可以看出它会调用
AbstractAutoProxyCreator#wrapIfNecessary()
,也就是 Spring 创建 AOP 代理类时调用的方法。也就是说,
getEarlyBeanReference()
会提前调用 AbstractAutoProxyCreator#wrapIfNecessary()
来生成 AOP 代理类。这样的话,当普通的 AOP 代理 bean 被循环依赖时,就能被正确的注入属性引用。
非 SmartInstantiationAwareBeanPostProcessor 生产代理的情况Spring 产生 AOP 代理是在 bean 创建的第三步
initializeBean
的时候,通过 BeanPostProcessor#postProcessAfterInitialization
来产生 AOP 代理类的。在 Spring 中其实是有两种方式产生 AOP 代理的:
- 通过 AbstractAdvisorAutoProxyCreator --> 即: AbstractAutoProxyCreator#wrapIfNecessary()
- 通过 AbstractAdvisingBeanPostProcessor
而 AbstractAdvisingBeanPostProcessor 是没有实现 SmartInstantiationAwareBeanPostProcessor 的。
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware
......public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor
.......
通过 AbstractAdvisingBeanPostProcessor 产生的 AOP 代理 bean 被循环依赖时,通过三级缓存
Map&
lt;
String, ObjectFactory&
lt;
?&
gt;
&
gt;
singletonFactories
来获取 bean 的早期引用时,就不会提前创建 AOP 代理 bean,也就是拿不到最终暴露到 Spring 容器中的 AOP 代理 bean 的早期引用,这样就会导致这种 AOP 代理 bean 循环依赖注入时的引用不正确。这种情况是不被允许的,Spring 在 initializeBean 之后,做了 check,检验二级缓存中的 bean 与最终暴露到 Spring 容器中的 bean 是否是相同的,如果不同,就会报错。
文章图片
而 @Async、@Valid 都是使用 AbstractAdvisingBeanPostProcessor 来产生 AOP 代理的。
综上,@Async、@Valid 产生的 AOP 代理 bean 被循环依赖时,会导致 Spring 容器启动异常,是不支持这种循环依赖的。
小结在 Spring 中有两种方式产生 AOP 代理:
- 通过 AbstractAdvisorAutoProxyCreator,即: AbstractAutoProxyCreator#wrapIfNecessary()
被用户自定义的 @Aspect 拦截产生的 AOP 代理 bean,都是走这种方式 - 通过 AbstractAdvisingBeanPostProcessor
@Async、@Valid 产生的 AOP 代理 bean 是走这种方式
Spring 不支持 @Async、@Valid 产生的 AOP 代理 bean 被循环依赖,需要通过添加 @Lazy 来解决
如果本文对你有所帮助,欢迎点赞收藏!
有关 Spring 源码方面的问题欢迎留言一起交流...
公众号后台回复:下载IoC 或者 下载AOP 可以免费下载源码测试工程…
阅读更多文章,请关注公众号: 老王学源码
文章图片
博主好课推荐:
课程 | 地址 |
---|---|
Dubbo源码解读——通向高手之路 | https://edu.51cto.com/sd/2e565 |
正则表达式基础与提升 | https://edu.51cto.com/sd/59587 |
推荐阅读
- # yyds干货盘点 # 手把手教你抖音系列视频批量下载器开发
- MySQL 数据库SQL 语句的高阶运用
- #yyds干货盘点#nmap(网络探测工具和安全/端口扫描器)
- 矿用防爆LED显示屏(单色,双色,全彩)T:l35-627l-8Oll
- gitlab安装备份与还原
- 马哥N63第六周作业
- 操作系统学习
- C/C++气象数据中心实战,手把手教你做工业级项目
- #yyds干货盘点#k8s中的核心组件