Detailed explanation
Dependency injection (DI) means an object receives its collaborators instead of creating them itself. In Spring, the IoC container creates and manages beans, resolves their dependencies, and supplies those dependencies during bean creation. The three commonly discussed forms are constructor injection, setter injection, and field injection. They differ in when dependencies become available, whether required dependencies are explicit, and how easily a class can be instantiated outside Spring. Interviewers expect you to explain these trade-offs, not just list annotations.
Constructor injection supplies dependencies through constructor parameters and is generally preferred for required collaborators. In the OrderService example, Spring resolves a PaymentService bean and passes it to the constructor before the OrderService instance is fully constructed. The dependency can be stored in a final field, preventing reassignment; this does not make the entire object immutable. Since Spring 4.3, @Autowired is unnecessary when a bean class has only one constructor. Constructor injection also makes unit testing straightforward: create OrderService with a mock or stub without starting the Spring container.
Setter injection supplies dependencies through setter methods, typically annotated with @Autowired, after the object has been constructed. It suits optional dependencies or collaborators that genuinely need replacement, but the class must behave safely when an optional dependency is absent. Setter injection is not automatically optional: @Autowired is required by default, so optional behaviour must be configured explicitly, for example with @Autowired(required = false). Because construction happens first, a setter-injected dependency is unavailable inside the constructor, and mutable configuration can allow incomplete object states.
Field injection places @Autowired directly on a field. Spring constructs the object and then injects the dependency, using reflection when necessary, including for private fields. This is concise but hides dependencies from the constructor and complicates plain unit tests because callers cannot supply them through the normal construction API. For all three forms, ambiguous candidates may require @Qualifier or @Primary. Constructor injection also exposes constructor-based circular dependencies, which usually indicate that responsibilities should be redesigned rather than concealed with another injection style.
Key points to remember
- Constructor injection is preferred for required dependencies and supports final dependency fields.
- A single constructor does not need @Autowired in Spring 4.3 and later.
- Setter injection happens after construction and suits explicitly configured optional dependencies.
- Field injection is concise but hides dependencies and complicates testing without Spring.
- Spring resolves dependency candidates by type; @Qualifier or @Primary can disambiguate multiple beans.
Common mistakes candidates make
- Claiming that final dependency fields make the entire bean immutable.
- Assuming setter injection is optional by default despite @Autowired being required by default.
- Saying field injection makes unit testing impossible rather than explaining the extra setup it requires.
Likely follow-up questions
What happens if two beans implement PaymentService?
A single-valued dependency needs an unambiguous candidate; otherwise Spring can fail with NoUniqueBeanDefinitionException. Use @Qualifier to select a particular bean or @Primary to designate the preferred candidate.
How would you represent an optional dependency?
You can use an @Autowired(required = false) setter and ensure the class works when that setter is not called. Constructor injection can also express optionality with Optional<PaymentService>, so optional dependencies do not always require setters.
Can constructor injection resolve a circular dependency?
A direct constructor cycle, where A requires B and B requires A, cannot be resolved through normal eager construction. Prefer redesigning the responsibilities; lazy proxies or provider-based lookup can defer resolution but should not replace sound design.
Interview tip: Name the three forms, recommend constructor injection for required dependencies, and use OrderService to contrast construction-time injection with post-construction injection.
