Interview Prep Zoneby CuriouserLabs

Question 2 of 5 · senior level

Constructor vs setter vs field injection — defend your default and its exceptions.

🎤 Say this first

Default to constructor injection: dependencies are explicit and final (immutable, thread-safe), the object can never exist half-wired, it works without a container in unit tests, circular dependencies fail fast instead of hiding, and a bloated constructor is a smell-detector for classes doing too much. Field injection hides dependencies, requires reflection/container to test, and can't be final. Setter injection remains for genuinely optional or reconfigurable-at-runtime collaborators.

The full picture

The recommended shape
@Service
public class OrderService {
    private final OrderRepository repo;        // final — visible in any thread
    private final PaymentPort payments;        //         safely after construction

    // Single constructor: @Autowired not even needed (Spring 4.3+)
    public OrderService(OrderRepository repo, PaymentPort payments) {
        this.repo = repo;
        this.payments = payments;
    }
}
// Unit test: new OrderService(fakeRepo, fakePayments) — no Spring, no reflection.
CriterionConstructorSetterField
Immutability / final fields
Honest API (deps visible)partly❌ hidden
Testable without container✅ plain new✅ awkward❌ reflection
Fails fast on missing dep✅ at construction❌ NPE later❌ NPE later
Circular dependency behaviorfails at startup (good!)may 'work' silentlymay 'work' silently

🔄 Likely follow-up questions

  • Why does the Spring team itself recommend constructor injection?
  • When is @Lazy on a constructor parameter justified?
  • How does constructor injection interact with @Value and configuration properties?