Why does the Spring team recommend constructor injection over field injection for required dependencies?
answer
- fail-fast startup vs NPE later
- final = immutable + thread-safe
- ctor signature = honest contract, exposes God object
- new Service(mock) — no context
- cycles fail loud vs hidden
basics
~10 sConstructor injection guarantees required dependencies exist at creation, lets fields be final (immutable), makes dependencies explicit, and lets you build the object in tests with plain new — no Spring or reflection needed.
solid answer
~40 sConstructor injection is preferred for required dependencies for several reasons. First, it guarantees the object is never created in a half-initialized state: if a dependency is missing, the container fails fast at startup rather than throwing a NullPointerException later. Second, fields can be final, making the bean immutable and thread-safe by construction. Third, dependencies are explicit in the constructor signature — a class demanding eight constructor args visibly signals it does too much (a design smell field injection hides). Fourth, testability: you can instantiate the class with `new MyService(mockDep)` in a plain unit test without a Spring context or reflection. Field injection defeats all of these: no final, hidden dependencies, and you must use reflection or a container to populate fields in tests. Setter injection remains appropriate for genuinely optional dependencies.
code
java · 20 lines// Constructor injection — unit-testable with plain new, no Spring context
class OrderServiceTest {
@Test
void placesOrder() {
PaymentGateway gateway = mock(PaymentGateway.class);
OrderService service = new OrderService(gateway); // no container needed
service.place(anOrder());
verify(gateway).charge(any());
}
}
// Field injection — same test now needs reflection or a Spring context
class BadServiceTest {
@Test
void placesOrder() {
BadService service = new BadService();
ReflectionTestUtils.setField(service, "gateway", mock(PaymentGateway.class));
// brittle: string field name, breaks on rename
}
}go deeper
Knows constructor injection is recommended but may only cite 'best practice'.
Articulates fail-fast, final/immutability, and testability concretely.
Connects it to SRP, circular-dependency detection, and thread-safety.
Treats the constructor as the class's public contract and a design-pressure signal.
The Spring reference documentation explicitly recommends **constructor injection for mandatory dependencies** and setter injection for optional ones. The reasons: **1. Fail-fast on missing required dependencies.** With constructor injection the container cannot even instantiate the bean unless every parameter resolves. A misconfiguration surfaces as a startup `NoSuchBeanDefinitionException` / `UnsatisfiedDependencyException`, not a runtime `NullPointerException` deep in production. Field injection allows an object to exist with a `null` collaborator. **2. Immutability.** Constructor-assigned fields can be `final`. A `final` reference is set exactly once and never reassigned, which makes the bean **thread-safe** with respect to its dependencies and easier to reason about. Neither setter nor field injection can use `final`. **3. Explicit dependencies / visible design smells.** The constructor signature is the object's honest contract of what it needs. If it grows to many parameters, that's a loud signal the class has too many responsibilities and should be split (**Single Responsibility Principle**). Field injection scatters `@Autowired` across the class and hides the true dependency count, letting a **God object** grow unnoticed. **4. Testability without the container.** A constructor-injected class is trivially unit-tested: ```java var service = new OrderService(mock(PaymentGateway.class)); ``` No `ApplicationContext`, no `@SpringBootTest`, no reflection. Field-injected classes have no way to set private `@Autowired` fields from a plain test except `ReflectionTestUtils.setField(...)` or spinning up a context — slower and more fragile. **5. No circular-dependency hiding.** Constructor injection **fails loudly** on a circular dependency (`BeanCurrentlyInCreationException`), forcing you to fix the design. Field/setter injection can silently paper over cycles (Spring resolves them by setting fields later), leaving a latent design problem. **Counterpoints / when not to.** - **Optional dependencies** → setter injection with `@Autowired(required=false)` or `Optional<T>`/`@Nullable`. - **Legacy frameworks** that require a no-arg constructor may force setter injection. - Historically, unavoidable circular dependencies were sometimes broken by making one side setter-injected — but the modern advice is to redesign instead. **Key APIs.** `@Autowired`, `@Autowired(required=false)`, `UnsatisfiedDependencyException`, `BeanCurrentlyInCreationException`, `ReflectionTestUtils.setField` (test-only workaround for field injection).
- How does constructor injection help expose a class that violates the Single Responsibility Principle?Every dependency must appear as a constructor parameter, so a bloated constructor with many args is an immediately visible signal the class does too much and should be split. Field injection hides this because dependencies are scattered as annotated fields.
- What happens with a circular dependency under constructor vs field injection?Constructor injection fails fast with BeanCurrentlyInCreationException, forcing a redesign. Field/setter injection can silently resolve the cycle by populating fields after construction, hiding the design problem.
saying these in an interview costs you the question
- 'Field injection is cleaner because there's less boilerplate' — ignores testability/immutability costs
- Claiming constructor injection can't handle optional dependencies (it can, via @Nullable/Optional)
- Saying field injection is fine because Spring handles it — misses that it breaks plain unit tests