0Pricing
Java Academy · Lesson

Constructor Injection and Circular Dependencies

Prefer constructor injection for testability, detect circular dependencies, and break them with @Lazy.

Constructor Injection and Circular Dependencies is a free Java Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Java Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Three Injection Types

Spring supports constructor injection (recommended), field injection (@Autowired on a field), and setter injection. Constructor injection is preferred for testability and immutability.

Constructor Injection

Declare dependencies as constructor parameters. Spring injects them automatically. If there is only one constructor, @Autowired is optional (Spring Boot 2.1+).

@Service
public class OrderService {
    private final OrderRepository repo;
    private final EmailService email;
    // @Autowired is optional when there is only one constructor
    public OrderService(OrderRepository repo, EmailService email) {
        this.repo  = repo;
        this.email = email;
    }
}

Why Constructor Injection Is Preferred

Constructor injection: (1) dependencies are final — immutable, (2) class is always fully initialized, (3) no Spring dependency in unit tests — just call new OrderService(mockRepo, mockEmail).

// Unit test — no Spring context needed:
OrderRepository mockRepo  = mock(OrderRepository.class);
EmailService    mockEmail = mock(EmailService.class);
OrderService    service   = new OrderService(mockRepo, mockEmail);
// Test service logic directly

Field Injection (Avoid)

Field injection with @Autowired is concise but problematic: fields cannot be final, the bean is not fully initialized after construction, and unit testing requires Spring or Mockito's reflection helpers.

// Avoid in production code:
@Service
public class UserService {
    @Autowired  // hidden dependency
    private UserRepository repo; // cannot be final
}

Setter Injection for Optional Dependencies

Use setter injection for optional dependencies that have a sensible default — mark the setter @Autowired(required = false).

@Service
public class NotificationService {
    private SmsSender smsSender; // optional
    @Autowired(required = false)
    public void setSmsSender(SmsSender s) { this.smsSender = s; }
}

What Is a Circular Dependency?

A circular dependency occurs when A requires B and B requires A (directly or transitively). Spring detects this at startup and throws BeanCurrentlyInCreationException.

// CIRCULAR:
@Service class A { A(B b) {} }
@Service class B { B(A a) {} }
// Spring throws: BeanCurrentlyInCreationException: Is there an unresolvable circular reference?

Breaking Circularity: Refactor to a Third Service

The cleanest fix is to extract the shared logic into a third service that neither A nor B depends on cyclically.

// Before: A <-> B
// After: A -> C, B -> C (no cycle)
@Service class SharedLogic { ... }
@Service class A { A(SharedLogic s) {} }
@Service class B { B(SharedLogic s) {} }

Breaking Circularity: @Lazy

Annotate one injection point with @Lazy. Spring creates a proxy for B immediately; the real B is instantiated only when A first calls a method on it.

@Service
public class A {
    private final B b;
    public A(@Lazy B b) { this.b = b; } // breaks the cycle
}

Breaking Circularity: ApplicationContext Lookup

Inject ApplicationContext into one bean and look up the other bean lazily via context.getBean() — but this reduces testability and clarity.

@Service
public class A implements ApplicationContextAware {
    private ApplicationContext ctx;
    public void setApplicationContext(ApplicationContext c) { this.ctx = c; }
    public void doWork() { ctx.getBean(B.class).help(); }
}

Setter Injection to Break Circularity

Setter-injected beans are created first (construction succeeds), then dependencies are set. This allows circular dependencies at the cost of mutability.

@Service
public class A {
    private B b;
    @Autowired public void setB(B b) { this.b = b; } // Spring sets after construction
}

spring.main.allow-circular-references (Spring Boot 2.6+)

Spring Boot 2.6+ disallows circular dependencies by default. Enable them explicitly only as a temporary measure while refactoring: spring.main.allow-circular-references=true.

# application.properties (temporary workaround only):
spring.main.allow-circular-references=true

Quick Check

Why is constructor injection preferred over field injection?

Recap

Prefer constructor injection: immutable, testable, explicit. Avoid field injection. Use @Lazy or extract a shared service to break circular dependencies. Spring Boot 2.6+ flags circular refs at startup — treat them as design warnings.

Frequently asked questions

Is the “Constructor Injection and Circular Dependencies” lesson free?

Yes — the full text of “Constructor Injection and Circular Dependencies” is free to read here on the web, and the Java Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Java Academy course, upgrade to CoddyKit PRO.

What will I learn in “Constructor Injection and Circular Dependencies”?

Prefer constructor injection for testability, detect circular dependencies, and break them with @Lazy. You practise Java Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Java Academy?

No prior experience is required. Java Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Constructor Injection and Circular Dependencies” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Java Academy lesson?

Yes. Every Java Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Auto-Configuration and Spring Boot Starters
  2. Application Properties and Profiles
  3. Bean Wiring: @Component, @Service, @Repository
  4. Constructor Injection and Circular Dependencies
← Back to Java Academy