Correctness12 - SpotBugsExtensionForSpringFrameWork/CS5098 GitHub Wiki

Bug pattern name: CONSTRUCTOR_CIRCULAR_DEPENDENCY Short Description: While using constructor injection, beware of circular dependencies (BeanCurrentlyInCreationException)

Description

When Spring context is loading all the beans, it tries to create beans in the order needed for them to work completely. For instance, if we didn’t have a circular dependency, like the following case:

Bean A → Bean B → Bean C

Spring will create bean C, then create bean B (and inject bean C into it), then create bean A (and inject bean B into it).

But, when having a circular dependency, Spring cannot decide which of the beans should be created first, since they depend on one another. In these cases, Spring will raise a BeanCurrentlyInCreationException while loading context.

It can happen in Spring when using constructor injection; if you use other types of injections you should not find this problem since the dependencies will be injected when they are needed and not on the context loading.

// Example 1
@Component
public class BeanA implements IBeanA {
    private IBeanB beanB; // circular dependencies
    @Autowired
    public BeanA(final IBeanB beanB) {
        super();
        this.beanB = beanB;
    }
}
@Component
public class BeanB implements IBeanB {
    final IBeanA beanA; // circular dependencies

    @Autowired
    public BeanB(final IBeanA beanA) {
        super();
        this.beanA = beanA;
    }
}
// ---------------------------------------------------------
// Example 2 
@Component
public class CircularDependencyA {

    private CircularDependencyB circB;

    @Autowired
    public CircularDependencyA(CircularDependencyB circB) {
        this.circB = circB;
    }
}
@Component
public class CircularDependencyB {

    private CircularDependencyA circA;

    @Autowired
    public CircularDependencyB(CircularDependencyA circA) {
        this.circA = circA;
    }
}

// Result: BeanCurrentlyInCreationException

Spring will not be able to resolve this kind of wiring scenario, circular dependencies. BeanCurrentlyInCreationException (One of the subclasses of BeanCreationException) will be thrown.

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). Spring Docs

There are two more circular dependencies: Setter loop dependencies in singleton mode; Non-singleton circular dependency link link.

Evidence

  • "There is a pool of objects being created in spring, which is added to the pool when objects are created, singletonsCurrentlylnCreation.add(beanName). The creation is completed and deleted from the pool. When trying to create an object, if the name of the object is already included in the pool before creation, it will directly return BeanCurrentlylnCreationException. The exception indicates a circular dependency." link
  • Additional Theory: "The initialization of the spring singleton object is roughly divided into three steps: createBeanInstance: instantiation, in fact, is the constructor of the calling object to instantiate the object; populateBean: populates the property, this step is mainly filled with the dependency properties of multiple beans; initializeBean: Calls the init method in spring xml. From the singleton bean initialization steps described above, we can see that circular dependencies mainly occur in the first and second steps. That is, constructor loop dependencies and field loop dependencies. link"

Reference List