发布于2024-11-02 12:03 阅读(509) 评论(0) 点赞(29) 收藏(0)
优质博文:IT-BLOG-CN
在Spring
框架中,循环依赖Circular Dependency
是指两个或多个Bean
相互依赖,形成一个循环引用。例如,Bean A
依赖于Bean B
,而Bean B
又依赖于Bean A
。这种情况可能会导致Spring
容器在创建Bean
时出现问题。如下:
public class A {
private final B b;
public A(B b) {
this.b = b;
}
}
public class B {
private final A a;
public B(A a) {
this.a = a;
}
}
启动时会出现如下错误:
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'A': Requested bean is currently in creation: Is there an unresolvable circular reference?
【1】构造器注入: 构造器注入不支持循环依赖,因为在Spring
容器尝试创建Bean
时,它需要立即解析所有的构造函数参数,这会导致循环依赖问题。因此,避免使用构造器注入来解决循环依赖。@Lazy
注解可以延迟Bean
的初始化,使得Spring
在构造器注入时也能解决循环依赖问题。具体来说,@Lazy
会告诉Spring
在第一次使用Bean
时才进行初始化,而不是在容器启动时立即初始化。
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; @Component public class A { private final B b; @Autowired public A(@Lazy B b) { this.b = b; } } @Component public class B { private final A a; @Autowired public B(@Lazy A a) { this.a = a; } }
【2】Setter
注入: Setter
注入可以解决循环依赖问题,因为Spring
容器可以首先创建Bean
的实例,然后再注入依赖。
public class A {
private B b;
public void setB(B b) {
this.b = b;
}
}
public class B {
private A a;
public void setA(A a) {
this.a = a;
}
}
【3】@Autowired
注解: 使用@Autowired
注解进行Setter
注入或者字段注入,也可以解决循环依赖问题。
public class A {
@Autowired
private B b;
}
public class B {
@Autowired
private A a;
}
【4】@Lazy
注解: 使用@Lazy
注解可以延迟Bean
的初始化,从而解决循环依赖问题。
public class A {
@Autowired
@Lazy
private B b;
}
public class B {
@Autowired
@Lazy
private A a;
}
【5】使用ObjectFactory
或Provider
: 使用ObjectFactory
或Provider
可以在需要时才获取Bean
实例,从而解决循环依赖问题。
public class A { @Autowired private ObjectFactory<B> bFactory; public void someMethod() { B b = bFactory.getObject(); // 使用B } } public class B { @Autowired private ObjectFactory<A> aFactory; public void someMethod() { A a = aFactory.getObject(); // 使用A } }
【6】配置allow-circular-references: true
: 用于控制是否允许Bean
之间的循环依赖。true
:允许Bean
之间存在循环依赖。Spring
容器会尝试通过创建Bean
的代理对象来解决循环依赖问题。这是默认行为。但从设计和架构的角度来看,尽量避免循环依赖是更好的做法。
spring:
main:
allow-circular-references: true
首先,我们要知道Spring
在创建Bean
的时候默认是按照自然排序进行创建的,所以第一步Spring
会去创建A
。
Spring
创建Bean
的过程中分为三步:
1、实例化: 对应方法AbstractAutowireCapableBeanFactory
中的createBeanInstance
方法,简单理解就是new
了一个对象。
2、属性注入: 对应方法AbstractAutowireCapableBeanFactory
的populateBean
方法,为实例化中new
出来的对象填充属性。
3、初始化: 对应方法AbstractAutowireCapableBeanFactory
的initializeBean
,执行aware
接口中的方法,初始化方法,完成AOP
代理。
Spring
是如何解决循环依赖问题的:三级缓存
三级缓存的组成
一级缓存singletonObjects
: 存储已经完全初始化的单例Bean
。类型ConcurrentHashMap<String, Object>
二级缓存earlySingletonObjects
: 多了一个early
,表示缓存的是早期的bean
对象。早期是什么意思?表示Bean
的生命周期还没走完就把这个Bean
放入earlySingletonObjects
,通常是为了避免循环依赖。类型ConcurrentHashMap<String, Object>
三级缓存singletonFactories
: 存储创建Bean
的工厂ObjectFactory
,用于解决循环依赖。类型:ConcurrentHashMap<String, ObjectFactory<?>>
/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);
创建Bean
实例: 当Spring
容器创建一个Bean
时,首先会尝试从一级缓存singletonObjects
中获取该Bean
。如果获取不到,再尝试从二级缓存earlySingletonObjects
中获取。如果仍然获取不到,再尝试从三级缓存singletonFactories
中获取。
protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }
提前曝光Bean
: 在创建Bean
的过程中,如果检测到循环依赖,Spring
会提前将该Bean
的一个早期引用(通常是通过ObjectFactory
创建的代理对象)放入三级缓存singletonFactories
中。
解决循环依赖: 当另一个Bean
需要依赖这个尚未完全初始化的Bean
时,会从三级缓存singletonFactories
中获取该Bean
的早期引用,并将其放入二级缓存earlySingletonObjects
中。
完成初始化: 一旦Bean
完全初始化完成,Spring
会将其从二级缓存earlySingletonObjects
中移除,并放入一级缓存singletonObjects
中。
案例:如上有两个类A
和B
,它们通过构造器注入互相依赖:
【1】创建A
的实例: A
依赖B
,但B
尚未创建。Spring
会将A
的早期引用(通常是一个代理对象)放入三级缓存singletonFactories
中。
【2】创建B
的实例: B
依赖A
,Spring
会从三级缓存singletonFactories
中获取A
的早期引用,并将其放入二级缓存 earlySingletonObjects
中。通过早期引用可知,B
注入的是A
的引用,所以最终拿到的是一个完整的A
对象。
【3】完成B
的初始化: B
完全初始化后,放入一级缓存singletonObjects
中。
【4】完成A
的初始化: A
获取到B
的完全初始化的实例后,完成自身初始化,并放入一级缓存singletonObjects
中。
【1】代理对象的创建: 在某些情况下,Spring
需要为bean
创建代理对象(例如,使用AOP
时)。代理对象的创建通常在bean
初始化的后期阶段进行。
如果只使用二级缓存,意味着所有Bean
在实例化后就要完成AOP
代理,在某些情况下,Spring
可能无法正确地创建代理对象,因为代理对象的创建依赖于完整的bean
初始化过程,这样违背了Spring
设计的原则,Spring
在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator
这个后置处理器来在Bean生命周期的最后一步来完成AOP
代理,而不是在实例化后就立马进行AOP
代理。
三级缓存中的对象工厂可以确保在需要时创建代理对象,并将其放入二级缓存,从而确保代理对象可以在循环依赖中正确地被引用。
【2】延迟创建早期引用: 三级缓存中的对象工厂允许Spring
在需要时延迟创建早期引用,而不是立即创建。这种延迟创建机制可以确保在某些特殊情况下,bean
可以在完全初始化之前被引用。
通过这种方式,Spring
可以更灵活地处理各种复杂的依赖关系和代理对象的创建。
原文链接:https://blog.csdn.net/zhengzhaoyang122/article/details/142691283
作者:你是好人
链接:http://www.phpheidong.com/blog/article/554245/556123f6ad6c55d9bc88/
来源:php黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 php黑洞网 All Rights Reserved 版权所有,并保留所有权利。 京ICP备18063182号-4
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!