问题
As per my understanding, @Lazy annotation and lazy-init attribute of tag should have the same functionality. But when I developed the following code, it's showing distinct behaviours. In the following code, I was expecting :- (Circular Dependency Error)
org.springframework.beans.factory.BeanCurrentlyInCreationException
I have attached code using @Lazy annotation, as per my expectations it should not allow Circular Dependency.
@Component
public class A {
private B b;
@Autowired
public A(@Lazy B b) {
System.out.println("A.A() - 1-param Constructor");
this.b = b;
}
}
@Component
public class B {
private A a;
@Autowired
public B(A a) {
System.out.println("B.B() - 1-param Constructor");
this.a = a;
}
}
Main Class :
public class AnnotationApp{
public static void main(String[] args){
ApplicationContext ctx = new ClassPathXmlApplicationContext("com/ry/cfgs/annotationAppContext.xml");
B objB = ctx.getBean("b", B.class);
A objA = ctx.getBean("a", A.class);
}
}
Spring Configuration File :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.ry.beans.annotation"></context:component-scan>
</beans>
OUTPUT :-
A.A() - 1-param Constructor
B.B() - 1-param Constructor
Require explanation, why it's behaving like this?
回答1:
From Spring Framework Documentation:
... you can also place the
@Lazy
annotation on injection points marked with@Autowired
or@Inject
. In this context, it leads to the injection of a lazy-resolution proxy.
So, in the following code:
@Autowired
public A(@Lazy B b) {
// ...
}
b
will be injected (autowired) on first access instead of on startup.
Now, if you change your code to the following:
@Autowired
public A(@Lazy B b) {
System.out.println("A.A() - 1-param Constructor");
System.out.println(b.toString());
this.b = b;
}
you will see that org.springframework.beans.factory.BeanCurrentlyInCreationException
is thrown.
回答2:
You've already used one of mechanisms to avoid circular dependency by using @Lazy on the constructor.
See this link which explains what that is. Quoting it here:
Circular dependencies
If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.
For example: Class A requires an instance of class B through constructor injection, and class B requires an instance of class A through constructor injection. If you configure beans for classes A and B to be injected into each other, the Spring IoC container detects this circular reference at runtime, and throws a BeanCurrentlyInCreationException.
One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.
Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken-and-egg scenario).
When you added
public A(@Lazy B b) { .. }
by using @Lazy, Spring will inject a Proxy instance of B
(typically CGLIB) instead of simply B
. As a result, it does NOT need to create an instance and so it worked.
Try removing the @Lazy
and you'll run to the issue you mentioned.
Ways to avoid Circular dependencies:
- Use @Lazy as you did
- Use setter injection instead of Constructor injection
Some more techniques are given in this link
来源:https://stackoverflow.com/questions/53924075/whats-the-difference-between-lazy-annotation-and-lazy-init-attribute-of-bean