Constructor Injection vs. Field Injection: Lessons from a Code Review

Recently, while reviewing a peer’s code, I noticed they had used @AllArgsConstructor instead of the more familiar @Autowired annotation. It sparked a thought: what are the real advantages and drawbacks of constructor injection compared to field injection, and how does this tie into immutability and singleton design?

Why Constructor Injection (@AllArgsConstructor) Can Be Better

Article content

  • Immutability: Dependencies are set once at object creation. No setter methods, no accidental reassignment.
  • Clarity: You immediately see what dependencies a class requires by looking at its constructor.
  • Testability: Easier to write unit tests because you can pass mocks directly into the constructor.
  • Avoids Reflection Pitfalls: Unlike field injection, constructor injection doesn’t rely on reflection, which can be harder to debug.

Drawbacks of Constructor Injection

  • Boilerplate: Without Lombok’s @AllArgsConstructor, constructors can get verbose when many dependencies exist.
  • Circular Dependencies: If two beans depend on each other, constructor injection can cause initialization issues. However a good design does not have this issue
  • Rigidness: Once constructed, dependencies cannot be swapped out — flexibility is reduced compared to setter injection.

Field Injection (@Autowired) in Comparison

Article content

  • Simplicity: Quick to write, especially for small projects.
  • Less Boilerplate: No need for constructors.
  • But… Dependencies are hidden, harder to test, and fields can be reassigned — breaking immutability.

The Immutability Challenge in Singleton Beans

In theory, constructor injection promotes immutability. But in practice, immutability isn’t always achievable when:

  • Singleton Scope: Spring creates a single instance of the bean. If other application components hold references and mutate shared state, immutability is broken.
  • Mutable Dependencies: Even if your class is immutable, injected dependencies may expose mutable state (e.g., collections, caches).
  • Cross‑Component Usage: When multiple components use the same singleton, shared mutable objects can lead to side effects.
  • This means immutability is only guaranteed if both your class and its dependencies are designed to be immutable. Otherwise, singleton scope can undermine the intent.

Example

Article content

Even if injected via constructor, CacheService is mutable and shared — immutability is lost.

My Takeaway

Constructor injection (@AllArgsConstructor) is generally the cleaner, safer choice. It enforces dependency visibility and supports immutability. But immutability in Spring applications isn’t automatic — singleton beans and mutable dependencies can still undermine it. The real key is designing dependencies carefully and being mindful of shared state.

 

To view or add a comment, sign in

Others also viewed

Explore content categories