Spring循环依赖

1. 为什么会出现循环依赖

AServiceProxy.target = 普通对象

AService Bean的生命周期:

  1. creatingSet<'AService'>

  2. 推断构造方法,进行实例化

  3. 普通对象 --> 放入三级缓存 singletonFactories.put("AService", 函数式接口() -> get(beanName, mbd, bean))

  4. 依赖注入 --> 先ByType再ByName 之后 填充其他属性
    BService的Bean的生命周期:

    1. 推断构造方法,产生一个普通对象
    2. 依赖注入... 填充aService-->单例池 没找到--> xxxmap.get("AService") 好像找到了 创建AService:
      进入循环依赖了。
      单例池-->> creatingSet --> AService 出现循环依赖 -->
      earlySingtonObjects -->singltonFactories-->
      lambda,执行lambda表达式, (判断需要执行AOP,不需要则产生普通对象)执行AOP 产生代理对象
      --> 从三级缓存中移除 函数对象
      ->>放入二级缓存earlySingtonObjects
  5. 初始化之前

  6. 初始化
    // 6. 初始化之后(AOP) --> 产生一个AServiceProxy, 最后放入单例池的是代理对象, 出现问题了

  7. 初始化之后
    如果发送了循环依赖,且存在AOP,从二级缓存池中取出
    earlySingletonObjects.get("AService"),拿到代理对象
    代理对象是需要包含target:普通对象的,这里从三级缓存中去拿到

    代理对象的主要作用:去执行你定义好的切面逻辑,执行完以后,再使用target去执行普通逻辑

  8. 放入单例Bean池

  9. creatingSet.remove('AService')


那么我们把AOP放到前面去做!!!

  1. 推断构造方法,进行实例化
  2. 普通对象 --> AOP -->AServiceProxy 代理对象 --> xxxmap
    但是并不是所有的对象都需要在第一步去进行AOP的

2. 如何打破循环依赖 以及提前AOP

  1. 第一级缓存: singletonObjects
    保存了经过完整生命周期的Bean对象
  2. 第二级缓存: earlySingletonObjects
    出现了循环依赖,去保存一些提前产生的代理对象( 并没有经过完整的生命周期的)
    并且这个对象内部的普通对象也是没有经过完整的生命周期的
    为什么要保存这个对象? 因为要保持单例
  3. 第三级缓存: singletonFactories
    要打破循环,出现循环依赖的时候,要提前产生AOP,但是AOP是需要原来的bean的所以有三级缓存
    进行AOP的时候需要拿到普通对象

如果一个对象出现了循环依赖,那么它就需要提前进行AOP

实例化-->AService普通对象 -- > 循环依赖 ? AOP --> AService代理对象 --> xxxMap.put("AService", AService 代理对象)

但是开始不好去判断,是否出现了循环依赖啊.

AService的Bean的生命周期:

  1. creatingSet<'AService'>
  2. 实例化 --> AService 普通对象
  3. 依赖注入,填充 bService --> 单例池没有 --> 创建BService
    BService的Bean的生命周期
    0. creatingSet<'BService'>
    1. 实例化-->普通对象
    2. 填充aService--> 单例池是否存在 -->
      1. 判断creatingSet.contains(AService) -> 代表了AService出现了循环依赖-->
      2. (先在earlySingletonObjects中去找)AOP--> AService代理对象, 然后赋值给aService属性
      3. 这里不能把AService代理对象放入单例池中,因为A的代理对象是需要A的普通对象的,作为target
        并且这里的A的代理对象也没有经过完整的生命周期
    3. 填充其他属性
    4. 做一些其他的事情(AOP)
    5. 添加到单例池
  4. 依赖注入,填充 bService --> 单例池没有 --> 创建BService
    BService的Bean的生命周期
    0. creatingSet<'BService'>
    1. 实例化-->普通对象
    2. 填充aService--> 单例池是否存在 -->
      1. 判断creatingSet.contains(AService) -> 代表了AService出现了循环依赖-->
      2. (先在earlySingletonObjects中去找) AOP--> AService代理对象 所以我们需要拿到这个代理对象,单例的,应该对应的是同一个单例
        这里放入了earlySingletonObjects缓存池中 (二级缓存)
        , 然后赋值给aService属性
      3. 这里不能把AService代理对象放入单例池中,因为A的代理对象是需要A的普通对象的,作为target
        并且这里的A的代理对象也没有经过完整的生命周期
    3. 填充其他属性
    4. 做一些其他事情AOP
    5. 添加到单例池

如果提前执行了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

posted @   CrazyShanShan  阅读(58)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示