Spring的循环依赖
转载:https://www.cnblogs.com/zzq6032010/p/11406405.html
总结:
循环依赖可以分为二种:
1.构造器的循环依赖
在二个service的实现类中互相注入,互为构造器的参数,这种情况无法解决,因为JVM虚拟机在对类进行实例化的时候,需先实例化构造器的参数,而由于循环引用这个参数无法提前实例化。
2.属性的循环依赖
在二个service的实现类中互相注入,但是没有互为构造器的参数,这种情况是可以正常实例化的,但是如果加上了@scope=prototype的话就不能正常实例化了,默认是单例,是可以正常实例化了,实例化就类似于new 一个对象的过程。
参考:https://www.jianshu.com/p/8bb67ca11831,如果想要详细地去了解的话,可以去原文章处了解。
再做进一步总结:
1、构造器注入和prototype类型的field注入发生循环依赖时都无法初始化
2、属性注入单例的bean时,尽管有循环依赖,但bean仍然可以被成功初始化
面试官可能会提的问题:
- 单例的属性注入bean是如何解决循环依赖问题呢?如果A中注入了B,那么他们初始化的顺序是什么样子的?
- 为什么prototype类型的和构造器类型的Spring无法解决循环依赖呢?
对于第一个问题:
我们假设循环注入是:A依赖B(A中autowire了B),B又依赖A(B中又autowire了A),那么它的的指向顺序是这样的
解决循环依赖的关键方法,getSingleton,addSingletonFactory
第一个方法getSingleton会从singletonFactories里面拿Singleton,而addSingletonFactory会把Singleton放入singletonFactories
总结起来就是:
Spring在InstantiateBean时执行构造器方法,构造出实例,如果是单例的话,会将它放入一个singletonBeanFactory的缓存中,再进行populateBean方法,设置属性。通过一个singletonBeanFactory的缓存解决了循环依赖的问题
其实就是利用三级缓存来解决问题的,也就是singletonBean的生产工厂,在DefaultListableBeanFactory类中的三个map,
一级缓存:
/** 保存所有的singletonBean的实例 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);
二级缓存:
/** 保存所有早期创建的Bean对象,这个Bean还没有完成依赖注入 */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
三级缓存:
/** singletonBean的生产工厂*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
如果A注入了B,它们的初始化顺序为:
单例的设值注入,如果A中注入了B,B应该是A中的一个属性,那么猜想应该是A已经被instantiate(实例化)之后,在populateBean(填充A中的属性)时,对B进行初始化
原因是:
因为A中构造器注入了B,那么A在关键的方法addSingletonFactory()之前就去初始化了B,导致三级缓存中根本没有A,所以会发生死循环,Spring发现之后就抛出异常了。至于Spring是如何发现异常的呢,本质上是根据Bean的状态给Bean进行mark,如果递归调用时发现bean当时正在创建中,那么久抛出循环依赖的异常即可。
对于第二个问题:
需要先了解prototype的Bean是如何初始化的。prototypeBean有一个关键的属性,是被final修饰的ThreadLocal,
保存着正在创建的prototype的beanName,在流程上并没有暴露任何factory之类的缓存。并且在beforePrototypeCreation(String beanName)方法时,把每个正在创建的prototype的BeanName放入一个set中,并且会循环依赖时检查beanName是否处于创建状态,如果是就抛出异常,因为无论是构造注入还是设值注入,第二次进入同一个Bean的getBean方法是,一定会在校验部分抛出异常,因此不能完成注入,也就不能实现循环引用。
推荐阅读
-
Spring的循环依赖
-
Spring 单例Bean依赖多例Bean时,多例Bean始终是同一个 博客分类: 常见问题 常见问题
-
Acwing 10. 有依赖的背包问题(01背包 依赖性
-
proctected属性和方法的包依赖性
-
pom依赖各节点说明_节点模块中的对等依赖性是什么?
-
用AOP实现业务service的重新调用(二) 博客分类: spring springaopinterceptorStaleConnectionExceptionSQL0901
-
struts和spring的整合方式对比 博客分类: Java编程springstruts strutsspringjavawebxml
-
IntelliJ IDEA - 如何查看 Maven 项目中所有 Jar 包的依赖关系图?
-
解决spring boot应用以docker容器方式启动后,进程ID是1而导致的jstack和jmap等命令不可用的问题 博客分类: dockersprng boot docker spring-boot
-
解决spring boot应用以docker容器方式启动后,进程ID是1而导致的jstack和jmap等命令不可用的问题 博客分类: dockersprng boot docker spring-boot