Spring循环依赖
1. 为什么会出现循环依赖
AServiceProxy.target = 普通对象
AService Bean的生命周期:
-
creatingSet<'AService'>
-
推断构造方法,进行实例化
-
普通对象 --> 放入三级缓存 singletonFactories.put("AService", 函数式接口() -> get(beanName, mbd, bean))
-
依赖注入 --> 先ByType再ByName 之后 填充其他属性
BService的Bean的生命周期:- 推断构造方法,产生一个普通对象
- 依赖注入... 填充aService-->单例池 没找到--> xxxmap.get("AService") 好像找到了 创建AService:
进入循环依赖了。
单例池-->> creatingSet --> AService 出现循环依赖 -->
earlySingtonObjects -->singltonFactories-->
lambda,执行lambda表达式, (判断需要执行AOP,不需要则产生普通对象)执行AOP 产生代理对象
--> 从三级缓存中移除 函数对象
->>放入二级缓存earlySingtonObjects
-
初始化之前
-
初始化
// 6. 初始化之后(AOP) --> 产生一个AServiceProxy, 最后放入单例池的是代理对象, 出现问题了 -
初始化之后
如果发送了循环依赖,且存在AOP,从二级缓存池中取出
earlySingletonObjects.get("AService"),拿到代理对象
代理对象是需要包含target:普通对象的,这里从三级缓存中去拿到代理对象的主要作用:去执行你定义好的切面逻辑,执行完以后,再使用target去执行普通逻辑
-
放入单例Bean池
-
creatingSet.remove('AService')
那么我们把AOP放到前面去做!!!
- 推断构造方法,进行实例化
- 普通对象 --> AOP -->AServiceProxy 代理对象 --> xxxmap
但是并不是所有的对象都需要在第一步去进行AOP的
2. 如何打破循环依赖 以及提前AOP
- 第一级缓存: singletonObjects
保存了经过完整生命周期的Bean对象 - 第二级缓存: earlySingletonObjects
出现了循环依赖,去保存一些提前产生的代理对象( 并没有经过完整的生命周期的)
并且这个对象内部的普通对象也是没有经过完整的生命周期的
为什么要保存这个对象? 因为要保持单例 - 第三级缓存: singletonFactories
要打破循环,出现循环依赖的时候,要提前产生AOP,但是AOP是需要原来的bean的所以有三级缓存
进行AOP的时候需要拿到普通对象
如果一个对象出现了循环依赖,那么它就需要提前进行AOP
实例化-->AService普通对象 -- > 循环依赖 ? AOP --> AService代理对象 --> xxxMap.put("AService", AService 代理对象)
但是开始不好去判断,是否出现了循环依赖啊.
AService的Bean的生命周期:
- creatingSet<'AService'>
- 实例化 --> AService 普通对象
- 依赖注入,填充 bService --> 单例池没有 --> 创建BService
BService的Bean的生命周期
0. creatingSet<'BService'>- 实例化-->普通对象
- 填充aService--> 单例池是否存在 -->
- 判断creatingSet.contains(AService) -> 代表了AService出现了循环依赖-->
- (先在earlySingletonObjects中去找)AOP--> AService代理对象, 然后赋值给aService属性
- 这里不能把AService代理对象放入单例池中,因为A的代理对象是需要A的普通对象的,作为target
并且这里的A的代理对象也没有经过完整的生命周期
- 填充其他属性
- 做一些其他的事情(AOP)
- 添加到单例池
- 依赖注入,填充 bService --> 单例池没有 --> 创建BService
BService的Bean的生命周期
0. creatingSet<'BService'>- 实例化-->普通对象
- 填充aService--> 单例池是否存在 -->
- 判断creatingSet.contains(AService) -> 代表了AService出现了循环依赖-->
- (先在earlySingletonObjects中去找) AOP--> AService代理对象 所以我们需要拿到这个代理对象,单例的,应该对应的是同一个单例
这里放入了earlySingletonObjects缓存池中 (二级缓存)
, 然后赋值给aService属性 - 这里不能把AService代理对象放入单例池中,因为A的代理对象是需要A的普通对象的,作为target
并且这里的A的代理对象也没有经过完整的生命周期
- 填充其他属性
- 做一些其他事情AOP
- 添加到单例池
如果提前执行了lambda表达式,那么后面就不需要再进行AOP了
3. @Async 和@Lazy
Async底层不是AOP, 加@Async 会报错。
加@Lazy能够 -> 直接创建一个和Lazy相关的代理对象。
在创建AService对象的时候,先不创建BService的bean对象,而是直接创建一个BServiceProxy对象,然后赋值给B。
再bService真正执行这个方法的时候,再去拿。
然后创建BService的时候,如果他的属性需要AService,然后发现AService是有的,那么就没有发生循环依赖问题。
public class AService {
@Autowired
@Lazy
private BService bService;
@Async
public void test() {
System.out.println(bService.toString());
}
}
4. 构造方法产生的循环依赖问题
只能加上@Lazy进行解决。真正去使用bService属性的时候,因为是代理对象,所以回去找到真正对应的bService的bean。
参考: https://www.bilibili.com/video/BV1tR4y1F75R?p=27&spm_id_from=pageDriver
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义