spring的循环依赖

spring如何解决这个问题。 Bean A依赖于bean b,bean b依赖于bean a。

正如其他答案所说的,Spring只是照顾它,创buildbean并根据需要注入它们。

其中一个后果就是bean的注入/属性设置可能与您的XML布线文件似乎暗示的顺序不同。 所以你需要小心,你的属性设置者不要做依赖于已经被调用的其他setter的初始化。 处理这个问题的方法是将bean声明为实现InitializingBean接口。 这需要你实现afterPropertiesSet()方法,这就是你进行关键初始化的地方。 (我还包括检查重要属性已经设置的代码。)

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; } } 

如果你有这个configuration文件:

 <bean id="a" class="mypackage.A"> <property name="b" ref="b" /> </bean> <bean id="b" class="mypackage.B"> <property name="a" ref="a" /> </bean> 

使用此configuration创build上下文时,您将看到以下输出:

 Creating instance of A Creating instance of B Setting property a of B instance Setting property b of A instance 

请注意,当a被注入到ba还没有被完全初始化。

在我正在使用的代码库(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)我用条件断点代码打印exception:

  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可以很好的为你解决这些参考。

出于这个原因,我build议使用AbstractRefreshableApplicationContext#setAllowCircularReferences(false)禁用循环依赖解决scheme,以防止许多未来的问题。

它只是做到了。 它实例化ab ,并注入每个到另一个(使用他们的setter方法)。

有什么问题?

从春季参考 :

一般来说,你可以相信Spring做正确的事情。 它在容器加载时检测configuration问题,例如引用不存在的bean和循环依赖关系。 当bean实际创build时,Spring会尽可能晚地设置属性并解决依赖关系。

Spring容器能够parsing基于Setter的循环依赖关系,但在基于构造器的循环依赖关系的情况下给出运行时exceptionBeanCurrentlyInCreationException。 在基于Setter的循环依赖的情况下,IOC容器的处理方式与典型的情况不同,在这种情况下,它将在注入之前完全configuration协作bean。 例如,如果Bean A对Bean C上的Bean B和Bean B具有依赖关系,那么容器在将其注入到B之前完全初始化C,并且一旦B完全初始化,它就被注入到A.但是在循环依赖的情况下,在完全初始化之前,这些bean被注入到另一个中。

如果您通常使用构造函数注入并且不想切换到属性注入,那么Spring的lookup-method -injection将让一个bean懒洋洋地查找另一个bean,从而解决循环依赖问题。 请参阅: http : //docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161

它在这里清楚地解释。 感谢Eugen Paraschiv。

循环依赖是一个devise气味,要么修复它,要么使用@Lazy作为依赖关系,导致问题解决它。

说A取决于B,那么Spring将首先实例化A,然后B,然后为B设置属性,然后将B设置为A.

但是如果B也依赖于A呢?

我的理解是:Spring刚刚发现A已经被构造(构造函数执行),但是没有完全初始化(不是所有的注入都完成了),好吧,它认为,没关系,A是没有完全初始化的,完全初始化A到B的实例。 在B完全初始化之后,它被设置为A,最后现在A被完全启动。

换句话说,它只是提前暴露A到B.

对于通过构造函数的依赖关系,Sprint只抛出BeanCurrentlyInCreationException,为了解决这个exception,将lazy-init设置为true,以便通过constructor-arg方式依赖其他bean。

通过使用Setter注入或Field注入或通过使用@Lazy的依赖。