Dependency Injection is a design pattern that allows an object's dependencies to be supplied externally rather than being created within the object itself. This external supply of dependencies enables loose coupling and makes code more flexible and testable. IoC is the broader principle that underlies DI, where the control of object creation and lifecycle management is handed over to a container or framework.
In traditional programming, objects create their dependencies directly, leading to tightly coupled code:
public class OrderService { private ProductService productService = new ProductService(); public void placeOrder() { productService.processOrder(); } }
Here, OrderService
is tightly coupled to ProductService
. Any change to ProductService
may require modifications to OrderService
. With DI, the dependency is injected externally:
public class OrderService { private final ProductService productService; public OrderService(ProductService productService) { this.productService = productService; } public void placeOrder() { productService.processOrder(); } }
Spring Boot provides three main types of dependency injection:
- Constructor Injection: Dependencies are injected via the class constructor.
- Setter Injection: Dependencies are injected through setter methods.
- Field Injection: Dependencies are injected directly into fields using annotations.
Spring Boot's IoC container manages dependency injection using annotations like @Component
, @Service
, @Repository
, and @Controller
. Let's explore a simple example:
Create a service class annotated with @Service
:
import org.springframework.stereotype.Service; @Service public class ProductService { public void processOrder() { System.out.println("Order processed by ProductService"); } }
Create another class, OrderService
, that depends on ProductService
:
import org.springframework.stereotype.Service; @Service public class OrderService { private final ProductService productService; public OrderService(ProductService productService) { this.productService = productService; } public void placeOrder() { productService.processOrder(); } }
Spring Boot automatically resolves the dependency and injects ProductService
into OrderService
. This is made possible by Spring Boot's IoC container, which scans for components and manages their lifecycle.
To demonstrate setter injection, modify the OrderService
class:
import org.springframework.stereotype.Service; @Service public class OrderService { private ProductService productService; public void setProductService(ProductService productService) { this.productService = productService; } public void placeOrder() { productService.processOrder(); } }
Field injection is even more concise, using the @Autowired
annotation:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class OrderService { @Autowired private ProductService productService; public void placeOrder() { productService.processOrder(); } }
Although field injection is convenient, constructor injection is generally recommended because it ensures immutability and makes testing easier.
Testing is a crucial aspect of application development. With DI, you can easily inject mock dependencies for testing purposes. Here's an example using JUnit:
import org.junit.jupiter.api.Test; import static org.mockito.Mockito.*; public class OrderServiceTest { @Test void testPlaceOrder() { ProductService mockProductService = mock(ProductService.class); OrderService orderService = new OrderService(mockProductService); orderService.placeOrder(); verify(mockProductService).processOrder(); } }
Spring Boot's DI and IoC features make application development more streamlined and maintainable. By allowing the framework to manage dependencies, you can focus on building functionality rather than handling object creation and lifecycle management. These principles are foundational to modern software design and are extensively used in microservices architectures.
In the next article, we will explore Spring Boot Actuator, a powerful tool for monitoring and managing your applications. Stay tuned!