spring循环依赖和三级缓存
是什么
介绍
一个或者多个bean之间存在直接或者间接的依赖关系并构成循环调用
根本原因还是在代码的一个设计上,因为模块耦合度较高的情况下,依赖的复杂度一定会增加
关键两个问题
- 死循环创建
- apo代理
表现形态
互相依赖:A-B B-A
间接依赖
自我依赖
bean的基本创建管理过程
先实例化,再添加属性
初始化后才能使用,初始化后才会扔到单例池
循环依赖
解决
三级缓存来解决部分循环依赖的问题
三级缓存
其实就是用来存放不同类型的bean
- 第一级缓存存放的是完全初始化号的bean,这个bean可以直接被使用
- 第二级缓存存放的是原始的bean对象,也就是说bean里面的属性还没有北赋值或者依赖注入
- 第三极缓存存放的是一个bean工厂对象,用来生成原始bean对象并且放入到二级缓存中
引入:二级缓存处理
假设Bean A 和 Bean B之间存在循环依赖
处理过程:
- 实例化beanA后将未进行注入的A放到半成品池子中
- 对A进行依赖注入需要先实例化B
- 给B填充属性,去获取A的时候,判断单例池中没有,然后半成品池中有,将A(未进行属性填充)注入,然后初始化B完成
- 将B丢到单例池
- A注入B完成属性填充,完成初始化,放入单例池
- 完成A的整个初始化(关键是A一旦实例化(还未进行属性填充)就会添加到半成品池)
=》二级缓存就能解决死循环创建问题
=》但是没法解决AOP代理问题,所以需要三级缓存
在初始化的时候执行Bean的后置处理,在这里构造AOP代理对象
创建代理有两个入口
三级缓存
- 实例化A,调提前引用,将factory(a) 放入工厂池
- 给B填充A属性的时候会引用A,调用A的提前引用factory(a)创建A的动态代理,proxy$a 放到半成品池,填充到B
- B填充完属性调后置处理,创建proxy$B,放到单例池
- proxy$A属性填充时proxy$B已经有了
核心思想是把bean的实例化和bean中的属性注入过程分离
采用一级缓存来存放完整的bean实例
采用二级缓存来存储不完整的Bean实例,通过不完整的bean实例作为突破口来解决循环依赖的问题
至于第三级缓存主要是解决代理对对象的循环依赖问题
注:只能解决单实例存在的循环引用问题
且,以下四种情况需要人为干预
1.多实例的Setter注入导致的循环依赖,需要把Bean改成单例。
2.构造器注入导致的循环依赖,可以通过@Lazy注解
3.@Dependson导致的循环依赖,找到注解循环依赖的地方,迫使它不循环依赖。·
4.单例的代理对象Setter注入导致的循环依赖
- 可以使用@Lazy注解
- 或者使用@DependsOn注解指定加载先后关系
作者: deity-night
出处: https://www.cnblogs.com/deity-night/
关于作者:码农
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接 如有问题, 可邮件(***@163.com)咨询.