| Constructor | Setter | Field | |
|---|---|---|---|
| Cú pháp | Constructor param | @Autowired trên setter | @Autowired trên field |
| Immutability | ✅ final field | ❌ | ❌ |
| Mandatory dep | ✅ Rõ ràng | ❌ Optional mặc định | ❌ |
| Testability | ✅ Tốt nhất | ✅ Tốt | ⚠ Cần reflection |
| Circular dep | Phát hiện sớm | Có thể bỏ qua | Có thể bỏ qua |
java
// Constructor injection (RECOMMENDED)
@Service
class OrderService {
private final OrderRepository repo; // final!
private final PaymentService payment;
// Spring tự detect nếu chỉ có 1 constructor (Spring 4.3+)
OrderService(OrderRepository repo, PaymentService payment) {
this.repo = repo;
this.payment = payment;
}
}
// Setter injection — optional dependency
@Service
class ReportService {
private MetricService metrics; // optional
@Autowired(required = false)
void setMetrics(MetricService metrics) {
this.metrics = metrics;
}
}
// Field injection — KHÔNG khuyến nghị
@Service
class BadService {
@Autowired OrderRepository repo; // không test được dễ dàng
}Tại sao constructor injection tốt nhất:
1. final field → immutable, thread-safe.
2. Bắt buộc dependency → null-safe.
3. Test không cần Spring context — new OrderService(mockRepo, mockPayment).
4. Circular dependency phát hiện ngay khi startup.
| Constructor | Setter | Field | |
|---|---|---|---|
| Syntax | Constructor param | @Autowired on setter | @Autowired on field |
| Immutability | ✅ final field | ❌ | ❌ |
| Mandatory dep | ✅ Explicit | ❌ Optional by default | ❌ |
| Testability | ✅ Best | ✅ Good | ⚠ Requires reflection |
| Circular dep | Detected early | Can be hidden | Can be hidden |
java
// Constructor injection (RECOMMENDED)
@Service
class OrderService {
private final OrderRepository repo; // final!
private final PaymentService payment;
// Spring auto-detects if there is only one constructor (Spring 4.3+)
OrderService(OrderRepository repo, PaymentService payment) {
this.repo = repo;
this.payment = payment;
}
}
// Setter injection — optional dependency
@Service
class ReportService {
private MetricService metrics;
@Autowired(required = false)
void setMetrics(MetricService metrics) {
this.metrics = metrics;
}
}
// Field injection — NOT recommended
@Service
class BadService {
@Autowired OrderRepository repo; // hard to test without Spring
}Why constructor injection is best:
1. final field → immutable, thread-safe.
2. Mandatory dependency → null-safe.
3. Test without Spring context — new OrderService(mockRepo, mockPayment).
4. Circular dependencies detected immediately at startup.