Spring如何解决这个问题:bean A依赖于bean B,bean A依赖于bean A.
答案 0 :(得分:69)
Spring reference manual解释了如何解决循环依赖关系。首先实例化bean,然后相互注入。
考虑这个课程:
package mypackage;
public class A {
public A() {
System.out.println("Creating instance of A");
}
private B b;
public void setB(B b) {
System.out.println("Setting property b of A instance");
this.b = b;
}
}
类似的课程B
:
package mypackage;
public class B {
public B() {
System.out.println("Creating instance of B");
}
private A a;
public void setA(A a) {
System.out.println("Setting property a of B instance");
this.a = a;
}
}
如果你有这个配置文件:
<bean id="a" class="mypackage.A">
<property name="b" ref="b" />
</bean>
<bean id="b" class="mypackage.B">
<property name="a" ref="a" />
</bean>
使用此配置创建上下文时,您会看到以下输出:
Creating instance of A
Creating instance of B
Setting property a of B instance
Setting property b of A instance
请注意,当a
注入b
时,a
尚未完全初始化。
答案 1 :(得分:38)
正如其他答案所说,Spring只会处理它,创建bean并根据需要注入它们。
其中一个后果是bean注入/属性设置可能以与XML布线文件似乎暗示的顺序不同的顺序发生。因此,您需要注意,您的属性设置器不会执行依赖于已调用的其他setter的初始化。处理此问题的方法是将bean声明为实现InitializingBean
接口。这要求您实现afterPropertiesSet()
方法,这是您进行关键初始化的地方。 (我还包括用于检查实际已设置重要属性的代码。)
答案 2 :(得分:17)
在我正在使用的代码库中(100万行+代码行),我们遇到了启动时间长,大约60秒的问题。我们得到了12000+ FactoryBeanNotInitializedException。
我所做的是在AbstractBeanFactory#doGetBean
中设置条件断点catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
它destroySingleton(beanName)
我用条件断点代码打印了异常:
System.out.println(ex);
return false;
显然,当FactoryBean涉及循环依赖图时会发生这种情况。我们通过实现ApplicationContextAware和InitializingBean并手动注入bean来解决它。
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class A implements ApplicationContextAware, InitializingBean{
private B cyclicDepenency;
private ApplicationContext ctx;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
ctx = applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
cyclicDepenency = ctx.getBean(B.class);
}
public void useCyclicDependency()
{
cyclicDepenency.doSomething();
}
}
这将启动时间减少到大约15秒。
所以不要总是认为春天可以很好地为你解决这些问题。
出于这个原因,我建议使用AbstractRefreshableApplicationContext#setAllowCircularReferences(false)禁用循环依赖项解决,以防止将来出现许多问题。
答案 3 :(得分:12)
它就是这样做的。它实例化a
和b
,并将每个注入另一个(使用他们的setter方法)。
有什么问题?
答案 4 :(得分:8)
问题 - &gt;
Class A {
private final B b; // must initialize in ctor/instance block
public A(B b) { this.b = b };
}
Class B {
private final A a; // must initialize in ctor/instance block
public B(A a) { this.a = a };
}
//由以下原因引起:org.springframework.beans.factory.BeanCurrentlyInCreationException:创建名称为&#39; A&#39;:请求的bean当前正在创建时出错:是否存在无法解析的循环引用?
解决方案1 - &gt;
Class A {
private B b;
public A( ) { };
//getter-setter for B b
}
Class B {
private A a;
public B( ) { };
//getter-setter for A a
}
解决方案2 - &gt;
Class A {
private final B b; // must initialize in ctor/instance block
public A(@Lazy B b) { this.b = b };
}
Class B {
private final A a; // must initialize in ctor/instance block
public B(A a) { this.a = a };
}
答案 5 :(得分:6)
你通常可以信任Spring 正确的事情。它检测到 配置问题,如 引用不存在的bean和 循环依赖,在容器中 加载时间。 Spring设置属性和 最迟解决依赖关系 可能,当实际上是豆 创建
答案 6 :(得分:6)
Spring容器能够解析基于Setter的循环依赖关系,但在基于构造函数的循环依赖关系中提供运行时异常BeanCurrentlyInCreationException。 在基于Setter的循环依赖的情况下,IOC容器处理它与典型情况不同,在典型情况下它将在注入之前完全配置协作bean。 例如,如果Bean A在Bean C上依赖Bean B和Bean B,则容器在将C注入B之前完全初始化C,并且一旦B完全初始化,它将被注入A。但是在循环依赖的情况下,在完全初始化之前,将豆子注入另一个豆子。
答案 7 :(得分:5)
明确解释here。感谢Eugen Paraschiv。
循环依赖是一种设计气味,要么修复它,要么使用@Lazy作为依赖项,导致问题解决它。
答案 8 :(得分:4)
说A取决于B,然后Spring首先实例化A,然后B,然后设置B的属性,然后将B设置为A.
但是,如果B还依赖于A?
我的理解是:Spring刚刚发现A已被构造(构造函数被执行),但未完全初始化(并非所有注入完成),嗯,它认为,没关系,可以容忍A没有完全初始化,只是设置这个未完全初始化的A实例现在变为B. B完全初始化后,将其设置为A,最后,A现在完全启动。
换句话说,它只是提前将A暴露给B.
对于依赖于构造函数的依赖项,Sprint只是抛出BeanCurrentlyInCreationException来解决这个异常,将bean的lazy-init设置为true,依赖于其他构造函数arg方式。
答案 9 :(得分:3)
如果你通常使用构造函数注入并且不想切换到属性注入,那么Spring的 lookup-method -injection将让一个bean懒惰地查找另一个因此解决了循环依赖。见这里:http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
答案 10 :(得分:2)
Spring 中的循环依赖:一个 Bean 对另一个 Bean 的依赖。 Bean A → Bean B → Bean A
解决方案:
@Lazy
注释@PostConstruct
注释答案 11 :(得分:0)
使用Setter Injection或Field Injection或使用@Lazy作为依赖项。
答案 12 :(得分:0)
如果两个bean彼此依赖,那么我们不应该在两个bean定义中使用Constructor注入。相反,我们必须在任何一个bean中使用setter注入。 (当然我们可以在两个bean定义中使用setter注入,但是抛出'BeanCurrentlyInCreationException'中的构造函数注入
请参阅Spring文档“https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource”
答案 13 :(得分:0)
当spring bean之间存在Circular Dependency时,构造函数注入失败。所以在这种情况下,我们Setter注入有助于解决问题。
基本上,构造函数注入对强制依赖很有用,对于可选的依赖更好地使用Setter注入,因为我们可以重新注入。