Spring是如何解决循环依赖问题的?(三级缓存:singletonObjects,earlySingletonObjects,singletonFactories)为什么构造器注入无法解决循环依赖?
Spring是如何解决循环依赖问题的?(三级缓存:singletonObjects,earlySingletonObjects,singletonFactories)为什么构造器注入无法解决循环依赖?
- 写回答
- 好问题 0 提建议
- 关注问题
- 邀请回答
-
2条回答 默认 最新
关注让 “紫雾凌寒” 来帮你回答,本回答结合 DeepSeek
如果还有问题,请评论或点击头像私信,如果有帮助到您, 请关注、采纳、点赞
- Spring解决循环依赖的方式(基于三级缓存)
- 关键点:- Spring通过三级缓存来解决循环依赖问题。这三级缓存分别是
singletonObjects(存放完整的单例对象)、earlySingletonObjects(存放提前暴露的单例对象)、singletonFactories(存放单例对象的工厂)。 - 解决方案:
- 当一个bean被创建时,首先会将其创建过程中的工厂对象放入
singletonFactories中。 - 然后,如果有其他bean需要依赖这个正在创建的bean,会从
singletonFactories中获取对应的工厂对象来获取一个提前暴露的单例对象(可能是一个不完整的对象)。 - 当这个bean创建完成后,会将其从
singletonFactories中移除,并放入singletonObjects中。同时,如果在创建过程中允许提前暴露单例,也会放入earlySingletonObjects中。 - 示例代码如下(简化的Spring创建bean的逻辑示意):
- Spring通过三级缓存来解决循环依赖问题。这三级缓存分别是
// 三级缓存 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(); private final Map<String, Object> singletonFactories = new ConcurrentHashMap<>(); private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(); // 创建bean的方法 protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) { // 创建bean的实例 Object bean = createBeanInstance(mbd, beanName, args); // 将创建bean的工厂对象放入singletonFactories addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); // 属性填充等后续操作 populateBean(beanName, mbd, bean); // 初始化bean bean = initializeBean(beanName, bean, mbd); // 将完整的bean放入singletonObjects addSingleton(beanName, bean); return bean; } // 添加单例工厂 protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { singletonFactories.put(beanName, singletonFactory); } // 获取提前暴露的bean引用 protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; } // 添加单例对象 protected void addSingleton(String beanName, Object singletonObject) { singletonObjects.put(beanName, singletonObject); singletonFactories.remove(beanName); earlySingletonObjects.remove(beanName); }- 三级缓存的作用:
singletonFactories用于存放创建bean的工厂对象,这样在其他bean依赖该bean时,可以通过工厂获取一个提前暴露的对象(可能是不完整的),从而避免循环依赖。earlySingletonObjects用于存放提前暴露的单例对象,在bean创建完成但还未完全初始化时可以获取到。singletonObjects用于存放完整的单例对象。
2. 构造器注入无法解决循环依赖的原因
- 关键点:
- 构造器注入是在对象创建时就完成依赖注入,而Spring解决循环依赖是基于对象创建过程中的提前暴露和引用机制。
- 原因:
- 当使用构造器注入时,对象在构造器执行时就需要完整地获取其所有依赖。如果存在循环依赖,在创建第一个依赖对象时就会因为需要第二个依赖对象(而第二个依赖对象又需要第一个依赖对象)而陷入死循环,无法完成创建。
- 例如:
@Component class A { private B b; public A(B b) { this.b = b; } } @Component class B { private A a; public B(A a) { this.a = a; } }- 在创建`A`的实例时,需要传入`B`的实例,而创建`B`的实例时又需要传入`A`的实例,这就导致无法创建出任何一个对象。- 不同解决方案的优缺点
- 基于三级缓存的解决方案:- 优点:
- 能够很好地解决Spring框架中大部分的循环依赖问题,保证了Spring容器的正常运行。
- 实现方式相对优雅,在Spring的核心逻辑中通过巧妙的设计解决了复杂的循环依赖场景。
- 缺点:
- 实现较为复杂,需要理解Spring的对象创建流程和缓存机制。
- 对于一些特殊的循环依赖场景可能需要额外的处理,比如在bean创建过程中涉及复杂的代理创建等情况。
- 无其他解决方案(针对构造器注入):
- 优点:无。
- 缺点:构造器注入本身无法解决循环依赖问题,在设计依赖关系时需要特别注意避免这种情况,否则会导致程序无法正常运行。
- 总结:
- Spring通过三级缓存singletonObjects、earlySingletonObjects、singletonFactories有效地解决了大部分循环依赖问题。在bean创建过程中,利用工厂对象提前暴露不完整的单例对象,使得依赖该bean的其他bean能够正常创建,待bean创建完成后再放入完整的缓存中。而构造器注入由于在对象创建时就要求完整的依赖,所以无法解决循环依赖问题,在设计依赖关系时需要谨慎避免。
希望以上解答对您有所帮助。如果您有任何疑问,欢迎在评论区提出。
本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报- Spring解决循环依赖的方式(基于三级缓存)