How to achieve Dependency Injection in a Spring Boot App
In Spring Boot, initialising a class object involves creating and configuring instances of classes, often managed by the Spring IoC (Inversion of Control) container. There are several ways to initialise objects in Spring Boot, each with its own set of advantages and disadvantages. In this article, we will explore some common approaches on How to achieve Dependency Injection in a Spring Boot App along with real-time examples.
1. Constructor-based Initialisation:
In this approach, dependencies are injected through the constructor.
Pros:
- Explicit dependencies declaration.
- Immutable objects can be created.
Cons:
- Constructor overloading for multiple scenarios.
- Limited flexibility in changing dependencies dynamically.
Example:
public class ProductService {
private final ProductRepository productRepository;
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
// Other methods using productRepository
}
@Service
public class ProductRepository {
// Implementation details
}
2. Setter-based Initialization:
Dependencies are injected using setter methods.
Pros:
- Flexibility to change dependencies at runtime.
- Default constructor for optional dependencies.
Cons:
- Dependencies can be null if not set.
- More boilerplate code.
Example:
public class ProductService {
private ProductRepository productRepository;
public void setProductRepository(ProductRepository productRepository) {
this.productRepository = productRepository;
}
// Other methods using productRepository
}
@Service
public class ProductRepository {
// Implementation details
}
3. Field-based Initialisation:
Dependencies are directly annotated with @Autowired
at the field level.
Pros:
- Concise code.
- Easy to read.
Cons:
- Limited control over the injection process.
- Difficult to unit test.
Example:
public class ProductService {
@Autowired
private ProductRepository productRepository;
// Other methods using productRepository
}
@Service
public class ProductRepository {
// Implementation details
}
4. Constructor Injection with @Autowired:
Dependencies are injected through the constructor with the @Autowired
annotation.
Pros:
- Clean and concise.
- Promotes best practices.
Cons:
- Limited control over injection points.
Example:
public class ProductService {
private final ProductRepository productRepository;
@Autowired
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
// Other methods using productRepository
}
@Service
public class ProductRepository {
// Implementation details
}
Real-time Example:
Consider a simple Spring Boot application for managing products in an e-commerce system. We have a Product
class, a ProductService
for business logic, and a ProductRepository
for data access.
public class Product {
private String name;
private double price;
// Constructors, getters, setters
}
public interface ProductRepository {
List<Product> getAllProducts();
Product getProductById(long id);
void saveProduct(Product product);
void deleteProduct(long id);
}
@Service
public class ProductService {
private final ProductRepository productRepository;
@Autowired
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
public List<Product> getAllProducts() {
return productRepository.getAllProducts();
}
public Product getProductById(long id) {
return productRepository.getProductById(id);
}
public void saveProduct(Product product) {
productRepository.saveProduct(product);
}
public void deleteProduct(long id) {
productRepository.deleteProduct(id);
}
}
In this example, the ProductService
is initialized using constructor injection with the ProductRepository
dependency. The ProductRepository
can be implemented using various data storage technologies (e.g., databases, in-memory storage), and the ProductService
can be easily tested or switched to a different implementation without modifying the service class.
Each approach has its place depending on the specific requirements of your application. Choosing the right initialisation method depends on factors such as code readability, testability, and flexibility in managing dependencies at runtime.
In the last BitsToGigs post we had discussed The Most Useful Spring Boot Annotations all Devs Must Know.
Happy Learning!
Subscribe to the BitsToGigs Newsletter