Dependency Injection (DI) is a powerful design pattern in Java that promotes loose coupling, testability, and modularity by allowing objects to receive their dependencies from an external source, rather than creating them internally.
It is a core concept in frameworks like Spring, and mastering it is essential for writing scalable and maintainable Java applications.
1. What Is Dependency Injection?
Dependency Injection is a design pattern where dependencies (objects) are injected into a class, instead of the class creating them directly.
Example of tight coupling:
java
class Car {
Engine engine = new Engine(); // tightly coupled
}
With DI:
java
class Car {
private final Engine engine;
public Car(Engine engine) {
this.engine = engine; // dependency is injected
}
}
2. Types of Dependency Injection in Java
- Constructor Injection
- Setter Injection
- Field Injection (discouraged outside of frameworks)
3. Why Use DI?
- Promotes loose coupling
- Makes code easier to test
- Improves flexibility and maintainability
- Encourages use of interfaces and abstraction
4. DI in Plain Java (Manual Example)
java
class Service {
void serve() { System.out.println("Serving..."); }
}
class Client {
private final Service service;
public Client(Service service) {
this.service = service;
}
void doWork() {
service.serve();
}
}
5. Dependency Injection with Spring Framework
Spring uses an IoC (Inversion of Control) container to manage beans and inject dependencies automatically.
java
@Component
public class Engine {}
@Component
public class Car {
private final Engine engine;
@Autowired
public Car(Engine engine) {
this.engine = engine;
}
}
Spring Boot automatically scans and wires components using annotations like:
@Component
@Service
@Repository
@Autowired
@Configuration
/ @Bean
6. Constructor vs Setter Injection in Spring
Constructor Injection (Recommended):
java
@Autowired
public Car(Engine engine) { this.engine = engine; }
Setter Injection (Optional or circular dependencies):
java
@Autowired
public void setEngine(Engine engine) { this.engine = engine; }
7. Field Injection (Less Preferred)
java
@Autowired
private Engine engine;
Although shorter, it's harder to test and not recommended for new code.
8. Java Configuration with @Bean
java
@Configuration
public class AppConfig {
@Bean
public Engine engine() {
return new Engine();
}
@Bean
public Car car() {
return new Car(engine());
}
}
9. Dependency Injection vs Service Locator
FeatureDependency InjectionService LocatorPush modelYesNo (pull)Encourages testingYesNot idealDecouples logicYesPartially
10. Conclusion
Dependency Injection is a fundamental pattern for building clean, testable Java applications. Frameworks like Spring make it incredibly powerful and easy to use. By mastering DI, you unlock the ability to write flexible, loosely coupled code that scales with your project.