Java Spring中的循环依赖
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3485347/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Circular dependency in Spring
提问by fastcodejava
How does Spring resolve this: bean A is dependent on bean B, and bean B on bean A.
Spring是如何解决这个问题的:bean A依赖bean B,bean B依赖bean A。
采纳答案by Stephen C
As the other answers have said, Spring just takes care of it, creating the beans and injecting them as required.
正如其他答案所说,Spring 只负责它,创建 bean 并根据需要注入它们。
One of the consequences is that bean injection / property setting might occur in a different order to what your XML wiring files would seem to imply. So you need to be careful that your property setters don't do initialization that relies on other setters already having been called. The way to deal with this is to declare beans as implementing the InitializingBean
interface. This requires you to implement the afterPropertiesSet()
method, and this is where you do the critical initialization. (I also include code to check that important properties have actually been set.)
后果之一是 bean 注入/属性设置的发生顺序可能与您的 XML 连接文件所暗示的顺序不同。所以你需要注意你的属性设置器不要做依赖于已经被调用的其他设置器的初始化。处理这个问题的方法是将 bean 声明为实现InitializingBean
接口。这需要您实现该afterPropertiesSet()
方法,这是您进行关键初始化的地方。(我还包含了用于检查重要属性是否已实际设置的代码。)
回答by skaffman
It just does it. It instantiates a
and b
, and injects each one into the other (using their setter methods).
它就是这样做的。它实例化a
and b
,并将每个注入另一个(使用它们的 setter 方法)。
What's the problem?
有什么问题?
回答by Richard Fearn
The Spring reference manualexplains how circular dependencies are resolved. The beans are instantiated first, then injected into each other.
在Spring参考手册介绍了循环依赖是如何解决的。这些 bean 首先被实例化,然后相互注入。
Consider this class:
考虑这个类:
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;
}
}
And a similar class 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;
}
}
If you then had this configuration file:
如果你有这个配置文件:
<bean id="a" class="mypackage.A">
<property name="b" ref="b" />
</bean>
<bean id="b" class="mypackage.B">
<property name="a" ref="a" />
</bean>
You would see the following output when creating a context using this configuration:
使用此配置创建上下文时,您将看到以下输出:
Creating instance of A
Creating instance of B
Setting property a of B instance
Setting property b of A instance
Note that when a
is injected into b
, a
is not yet fully initialised.
请注意, whena
注入b
,a
尚未完全初始化。
回答by earldouglas
From the Spring Reference:
从春天参考:
You can generally trust Spring to do the right thing. It detects configuration problems, such as references to non-existent beans and circular dependencies, at container load-time. Spring sets properties and resolves dependencies as late as possible, when the bean is actually created.
您通常可以相信 Spring 会做正确的事情。它在容器加载时检测配置问题,例如对不存在的 bean 的引用和循环依赖。Spring 在真正创建 bean 时尽可能晚地设置属性并解析依赖项。
回答by Saurav S.
The Spring container is able to resolve Setter-based circular dependencies but gives a runtime exception BeanCurrentlyInCreationException in case of Constructor-based circular dependencies. In case of Setter-based circular dependency, the IOC container handles it differently from a typical scenario wherein it would fully configure the collaborating bean before injecting it. For eg., if Bean A has a dependency on Bean B and Bean B on Bean C, the container fully initializes C before injecting it to B and once B is fully initialized it is injected to A. But in case of circular dependency, one of the beans is injected to the other before it is fully initialized.
Spring 容器能够解析基于 Setter 的循环依赖,但在基于 Constructor 的循环依赖的情况下会给出运行时异常 BeanCurrentlyInCreationException。在基于 Setter 的循环依赖的情况下,IOC 容器处理它与典型场景不同,在典型场景中它将在注入之前完全配置协作 bean。例如,如果 Bean A 依赖于 Bean B 并且 Bean B 依赖于 Bean C,则容器在将 C 注入 B 之前完全初始化 C,一旦 B 完全初始化,它就会注入到 A。但是在循环依赖的情况下,一个的 bean 在完全初始化之前被注入另一个。
回答by jontejj
In the codebase I'm working with (1 million + lines of code) we had a problem with long startup times, around 60 seconds. We were getting 12000+ FactoryBeanNotInitializedException.
在我使用的代码库中(100 万+ 行代码),我们遇到了启动时间长的问题,大约 60 秒。我们得到 12000+ FactoryBeanNotInitializedException。
What I did was set a conditional breakpoint in AbstractBeanFactory#doGetBean
我所做的是在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;
}
where it does destroySingleton(beanName)
I printed the exception with conditional breakpoint code:
destroySingleton(beanName)
我在哪里用条件断点代码打印异常:
System.out.println(ex);
return false;
Apparently this happens when FactoryBeans are involved in a cyclic dependency graph. We solved it by implementing ApplicationContextAwareand InitializingBeanand manually injecting the beans.
显然,当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();
}
}
This cut down the startup time to around 15 secs.
这将启动时间缩短到 15 秒左右。
So don't always assume that spring can be good at solving these references for you.
所以不要总是假设 spring 可以很好地为你解决这些引用。
For this reason I'd recommend disabling cyclic dependency resolution with AbstractRefreshableApplicationContext#setAllowCircularReferences(false)to prevent many future problems.
出于这个原因,我建议使用AbstractRefreshableApplicationContext#setAllowCircularReferences(false)禁用循环依赖解析,以防止未来出现许多问题。
回答by barclar
If you generally use constructor-injection and don't want to switch to property-injection then Spring's lookup-method-injection will let one bean lazily lookup the other and hence workaround the cyclic dependency. See here: http://docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
如果您通常使用构造函数注入并且不想切换到属性注入,那么 Spring 的查找方法注入将让一个 bean 懒惰地查找另一个 bean,从而解决循环依赖。见这里:http: //docs.spring.io/spring/docs/1.2.9/reference/beans.html#d0e1161
回答by Silent
Say A depends on B, then Spring will first instantiate A, then B, then set properties for B, then set B into A.
假设 A 依赖于 B,那么 Spring 会先实例化 A,然后是 B,然后为 B 设置属性,然后将 B 设置为 A。
But what if B also depends on A?
但是如果 B 也依赖于 A 呢?
My understanding is: Spring just found that A has been constructed (constructor executed), but not fully initialized (not all injections done), well, it thought, it's OK, it's tolerable that A is not fully initialized, just set this not-fully-initialized A instances into B for now. After B is fully initialized, it was set into A, and finally, A was fully initiated now.
我的理解是:Spring刚刚发现A已经构造好了(构造函数执行了),但是没有完全初始化(不是所有的注入都完成了),好吧,它想,没关系,A没有完全初始化是可以容忍的,就设置这个not-现在将 A 实例完全初始化到 B 中。B完全初始化后,被设置到A中,最后,A现在完全初始化了。
In other words, it just expose A to B in advance.
换句话说,它只是提前将 A 暴露给 B。
For dependencies via constructor, Sprint just throw BeanCurrentlyInCreationException, to resolve this exception, set lazy-init to true for the bean which depends on others via constructor-arg way.
对于通过构造函数的依赖关系,Sprint 只是抛出 BeanCurrentlyInCreationException,为了解决这个异常,通过构造函数参数的方式将依赖于其他人的 bean 的 lazy-init 设置为 true。
回答by whoami
回答by Srikant M
If two beans are dependent on each other then we should not use Constructor injection in both the bean definitions. Instead we have to use setter injection in any one of the beans. (of course we can use setter injection n both the bean definitions, but constructor injections in both throw 'BeanCurrentlyInCreationException'
如果两个 bean 相互依赖,那么我们不应该在两个 bean 定义中使用构造函数注入。相反,我们必须在任何一个 bean 中使用 setter 注入。(当然,我们可以在两个 bean 定义中使用 setter 注入,但是两个中的构造函数注入都会抛出 'BeanCurrentlyInCreationException'
Refer Spring doc at "https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource"
请参阅“ https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resource”中的Spring 文档