Spring Dependency Injection

Spring Dependency Injection (DI) lets a Spring application receive the objects it depends on instead of constructing those dependencies itself. This reduces coupling and makes components easier to test, configure, and replace.

In Spring, a dependency is an object that another object needs to perform its work. DI means the dependent class declares what it needs, while the Spring container creates suitable objects and supplies those dependencies.

Spring commonly manages application objects as beans. A class can be registered as a bean with annotations such as `@Component`, `@Service`, or `@Repository`, and Spring can discover it through component scanning. The container then resolves dependencies between managed beans.

Constructor injection is generally the clearest form of DI: dependencies are declared as constructor parameters. With a single constructor, Spring can use it for injection without requiring `@Autowired`; this also makes required dependencies explicit and allows the object to be created with all required state.

DI separates object construction from business logic. For example, a service can depend on a repository interface rather than directly creating a repository implementation with `new`. Spring can then provide the configured implementation, while a test can provide a stub or mock instead.

Dependency resolution happens when the Spring container creates and configures beans. If multiple beans match the required type, additional configuration such as `@Qualifier` or `@Primary` may be needed to identify which bean should be injected.

Key points

Interview questions

What is Dependency Injection in Spring?

Dependency Injection means a class receives the objects it needs from an external mechanism rather than creating those objects itself. In Spring, the application context acts as the container that creates and wires managed beans. This reduces coupling between classes and makes implementations easier to replace. It also makes unit testing easier because a test can supply a mock or alternative implementation.

How does constructor injection work in Spring?

A class declares its required dependencies as constructor parameters. When Spring creates that class as a bean, it resolves those parameters from beans in the application context and passes them to the constructor. With one constructor, Spring can use it for injection without requiring `@Autowired`. Constructor injection also allows required dependencies to be stored in `final` fields.

What is the difference between `@Component`, `@Service`, and `@Repository`?

All three are stereotype annotations that can make a class eligible for component scanning and therefore registration as a Spring bean. `@Component` is the general-purpose stereotype. `@Service` communicates that the class represents service-layer logic, while `@Repository` identifies persistence-related components and carries additional Spring semantics for exception translation in supported repository scenarios. The main distinction is their intended role and semantics rather than the basic mechanism of bean discovery.

What happens if two beans implement the same interface and you inject that interface?

Spring may find multiple candidates for the same dependency type and cannot choose one automatically. The application can disambiguate the injection with `@Qualifier`, naming the desired bean explicitly. Alternatively, `@Primary` can mark one candidate as the default when several candidates are available. The choice depends on whether one implementation should generally be preferred or the injection point should explicitly select an implementation.

Why is constructor injection often preferred over field injection?

Constructor injection makes required dependencies visible in the class's public construction contract. It allows dependencies to be stored in `final` fields and prevents a normally constructed object from existing without required collaborators. It also makes unit tests straightforward because the test can instantiate the class directly with test doubles. Field injection hides dependencies and relies more heavily on container-based setup.