[Java Backend Zero to Hello] BÀI 5.3: BEAN & DEPENDENCY INJECTION
📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 5: Spring Framework & Spring Boot | Bài 49/86
BÀI 5.3: BEAN & DEPENDENCY INJECTION
Mục tiêu
- Hiểu sâu về Bean lifecycle
- Các cách inject dependency
- Sử dụng @Qualifier, @Primary
- Lazy initialization
1. BEAN LIFECYCLE
Khởi tạo Bean
↓
Populate Properties (DI)
↓
BeanNameAware.setBeanName()
↓
BeanFactoryAware.setBeanFactory()
↓
ApplicationContextAware.setApplicationContext()
↓
BeanPostProcessor.postProcessBeforeInitialization()
↓
@PostConstruct
↓
InitializingBean.afterPropertiesSet()
↓
Custom init-method
↓
BeanPostProcessor.postProcessAfterInitialization()
↓
Bean sẵn sàng sử dụng
↓
@PreDestroy
↓
DisposableBean.destroy()
↓
Custom destroy-method
2. CÁC CÁCH KHAI BÁO BEAN
2.1 @Component
@Component
public class MyService { }
2.2 @Service, @Repository, @Controller
Đều là @Component với ý nghĩa cụ thể:
@Service- Business logic@Repository- Data access (có thêm exception translation)@Controller- Web controller
2.3 @Configuration + @Bean
@Configuration
public class AppConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return mapper;
}
}
2.4 @Import
@SpringBootApplication
@Import({DatabaseConfig.class, SecurityConfig.class})
public class Application { }
3. DEPENDENCY INJECTION CHI TIẾT
3.1 Constructor Injection (Khuyến nghị)
@Service
public class OrderService {
private final UserRepository userRepository;
private final PaymentService paymentService;
// @Autowired không bắt buộc nếu chỉ có 1 constructor (Spring 4.3+)
public OrderService(UserRepository userRepository, PaymentService paymentService) {
this.userRepository = userRepository;
this.paymentService = paymentService;
}
}
Ưu điểm:
- Dependency bắt buộc
- Field final (immutable)
- Dễ test
- Phát hiện lỗi sớm
3.2 Setter Injection
@Service
public class OrderService {
private UserRepository userRepository;
@Autowired(required = false) // Optional
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
Khi nào dùng: Dependency optional, cần thay đổi sau khi tạo.
3.3 Field Injection (Không khuyến nghị)
@Service
public class OrderService {
@Autowired
private UserRepository userRepository;
}
Nhược điểm:
- Khó test
- Ẩn dependency
- Không thể dùng với final
4. @QUALIFIER VÀ @PRIMARY
4.1 Vấn đề
public interface MessageService {
void send(String message);
}
@Service
public class EmailService implements MessageService { }
@Service
public class SmsService implements MessageService { }
@Service
public class NotificationService {
@Autowired
private MessageService messageService; // ❌ Không biết chọn cái nào
}
4.2 Giải pháp 1: @Qualifier
@Service
public class NotificationService {
private final MessageService messageService;
public NotificationService(@Qualifier("emailService") MessageService messageService) {
this.messageService = messageService;
}
}
4.3 Giải pháp 2: @Primary
@Service
@Primary
public class EmailService implements MessageService { }
@Service
public class SmsService implements MessageService { }
4.4 Giải pháp 3: Tên bean
@Service("emailMessageService")
public class EmailService implements MessageService { }
// Inject theo tên
@Resource(name = "emailMessageService")
private MessageService messageService;
5. LAZY INITIALIZATION
Bean chỉ được tạo khi cần dùng.
5.1 Global
spring:
main:
lazy-initialization: true
5.2 Per Bean
@Component
@Lazy
public class HeavyService { }
5.3 Inject Lazy
@Service
public class MyService {
private final HeavyService heavyService;
public MyService(@Lazy HeavyService heavyService) {
this.heavyService = heavyService;
}
}
6. OPTIONAL DEPENDENCY
@Service
public class MyService {
@Autowired(required = false)
private OptionalService optionalService;
// Hoặc
public MyService(Optional<OptionalService> optionalService) {
this.optionalService = optionalService.orElse(null);
}
}
7. COLLECTION INJECTION
public interface Plugin {
String getName();
}
@Component
public class PluginA implements Plugin { }
@Component
public class PluginB implements Plugin { }
@Service
public class PluginManager {
private final List<Plugin> plugins;
public PluginManager(List<Plugin> plugins) {
this.plugins = plugins; // Tự động inject tất cả Plugin
}
}
8. @ORDER VÀ @PRIORITY
@Component
@Order(1)
public class FirstPlugin implements Plugin { }
@Component
@Order(2)
public class SecondPlugin implements Plugin { }
9. @LOOKUP (Prototype trong Singleton)
Khi bean singleton cần bean prototype:
@Component
public abstract class CommandManager {
public void process() {
Command command = createCommand(); // Mỗi lần tạo mới
command.execute();
}
@Lookup
protected abstract Command createCommand();
}
10. @PROFILE
@Repository
@Profile("dev")
public class DevUserRepository implements UserRepository { }
@Repository
@Profile("prod")
public class ProdUserRepository implements UserRepository { }
11. @CONDITIONAL
@Configuration
public class AppConfig {
@Bean
@ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
public CacheService cacheService() {
return new CacheService();
}
@Bean
@ConditionalOnClass(name = "com.example.SomeClass")
public SomeService someService() {
return new SomeService();
}
}
Các annotation tương tự:
@ConditionalOnBean@ConditionalOnMissingBean@ConditionalOnClass@ConditionalOnProperty
12. BÀI TẬP THỰC HÀNH
Bài 1: Multi-implementation
Tạo interface PaymentService với 2 implementation: CreditCardPayment, MomoPayment. Sử dụng @Primary và @Qualifier.
Bài 2: Plugin System
Tạo hệ thống plugin với collection injection.
Bài 3: Profile
Tạo bean khác nhau cho dev và prod.
13. TÓM TẮT
| Khái niệm | Mô tả |
|---|---|
| @Component | Generic bean |
| @Service, @Repository | Stereotype |
| @Configuration + @Bean | Khai báo bean thủ công |
| Constructor Injection | Khuyến nghị |
| @Qualifier | Chọn bean cụ thể |
| @Primary | Bean ưu tiên |
| @Lazy | Khởi tạo trễ |
| @Profile | Theo môi trường |
| @Conditional | Theo điều kiện |
Bài tiếp theo: 5.4 Spring Data JPA
🧭 Điều Hướng Series
⬅️ Bài trước: BÀI 5.2: SPRING BOOT CƠ BẢN
📋 Lộ trình tổng quan: Xem Toàn Bộ Series
➡️ Bài tiếp theo: BÀI 5.4: SPRING DATA JPA
All rights reserved