Spring循环依赖
1 循环依赖解决及概述
(1)循环依赖的问题描述和Spring解决流程
@Component
public class A{
@Autowired
private B b;
}
@Component
public class B{
@Autowired
private A a;
}
循环依赖, 形式如上.
spring按照顺序初始化Bean:
第一步创建A, 构建A的BeanFactory放置在三级缓存中,然后实例化A, 注入属性, 发现A需要B, 在一级二级三级缓存中没有发现B, 开始创建B;
第二步, 创建B, 构建B的BeanFactory放置在三级缓存中, 然后实例化, 注入属性, 发现B需要A, 于是从一级二级缓存寻找A, 无果, 在三级缓存中找到A的BeanFactory, 从中取出 A(或A的代理对象), 然后将A放置在二级缓存中, 将其从三级缓存中删除, B初始化完成, 进入到一级缓存;
第三步, A从一级缓存中获取到初始化完成的B, A也初始化完成, 进入到一级缓存, A 和 B都初始化完成.
需要注意的是:
三级缓存的目的主要是延迟生成代理对象.
正常情况下, Bean的代理对象是在初始化完成后才生成的, 然而, 如果产生了循环依赖, spring出于开发者考虑, 如果对于Bean进行了AOP代理, 那么对于这个Bean的依赖, 开发者也必然是想注入其代理对象的.
因此, 在产生循环依赖的情况下, Bean的代理对象会提前生成, 将该代理对象传递给依赖者.
亦即是说, Bean的代理对象生成可能会有两个时间点.
在第二步中, spring会进行判断, 如果目标对象被代理, 则提前生成代理对象, 否则在对象初始化完成后再生成代理对象.
产生循环依赖的情况下, 被依赖对象的初始化完成需要从三级缓存中**取出BeanFactory中的实例引用, 此时会作一个判断, **判断该实例是否被增强(AOP), 来决定是否返回代理对象(提前生成代理对象).
在不产生循环依赖的情况下,不需要从beanFactory取出, 因此不需要进行该判断, 正常情况下, 在bean初始化完成后,进行代理对象的生成.
二级缓存的目的则是解决多个Bean相互依赖的问题
@Component
public class A{
@Autowired
private B b;
@Autowired
private C c;
}
@Component
public class B{
@Autowired
private A a;
}
@Component
public class C{
@Autowired
private C c;
}
如上所示, 当B进行初始化后, A被放置在二级缓存中, 等C进行初始化的时候, 从二级缓存中取出.
参考:
Spring是怎么解决循环依赖的? - 简书 (jianshu.com)
面试必杀技,讲一讲Spring中的循环依赖 - 程序员DMZ - 博客园 (cnblogs.com)
spring + spring mvc + tomcat 面试题(史上最全) - 疯狂创客圈 - 博客园 (cnblogs.com)
2 关于循环依赖的种类
spirng中注入的方式分为:
- 基于构造器的注入
- 基于setter的注入
另外又涉及到Bean的作用域:
- 单例-singleton
- 原型-prototype
- request
- session
- globle-session
因此循环依赖可以分为三类:
- 单例状态下基于setter注入方式的循环依赖
- 单例状态下基于构造器的注入方式的循环依赖
- 多例状态下的循环依赖
对于多例状态下的循环依赖,无法解决
基于构造器注入方式的单例循环依赖,可以采用@lazy的方式,延迟加载以解决
spring本身的机制,只能解决基于setter方式注入的单例循环依赖
资料来源
Spring面试题必知必会-循环依赖概览:
https://blog.csdn.net/a745233700/article/details/80959716
Spring循环依赖详解:
https://blog.csdn.net/a745233700/article/details/110914620
标签:初始化,缓存,Spring,代理,依赖,循环 From: https://www.cnblogs.com/wang-jifeng/p/16824184.html