Dependency Injection and Inversion Of Control
Introduction
We have a Food Interface and it is implemented by a Burger Class.
public interface Food{}
public class Burger implements Food{}
These two are used by the Chef class.
public class Chef{
private Food burger;
public Chef(){
burger = new Burger();
}
public void prepareFood(){
// Code
// Prepare the Burger.
}
}
The Chef class is dependent on the Burger class as it cannot work without instantiating the Burger object.
Now let’s imagine another Chef experienced in making Pizza.
public class Pizza implements Food{}
Now to implement Pizza in the Chef class we have to get rid of the Burger or duplicate the code.
public class Chef{
private Food burger;
public Chef(){
pizza = new Pizza();
}
public void prepareFood(){
// Code
// Prepare the Burger.
}
}
public class Chef{
private Food burger;
public Chef(){
burger = new Burger();
pizza = new Pizza();
}
public void prepareBurger(){
// Code
// Prepare the Burger.
}
public void preparePizza(){
// Code
// Prepare the Pizza.
}
}
Spring IOC - Inversion of Control
In a controller, we typically need an object of the service. Previously, we would create the object using the new keyword, and then we had to manage object creation and deletion manually.
With Dependency Injection (DI), we can focus on business logic without worrying about object creation.
Key Concept:
- IOC (Inversion of Control) is the principle
- Dependency Injection is the design pattern that implements IOC
In Spring, we simply inject the object instead of creating it manually.
Three Ways to Implement Dependency Injection
1. Without Dependency Injection (Tight Coupling)
public class Controller{
private Service service;
public void handleRequest(){
service.doSomething();
}
}
public class Service{
public void doSomething(){
System.out.println("doing something");
}
}
2. Constructor Injection
public class Controller{
private Service service;
public Controller(Service service){
// Dependency injected via constructor
this.service = service;
}
public void handleRequest(){
service.doSomething();
}
}
public class Service{
public void doSomething(){
System.out.println("doing something");
}
}
3. Setter Injection
public class Controller{
private Service service;
public void setService(Service service){
// Dependency injected via setter method
this.service = service;
}
public void handleRequest(){
service.doSomething();
}
}
public class Service{
public void doSomething(){
System.out.println("doing something");
}
}
4. Field Injection (Using @Autowired)
This is also known as Loose Coupling - the Spring framework automatically injects dependencies.
public class Controller{
@Autowired
private Service service;
public void handleRequest(){
service.doSomething();
}
}
public class Service{
public void doSomething(){
System.out.println("doing something");
}
}
Summary
- Constructor Injection: Best practice, immutable dependencies
- Setter Injection: Flexible, but can lead to null pointer exceptions
- Field Injection: Convenient but less explicit about dependencies
- Loose Coupling: Spring manages all object creation and lifecycle