I have situation like this. I cannot see any errors but I am not getting my results.
@ApplicationScoped
public class A {
private B b;
@Inject
public A(B b) {
this.b = b;
}
}
@Singleton
public class B {
private A a;
@Inject
public B(A a) {
this.a = a;
}
}
Is this type of dependency injection is wrong?
Can any one help me with this.
There are a couple of options to get rid of circular dependencies. For a longer chain, A -> B -> C -> D -> A , if one of the references is removed (for instance, the D -> A reference), the cyclic reference pattern is broken, as well. For simpler patterns, such as A -> B -> A , refactoring may be necessary.
To resolve circular dependencies: Then there are three strategies you can use: Look for small pieces of code that can be moved from one project to the other. Look for code that both libraries depend on and move that code into a new shared library. Combine projectA and projectB into one library.
A cyclic dependency is an indication of a design or modeling problem in your software. Although you can construct your object graph by using property injection, you will ignore the root cause and add another problem: property injection causes Temporal Coupling. Instead, the solution is to look at the design closely.
To remove the cycle using DI, we remove one direction of the two dependencies and harden the other one. To do that we should use an abstraction: in the order module, we add the DiscountCalculator interface. Thus, the Order class depends on this new type and the order module no longer depends on the customer module.
I'd avoid this circular dependency, there is a few reasons to do that.
Comment on this article
A messy constructor is a sign. It warns me that my class is becoming a monolith which is a jack of all trades and a master of none. In other words, a messy constructor is actually a good thing. If I feel that the constructor of a class is too messy, I know that it is time to do something about it.
And this one
You’ll find cases where a class A needs an instance of B and B needs an instance of A. This is a typical case of a circular dependency and is obviously bad. In my experience the solution is either to make B a part of A when the two are so strongly dependent that they really should be one class. More often though there is at least one more class C hiding in there so that B doesn’t need A but only C.
As Oliver Gerke commented:
Especially constructor injection actually prevents you from introducing cyclic dependencies. If you do introduce them you essentially make the two parties one because you cannot really change the one without risking to break the other, which in every case is a design smell.
Here is a small example of what I might do.
public class A {
private B b;
@Autowired
public A(B b) {
this.b = b;
}
public void doSomeWork() {
// WORK
}
public void doSomeWorkWithB() {
b.doSomeWork();
}
}
public class B {
private A a;
@Autowired
public B(A a) {
this.a = a;
}
public void doSomeWork() {
// WORK
}
public void doSomeWorkWithA() {
a.doSomeWork();
}
}
After refactoring it might look like this.
public class A {
private C c;
@Autowired
public A(C c) {
this.c = c;
}
public void doSomeWork() {
// WORK
}
public void doSomeWorkWithC() {
c.doSomeWorkThatWasOnA();
}
}
public class B {
private C c;
@Autowired
public B(C c) {
this.c = c;
}
public void doSomeWork() {
// WORK
}
public void doSomeWorkWithC() {
c.doSomeWorkThatWasOnB();
}
}
public class C {
public void doSomeWorkThatWasOnB() {
// WORK
}
public void doSomeWorkThatWasOnA() {
// WORK
}
}
Quoting from Section 5 of the CDI Specification 1.2:
The container is required to support circularities in the bean dependency graph where at least one bean participating in every circular chain of dependencies has a normal scope, as defined in Normal scopes and pseudo-scopes. The container is not required to support circular chains of dependencies where every bean participating in the chain has a pseudo-scope.
ApplicationScoped
is a normal scope, so this cycle should work.
In your sample, class A
cannot be proxied since it's missing a zero-argument constructor. Adding this constructor (which may have protected or package visibility), your sample deploys without problems.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With