Dependency Injection Pattern in Java
Learn Dependency Injection (DI) in Java with Constructor Injection, Setter Injection, Field Injection, Spring IoC Container, Bean Lifecycle, UML diagrams, enterprise examples, and interview questions.
What You Will Learn
- What is Dependency Injection?
- Why DI is Needed
- Tight Coupling vs Loose Coupling
- Types of Dependency Injection
- Constructor Injection
- Setter Injection
- Field Injection
- Spring IoC Container
- Enterprise Use Cases
- Benefits and Limitations
- Interview Questions
Introduction
Almost every enterprise application contains dependencies.
Example:
OrderController
↓
OrderService
↓
OrderRepository
↓
Database
Without Dependency Injection:
public class OrderService {
private OrderRepository repository =
new OrderRepository();
}
Problems:
Tightly Coupled
Difficult Testing
Hard Maintenance
Poor Flexibility
Dependency Injection solves this problem.
What is Dependency Injection?
Dependency Injection is a design pattern where dependencies are provided from outside instead of being created inside the class.
Instead of:
OrderRepository repository =
new OrderRepository();
Use:
OrderService(
OrderRepository repository)
Dependency comes from outside.
Purpose of Dependency Injection
Primary Goal:
Loose Coupling
Better Testability
Better Maintainability
Real World Analogy
Think about a car.
Without DI:
Car Builds Its Own Engine
With DI:
Engine Is Provided
↓
Car Uses Engine
Car doesn't care who created the engine.
Problem Without DI
flowchart LR
A[OrderService]
B[OrderRepository]
A --> B
OrderService creates repository itself.
Strong dependency.
Solution With DI
flowchart LR
A[Spring Container]
B[OrderService]
C[OrderRepository]
A --> B
A --> C
Container injects dependencies.
Dependency Injection Architecture
flowchart LR
A[Client]
B[IoC Container]
C[Service]
D[Repository]
A --> B
B --> C
B --> D
What Is A Dependency?
Example:
public class OrderService {
private OrderRepository repository;
}
Dependency:
OrderRepository
because OrderService depends on it.
Tight Coupling Example
public class OrderService {
private OrderRepository repository =
new OrderRepository();
public void createOrder() {
repository.save();
}
}
Problems:
Hard To Replace
Hard To Mock
Hard To Test
Loose Coupling Example
public class OrderService {
private OrderRepository repository;
public OrderService(
OrderRepository repository) {
this.repository = repository;
}
}
Now dependency is injected.
Types of Dependency Injection
Constructor Injection
Setter Injection
Field Injection
Constructor Injection
Recommended approach.
Example
public class OrderService {
private final OrderRepository repository;
public OrderService(
OrderRepository repository) {
this.repository = repository;
}
}
Flow
flowchart LR
A[Container]
B[Repository]
C[OrderService]
A --> B
B --> C
Benefits
Immutable Objects
Easy Testing
Mandatory Dependencies
Setter Injection
Dependency injected using setter method.
Example
public class OrderService {
private OrderRepository repository;
public void setRepository(
OrderRepository repository) {
this.repository = repository;
}
}
Flow
flowchart LR
A[Container]
B[Create Object]
C[Call Setter]
A --> B
B --> C
Benefits
Optional Dependencies
Runtime Updates
Field Injection
Common but not recommended.
Example
@Service
public class OrderService {
@Autowired
private OrderRepository repository;
}
Issues
Hard Testing
Reflection Based
Hidden Dependencies
Comparison
| Feature | Constructor | Setter | Field |
|---|---|---|---|
| Recommended | Yes | Sometimes | No |
| Testability | Excellent | Good | Poor |
| Immutability | Yes | No | No |
| Mandatory Dependency | Yes | No | No |
Inversion Of Control (IoC)
Dependency Injection is implemented using:
IoC Container
What Is IoC?
Traditional:
Application Controls Objects
IoC:
Container Controls Objects
IoC Flow
flowchart LR
A[Application]
B[IoC Container]
C[Beans]
A --> B
B --> C
Spring IoC Container
Spring creates:
Beans
Dependencies
Object Lifecycle
automatically.
Bean Creation Flow
flowchart LR
A[Application Start]
B[Scan Components]
C[Create Beans]
D[Inject Dependencies]
E[Ready]
A --> B
B --> C
C --> D
D --> E
Spring Example
Repository:
@Repository
public class OrderRepository {
}
Service:
@Service
public class OrderService {
private final OrderRepository repository;
public OrderService(
OrderRepository repository) {
this.repository = repository;
}
}
Controller:
@RestController
public class OrderController {
private final OrderService service;
public OrderController(
OrderService service) {
this.service = service;
}
}
Request Flow
flowchart LR
A[Controller]
B[Service]
C[Repository]
D[Database]
A --> B
B --> C
C --> D
Bean Lifecycle
Create Bean
↓
Inject Dependencies
↓
Initialize
↓
Use Bean
↓
Destroy Bean
Bean Lifecycle Flow
flowchart LR
A[Create]
B[Inject]
C[Initialize]
D[Use]
E[Destroy]
A --> B
B --> C
C --> D
D --> E
Banking Example
Fund Transfer System.
Dependencies:
TransferController
↓
TransferService
↓
AccountRepository
↓
Database
Banking Architecture
flowchart LR
A[Transfer Controller]
B[Transfer Service]
C[Account Repository]
D[Database]
A --> B
B --> C
C --> D
Insurance Example
Claim Processing System.
Dependencies:
ClaimController
↓
ClaimService
↓
ClaimRepository
All injected by Spring.
Insurance Flow
flowchart LR
A[Claim Controller]
B[Claim Service]
C[Claim Repository]
A --> B
B --> C
Unit Testing Example
Without DI:
OrderService service =
new OrderService();
Difficult to mock.
With DI:
OrderRepository mockRepo =
Mockito.mock(
OrderRepository.class);
OrderService service =
new OrderService(
mockRepo);
Easy testing.
Testing Architecture
flowchart LR
A[Test]
B[Mock Repository]
C[Service]
A --> B
B --> C
Enterprise Examples
Banking
Fund Transfer
Loan Processing
Account Management
Insurance
Claims
Policy Management
Premium Processing
Retail
Order Processing
Inventory Tracking
Healthcare
Patient Records
Appointment Systems
Spring Annotations
Common DI annotations:
@Component
@Service
@Repository
@Controller
@RestController
@Autowired
Benefits
✅ Loose Coupling
✅ Better Testing
✅ Easier Maintenance
✅ Flexible Architecture
✅ Improved Scalability
✅ Follows SOLID Principles
Limitations
❌ More Initial Setup
❌ Learning Curve
❌ Container Dependency
When To Use
Use DI when:
- Building Enterprise Applications
- Using Spring Framework
- Large Team Development
- Testability Is Important
When Not To Use
Avoid when:
- Small Utility Programs
- Simple Standalone Applications
Dependency Injection vs Factory Pattern
| Feature | Dependency Injection | Factory |
|---|---|---|
| Creates Objects | Container | Factory |
| Dependency Management | Yes | No |
| Spring Usage | Very High | Moderate |
Dependency Injection vs Service Locator
| Feature | DI | Service Locator |
|---|---|---|
| Dependency Visibility | Explicit | Hidden |
| Testability | High | Medium |
| Recommended | Yes | No |
Real Enterprise Architecture
flowchart LR
A[Controller]
B[Service]
C[Repository]
D[Database]
A --> B
B --> C
C --> D
Interview Questions
What is Dependency Injection?
A design pattern where dependencies are provided externally instead of being created inside a class.
What is IoC?
Inversion of Control.
Container manages object creation.
Types of DI?
Constructor Injection
Setter Injection
Field Injection
Recommended Injection Type?
Constructor Injection
Why Constructor Injection?
Immutable
Testable
Mandatory Dependencies
Spring Container Example?
ApplicationContext
Main Benefit?
Loose coupling and improved testability.
Key Takeaways
- Dependency Injection is one of the most important enterprise patterns.
- Promotes loose coupling.
- Makes testing easier.
- Implemented by Spring IoC Container.
- Constructor Injection is the preferred approach.
- Foundation of Spring Framework architecture.
- Used in Banking, Insurance, Retail, Healthcare, and almost every modern Java application.