spring循环依赖和三级缓存

是什么

介绍

一个或者多个bean之间存在直接或者间接的依赖关系并构成循环调用

根本原因还是在代码的一个设计上,因为模块耦合度较高的情况下,依赖的复杂度一定会增加

关键两个问题

  • 死循环创建
  • apo代理

表现形态

互相依赖:A-B   B-A

 间接依赖

 自我依赖

bean的基本创建管理过程 

 

 先实例化,再添加属性

初始化后才能使用,初始化后才会扔到单例池

 循环依赖

 

解决

三级缓存来解决部分循环依赖的问题

三级缓存

其实就是用来存放不同类型的bean

  • 第一级缓存存放的是完全初始化号的bean,这个bean可以直接被使用
  • 第二级缓存存放的是原始的bean对象,也就是说bean里面的属性还没有北赋值或者依赖注入
  • 第三极缓存存放的是一个bean工厂对象,用来生成原始bean对象并且放入到二级缓存中

引入:二级缓存处理

假设Bean A 和 Bean B之间存在循环依赖

处理过程:

  1. 实例化beanA后将未进行注入的A放到半成品池子中
  2. 对A进行依赖注入需要先实例化B 
  3. 给B填充属性,去获取A的时候,判断单例池中没有,然后半成品池中有,将A(未进行属性填充)注入,然后初始化B完成
  4. 将B丢到单例池
  5. A注入B完成属性填充,完成初始化,放入单例池
  6. 完成A的整个初始化(关键是A一旦实例化(还未进行属性填充)就会添加到半成品池

=》二级缓存就能解决死循环创建问题

=》但是没法解决AOP代理问题,所以需要三级缓存

在初始化的时候执行Bean的后置处理,在这里构造AOP代理对象

 创建代理有两个入口

 三级缓存

  1. 实例化A,调提前引用,将factory(a) 放入工厂池
  2. 给B填充A属性的时候会引用A,调用A的提前引用factory(a)创建A的动态代理,proxy$a 放到半成品池,填充到B
  3. B填充完属性调后置处理,创建proxy$B,放到单例池
  4. proxy$A属性填充时proxy$B已经有了

 

 核心思想是把bean的实例化和bean中的属性注入过程分离

采用一级缓存来存放完整的bean实例

采用二级缓存来存储不完整的Bean实例,通过不完整的bean实例作为突破口来解决循环依赖的问题

至于第三级缓存主要是解决代理对对象的循环依赖问题

注:只能解决单实例存在的循环引用问题

且,以下四种情况需要人为干预

1.多实例的Setter注入导致的循环依赖,需要把Bean改成单例。

2.构造器注入导致的循环依赖,可以通过@Lazy注解

3.@Dependson导致的循环依赖,找到注解循环依赖的地方,迫使它不循环依赖。·

4.单例的代理对象Setter注入导致的循环依赖

  • 可以使用@Lazy注解
  • 或者使用@DependsOn注解指定加载先后关系

 

posted on 2023-05-19 00:52  or追梦者  阅读(568)  评论(0编辑  收藏  举报