What is Spring dependency injection?
Spring Dependency Injection (DI) is a core feature of the Spring Framework that allows you to manage object dependencies without explicitly creating or looking up collaborating objects. It is a specific form of Inversion of Control (IoC), where the framework takes control of instantiating, configuring, and assembling components.
What is Dependency Injection (DI)?
Dependency Injection is a design pattern that allows a class to receive its dependencies from an external source rather than creating them itself. Instead of a component creating its dependencies, it declares what dependencies it needs, and a container (like the Spring IoC container) provides them at runtime.
Spring's Role in DI
The Spring Framework implements Dependency Injection through its Inversion of Control (IoC) container. The Spring IoC container is responsible for instantiating, configuring, and assembling your application's components (beans). It manages the lifecycle of these objects and injects their dependencies when they are created.
By shifting the responsibility of dependency resolution from the application code to the framework, Spring IoC promotes loose coupling, enhances testability, and simplifies application configuration.
Types of Dependency Injection in Spring
Spring primarily supports three types of dependency injection:
1. Constructor Injection
Dependencies are provided as arguments to the class's constructor. This is generally the preferred method as it ensures that the object is in a valid state immediately after construction and that all required dependencies are present. It also helps in creating immutable objects.
@Component
public class MyService {
private final MyRepository repository;
@Autowired // Optional for single constructor since Spring 4.3
public MyService(MyRepository repository) {
this.repository = repository;
}
public void doSomething() {
repository.findData();
}
}
@Component
public class MyRepository {
public void findData() {
System.out.println("Finding data...");
}
}
2. Setter Injection
Dependencies are injected by calling setter methods on your bean after the default no-argument constructor has been called. This method is useful for optional dependencies or when you need to change dependencies at runtime (though less common).
@Component
public class MyService {
private MyRepository repository;
@Autowired
public void setRepository(MyRepository repository) {
this.repository = repository;
}
public void doSomething() {
repository.findData();
}
}
@Component
public class MyRepository {
public void findData() {
System.out.println("Finding data...");
}
}
3. Field Injection
Dependencies are injected directly into instance fields by using annotations like @Autowired directly on the field. While convenient, it is generally discouraged as it makes classes harder to test (without the Spring container) and hides the class's dependencies.
@Component
public class MyService {
@Autowired
private MyRepository repository;
public void doSomething() {
repository.findData();
}
}
@Component
public class MyRepository {
public void findData() {
System.out.println("Finding data...");
}
}
Benefits of Spring DI
- Loose Coupling: Components are less dependent on each other, as the container manages their relationships.
- Enhanced Testability: Easier to unit test components by mocking or substituting dependencies.
- Readability and Maintainability: Business logic is cleaner, as dependency creation is delegated.
- Easier Configuration: Dependencies can be configured externally (e.g., via annotations, XML, Java config) without changing the source code.
- Reusability: Components become more reusable as they are not tied to specific implementations of their dependencies.
- Scalability: Easier to manage complex applications with many interacting components.
How Spring Performs DI
Spring uses various mechanisms to perform DI, primarily through annotations like @Autowired (from Spring), @Inject (from JSR-330, typically used with Spring for broader compatibility), and @Resource (from JSR-250, for resource injection by name). Alternatively, XML configuration or Java-based configuration (using @Configuration and @Bean annotations) can be used to define and wire beans.