Handle circular dependency in CDI

I have such a situation. I do not see errors, but I do not get 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 wrong?

Can anyone help me on this.

+5
source share
4 answers

I would avoid this cyclical dependency, there are several reasons for this.

Comment in this article

A fuzzy constructor is a sign. He warns me that my class becomes a monolith, which is a jack of all professions and a master of none. In other words, a messy constructor is really good. If I feel like the constructor of the class is too messy, I know it's time to do something.

And this one

You will find cases where class A needs an instance of B and B, an instance of A. is required. This is a typical case of a circular dependency and is obviously bad. In my experience, the solution is to make B part A when they are so dependent so much that they really have to be one class. Most often there is at least one other class C hiding there, so B does not need A, but only C.

As Oliver Gercke commented :

A special constructor injection actually prohibits you from introducing circular dependencies. If you introduce them, you will essentially make two sides of one, because you cannot change it without risking breaking the other, which in each case is a designer smell.

Here is a small example of what I can 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 may 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 } } 
+3
source

Quote from section 5 CDI 1.2 specification :

A container is needed to support pie charts in a dependency graph, in which at least one bean involved in each dependency circular chain has a normal scale, as defined in Normal Regions and Pseudo Objects. A container is not required to support circular dependency chains in which each bean in the chain has a pseudo-object.

ApplicationScoped is a normal scope, so this loop should work.

In your example, class A cannot be proxied because there is no constructor with a null argument. By adding this constructor (which may have security or package visibility), your sample is deployed without problems.

+2
source

You can also use Setgen Dependency Injection to solve this problem.

0
source

Definitely this solution. Let me quote :

The correct solution is to enter javax.enterprise.inject.Instance, where T is the type of class to introduce. Since the type is directly Foo, calling the get () method on an object typed as an instance is guaranteed to enter the correct object all the time. This approach works very well, because the instance is dynamically obtained from the container by the implementation itself and only as necessary. Therefore, the responsibility for finding dependencies rests with your code - your code is responsible for not making an endless loop.

  @Named public class Foo implements Fooable{ @Inject private Instance<Foo> foo; public void executeFirst(){ foo.get().executeSecond(); } @Transactional public void executeSecond(){ //do something } } 
0
source

All Articles