spring-projects / spring-framework

Spring Framework
https://spring.io/projects/spring-framework
Apache License 2.0
55.27k stars 37.62k forks source link

Creating beans in a resolvable circular dependency graph that involves both construct injection and properties dose not always work #32711

Closed RickardNarsromLINK closed 2 weeks ago

RickardNarsromLINK commented 3 weeks ago

I have several beans with circular dependencies between them, some are using constructor injection and some property injection. The dependency graph should be construable by carefully creating beans in the right order and injecting beans before they have all of there properties set.

Spring IoC dose not seem to be able to resolve such a dependency graph, but will fail with an exception.

A simple minimal example with only two beans is:

public class A {
public A(B b) { }
}
public class B {
public void setA(A a) { }
}
<beans>
<bean name="a" class="A">
<constructor-arg index="0" ref="b" />
</bean>
<bean name="b" class="B">
<property name="a" ref="a" />
</bean>
</beans>

Crating a ApplicationContext from this example results in an exception telling us we have a circular dependency. This dependency should however be able to be resolved as we are using property injection in one of the beans. By first creating "b", then creating "a" injecting "b" into the constructor and lastly injecting "a" into the setter in "b" we can create this set of beans.

In this simple example we can resolve the problem by exchanging the order of the declarations of the beans in the xml file, so that "b" is declared before "a".

I should probably be able to solve the more complicated dependency graph in my application by carefully exchanging the order of declarations of beans and properties in the xml config. But this is error prune and unstable. Any small change to the resulting config may brake bean creation again.

snicoll commented 2 weeks ago

Unfortunately, circular dependency references are best effort and we might consider deprecate and remove the feature in a future release. The resolution follows the declaration order and therefore this is the expected behavior.

You can break the cycle by being more declarative about it, using @Lazy and ObjectProvider.