Search code examples
javadependency-injectioncdi

Handle circular dependency in CDI


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.


Solution

  • 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
        }
    
    }