Spring如何解决这个问题:bean A依赖于bean B,bean B依赖于beanA。
Spring如何解决这个问题:bean A依赖于bean B,bean B依赖于beanA。
Answers:
在Spring参考手册介绍了循环依赖是如何解决的。首先实例化这些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
尚未完全初始化。
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
在我正在使用的代码库(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秒。
因此,不要总是认为spring可以很好地为您解决这些参考。
出于这个原因,我建议使用AbstractRefreshableApplicationContext#setAllowCircularReferences(false)禁用循环依赖关系解析,以防止将来出现许多问题。
问题->
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:创建名称为'A'的bean时出错:当前正在创建请求的bean:是否存在不可解析的循环引用?
解决方案1->
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->
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 };
}
就是这样。它实例化a
和b
,并将彼此注入(使用其setter方法)。
有什么问题?
假设A依赖于B,那么Spring将首先实例化A,然后实例化B,然后为B设置属性,然后将B设置为A。
但是,如果B也依赖于A怎么办?
我的理解是:Spring刚刚发现A已经被构造(构造函数执行),但是还没有完全初始化(并不是所有注入都完成了),嗯,它认为,没关系,A可以被完全初始化是可以容忍的,只要将其设置为-现在将A实例完全初始化为B。在B完全初始化之后,将其设置为A,最后,A现已完全启动。
换句话说,它只是提前将A暴露给B。
对于通过构造函数的依赖关系,Sprint只是抛出BeanCurrentlyInCreationException,以解决此异常,请通过构造函数-arg方式将依赖于其他bean的bean的lazy-init设置为true。
如果您通常使用构造函数注入并且不想切换到属性注入,那么Spring的lookup-method -injection将让一个bean懒惰地查找另一个,从而解决循环依赖性。参见此处:http : //docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
如果两个bean相互依赖,则我们不应在两个bean定义中都使用构造函数注入。相反,我们必须在任何一个bean中使用setter注入。(当然,我们可以在两个bean定义中同时使用setter注入,但是在两个bean定义中都使用构造函数注入会抛出'BeanCurrentlyInCreationException'
请参阅“ https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource ”中的Spring文档。