@Async 导致的循环依赖

这个问题发生的本质其实就是spring的循环依赖的问题,解决的方法也就是加了个@lazy ,因为现在开始求甚解了,于是就花力气来查看@lazy解决问题的原理

起因:在项目中为了提高接口的响应速度,使用了@Async 注解来进行异步操作,但是在项目启动过程中就出现了循环依赖的问题

当时觉得很奇怪,因为一开始项目启动是没有任何问题的,然后加了这个注解就出现了循环依赖的问题,这个就和我之前遇到的循环依赖问题出现的场景不一样,我之前遇到的都是A 类里面依赖B,B类里面遇到A 这种很显眼的场景出现的问题。我当时想到的就是好好的代码怎么加了个注解就出现这个问题了,要是有循环依赖的问题在更早的时候就应该暴露出来了才对。

于是顺着出错的代码进行查看,首先感到惊讶的就是

我平时的习惯上 service层的注入用的是 @Autowired ,但是他这里面就完全没有用到 任何的注入方式

其次就是我第一次见到在service层上用@AllArgsConstructor 这个注解

image-20231008225034656

然后我在网上查了一下,因为spring有个特性,是会找到第一个构造函数,然后会默认使用第一个构造函数来进行属性的注入,也就是说通过了AllArgsConstructor 注解之后,就不需要针对每个字段进行@Autowired 注入了

当时我以为觉得是这个的问题,因为我记得spring 对于构造函数的方式进行注入的话 ,是无法解决循环依赖的问题的,因为普通的解决循环依赖的方式是通过对象进行实例化的时候放到三级缓存里面,然后再针对它的属性注入的时候,生成他的属性的实例化对象,然后先完成他的实例化了之后才返回回来进行初始化,但是因为这边是构造函数,所以一开始就卡在了实例化上,
于是把他的构造函数形式的注入都改成了 Autowired,但是当我改到一半的时候也反应过来了,如果是因为这个原因的话 项目在更早的时候就报错了,不会等到今天才发现这个问题。

然后我这个时候怀疑到了是不是因为@Async 注解的缘故,因为他这个注解是用到了aop的, 我印象中针对aop 的实例化是需要进行额外的操作的,但是我后来又发现之前的代码里同样使用了 @Transactional 注解,这个事务注解 也是通过aop 这样就意味着不应该是aop的问题


确实是一直也找不到问题发生的原因,但是解决方法其实还是老一套的还是通过新增@Lazy 注解的

这个问题我一直也想不出原因,后来查了下相关的资料,发现@Async 产生的代理和普通的 AOP 代理(@Transactional)还是有差别的。
@Async 在通过 BeanPostProcessor 生成代理的时候,没有实现一个方法,这就导致了原本spring 设定中需要引用的是 bean 的早起引用的时候,结果获取到的是 bean 的原始对象的引用,导致了后续bean进行比较的时候,发现2个引用不一样,最后导致了检查报错
然后再加入@Lazy 注解了之后 假设 A 先加载,在创建 A 的实例时,会触发依赖属性 B 的加载,在加载 B 时发现它是一个被 @Lazy 标记过的属性。
那么,就不会去直接加载 B,而是产生了一个代理对象注入到了 A 中,这样 A 就能正常的初始化完成放入一级缓存了。
自然,B 加载时,再去注入 A 就能直接从一级缓存中获取到 A,这样 B 也能正常初始化完成了。
@Lazy 的本质就是将注入的依赖变成了一个代理对象。使用 @Lazy 时,不会触发依赖 bean 的加载。

参考资料:

Spring Boot 中使用 @Async 注解导致循环依赖的原因及解决方案_@enableasync 导致 对象注入重复-CSDN博客

【Spring源码三千问】@Lazy原理分析——它为什么可以解决特殊的循环依赖问题?_@lazy为什么能解决循环依赖-CSDN博客

posted @   冷扑星  阅读(334)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示