Spring — Circular Dependencies in Spring | Code Factory

  • 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.
package com.codeFactory;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author code.factory
*/
@Component
public class CircularDependencyA {
private CircularDependencyB circB; @Autowired
public CircularDependencyA(CircularDependencyB circB) {
this.circB = circB;
}
}
package com.codeFactory;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author code.factory
*/
@Component
public class CircularDependencyB {
private CircularDependencyA circA; @Autowired
public CircularDependencyB(CircularDependencyA circA) {
this.circA = circA;
}
}
Description:The dependencies of some of the beans in the application context form a cycle:┌─────┐
| circularDependencyA defined in file [PROJECT_PATH\target\classes\com\codeFactory\CircularDependencyA.class]
↑ ↓
| circularDependencyB defined in file [PROJECT_PATH\target\classes\com\codeFactory\CircularDependencyB.class]
└─────┘
  • When you have a circular dependency, it’s likely you have a design problem and the responsibilities are not well separated. You should try to redesign the components properly so their hierarchy is well designed and there is no need for circular dependencies.
  • If you cannot redesign the components (there can be many possible reasons for that: legacy code, code that has already been tested and cannot be modified, not enough time or resources for a complete redesign…), there are some workarounds to try.
  • A simple way to break the cycle is saying Spring to initialize one of the beans lazily. That is: instead of fully initializing the bean, it will create a proxy to inject it into the other bean. The injected bean will only be fully created when it’s first needed.
  • To try this with our code, you can change the CircularDependencyA to the following:
package com.codeFactory;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
* @author code.factory
*/
@Component
public class CircularDependencyA {
private CircularDependencyB circB; @Autowired
public CircularDependencyA(@Lazy CircularDependencyB circB) {
this.circB = circB;
}
}
  • If you run the test now, you will see that the error does not happen this time.
  • One of the most popular workarounds, and also what Spring documentation proposes, is using setter injection.
  • Simply put if you change the ways your beans are wired to use setter injection (or field injection) instead of constructor injection — that does address the problem. This way Spring creates the beans, but the dependencies are not injected until they are needed.
package com.codeFactory;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author code.factory
*/
@Component
public class CircularDependencyA {
private CircularDependencyB circB; public CircularDependencyB getCircB() {
return circB;
}
@Autowired
public void setCircB(CircularDependencyB circB) {
this.circB = circB;
}
}
  • Another way to break the cycle is injecting a dependency using @Autowired on one of the beans, and then use a method annotated with @PostConstruct to set the other dependency.
package com.codeFactory;import javax.annotation.PostConstruct;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author code.factory
*/
@Component
public class CircularDependencyA {
@Autowired
private CircularDependencyB circB;
@PostConstruct
public void init() {
circB.setCircA(this);
}
public CircularDependencyB getCircB() {
return circB;
}
}
package com.codeFactory;import org.springframework.stereotype.Component;/**
* @author code.factory
*/
@Component
public class CircularDependencyB {
private CircularDependencyA circA; public void setCircA(CircularDependencyA circA) {
this.circA = circA;
}
public CircularDependencyA getCircA() {
return circA;
}
}

--

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Docker Storage Tutorial

Use Jackson annotations with Jackson-jr

PHP in 2019: Why Would You Still Use PHP for Building Your Websites and Web Apps?

PHP in 2019: Why Would You Still Use PHP for Building Your Websites and Web Apps?

Azure Storage Accounts

Colorize Your Shell Commands without any libraries or plugins 💻

Codility algorithm practice Lesson 1: Iterations, Task 1: Binary Gap — a Python approach

Python: probably the best and most versatile programming language ever invented.

CSSBattle | #5 Acid Rain

How to Cancel a Running Process in a Separate Request/Command in .NET C#

Learn how to cancel an already running process in a separate request/command in DotNet (.NET) CSharp (C#)

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Code Factory

Code Factory

More from Medium

Is Java dead yet? Key figures about Java usage in 2022

Reactive programming: principles, standards, implementation in Java

non-static members and its control flow in Java

Docker for Java Dev environment with IntelliJ and Gateway