Spring creates objects (beans) and injects their dependencies, so code depends on abstractions, not manual wiring. Constructor injection makes dependencies explicit, supports immutability (`final`), and is easiest to test.
@Service
class UserService {
private final UserRepository repo;
UserService(UserRepository repo) {
this.repo = repo;
}
}
Advanced answer
Deep dive
Dependency Injection (DI) is a pattern where an external container (Spring) constructs objects and provides their dependencies. In Spring, this is handled by the IoC container (ApplicationContext): it instantiates beans, resolves wiring, manages lifecycle, and applies cross‑cutting features (AOP, transactions, security).
Constructor injection is preferred because:
**Dependencies are explicit**: if it compiles, required dependencies are provided.
**Immutability**: fields can be `final` and safely initialized once.
**Testability**: you can `new` the class in unit tests without Spring.
**Fail fast**: missing beans fail at startup, not at runtime.
Practical patterns
Use constructor injection for required dependencies.
For optional dependencies, prefer `Optional<T>` / `ObjectProvider<T>` or split responsibilities.
Watch constructor size: many dependencies often indicates a class doing too much.
Example
@Service
class UserService {
private final UserRepository repo;
private final Clock clock;
UserService(UserRepository repo, Clock clock) {
this.repo = repo;
this.clock = clock;
}
}