Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Circular dependency in Spring

Tags:

java

spring

The Spring reference manual explains how circular dependencies are resolved. The beans are instantiated first, then injected into each other.

Consider this class:

package mypackage;

public class A {

    public A() {
        System.out.println("Creating instance of A");
    }

    private B b;

    public void setB(B b) {
        System.out.println("Setting property b of A instance");
        this.b = b;
    }

}

And a similar class B:

package mypackage;

public class B {

    public B() {
        System.out.println("Creating instance of B");
    }

    private A a;

    public void setA(A a) {
        System.out.println("Setting property a of B instance");
        this.a = a;
    }

}

If you then had this configuration file:

<bean id="a" class="mypackage.A">
    <property name="b" ref="b" />
</bean>

<bean id="b" class="mypackage.B">
    <property name="a" ref="a" />
</bean>

You would see the following output when creating a context using this configuration:

Creating instance of A
Creating instance of B
Setting property a of B instance
Setting property b of A instance

Note that when a is injected into b, a is not yet fully initialised.


As the other answers have said, Spring just takes care of it, creating the beans and injecting them as required.

One of the consequences is that bean injection / property setting might occur in a different order to what your XML wiring files would seem to imply. So you need to be careful that your property setters don't do initialization that relies on other setters already having been called. The way to deal with this is to declare beans as implementing the InitializingBean interface. This requires you to implement the afterPropertiesSet() method, and this is where you do the critical initialization. (I also include code to check that important properties have actually been set.)


In the codebase I'm working with (1 million + lines of code) we had a problem with long startup times, around 60 seconds. We were getting 12000+ FactoryBeanNotInitializedException.

What I did was set a conditional breakpoint in AbstractBeanFactory#doGetBean

catch (BeansException ex) {
   // Explicitly remove instance from singleton cache: It might have been put there
   // eagerly by the creation process, to allow for circular reference resolution.
   // Also remove any beans that received a temporary reference to the bean.
   destroySingleton(beanName);
   throw ex;
}

where it does destroySingleton(beanName) I printed the exception with conditional breakpoint code:

   System.out.println(ex);
   return false;

Apparently this happens when FactoryBeans are involved in a cyclic dependency graph. We solved it by implementing ApplicationContextAware and InitializingBean and manually injecting the beans.

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class A implements ApplicationContextAware, InitializingBean{

    private B cyclicDepenency;
    private ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        ctx = applicationContext;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        cyclicDepenency = ctx.getBean(B.class);
    }

    public void useCyclicDependency()
    {
        cyclicDepenency.doSomething();
    }
}

This cut down the startup time to around 15 secs.

So don't always assume that spring can be good at solving these references for you.

For this reason I'd recommend disabling cyclic dependency resolution with AbstractRefreshableApplicationContext#setAllowCircularReferences(false) to prevent many future problems.


Problem ->

Class A {
    private final B b; // must initialize in ctor/instance block
    public A(B b) { this.b = b };
}


Class B {
    private final A a; // must initialize in ctor/instance block
    public B(A a) { this.a = a };
 }

// Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'A': Requested bean is currently in creation: Is there an unresolvable circular reference?

Solution 1 ->

Class A {
    private B b; 
    public A( ) {  };
    //getter-setter for B b
}

Class B {
    private A a;
    public B( ) {  };
    //getter-setter for A a
}

Solution 2 ->

Class A {
    private final B b; // must initialize in ctor/instance block
    public A(@Lazy B b) { this.b = b };
}

Class B {
    private final A a; // must initialize in ctor/instance block
    public B(A a) { this.a = a };
}

It just does it. It instantiates a and b, and injects each one into the other (using their setter methods).

What's the problem?


Say A depends on B, then Spring will first instantiate A, then B, then set properties for B, then set B into A.

But what if B also depends on A?

My understanding is: Spring just found that A has been constructed (constructor executed), but not fully initialized (not all injections done), well, it thought, it's OK, it's tolerable that A is not fully initialized, just set this not-fully-initialized A instances into B for now. After B is fully initialized, it was set into A, and finally, A was fully initiated now.

In other words, it just expose A to B in advance.

For dependencies via constructor, Sprint just throw BeanCurrentlyInCreationException, to resolve this exception, set lazy-init to true for the bean which depends on others via constructor-arg way.


From the Spring Reference:

You can generally trust Spring to do the right thing. It detects configuration problems, such as references to non-existent beans and circular dependencies, at container load-time. Spring sets properties and resolves dependencies as late as possible, when the bean is actually created.