@Conditional Bean: Nền Tảng Của Auto-Configuration
Tìm hiểu cơ chế đánh giá điều kiện của @Conditional tại pha BeanDefinition, họ annotation @ConditionalOn* và nguyên lý back off trong Spring Boot.
TL;DR: @Conditional là annotation gắn vào phương thức @Bean hoặc lớp @Configuration, yêu cầu container đánh giá một điều kiện trước khi đăng ký BeanDefinition vào beanDefinitionMap. Điều kiện này được thẩm định trong pha BeanFactoryPostProcessor — trước khi bất kỳ đối tượng bean nào được tạo. Nếu điều kiện sai, bean hoàn toàn không tồn tại trong container. Họ annotation @ConditionalOn* là các bản mở rộng của cơ chế này, giúp Spring Boot thực hiện nguyên tắc "back off" — chỉ cung cấp bean mặc định khi lập trình viên chưa tự định nghĩa.
Hiểu rõ @Conditional giúp bạn nắm vững cơ chế vận hành tự động của Spring Boot và chủ động xây dựng các thư viện starter dùng chung cho toàn bộ dự án.
1. @Conditional giải quyết bài toán đăng ký linh hoạt nào?
Hãy hình dung một thư viện kết nối cơ sở dữ liệu: nếu thư viện Redis có mặt trên classpath, hệ thống sẽ tạo bean CacheManager dùng Redis; nếu không, hệ thống tự động lùi về giải pháp dùng bộ nhớ trong (In-memory). Nếu không có cơ chế điều kiện ở tầng container, bạn sẽ phải viết hàng loạt lệnh if/else thủ công phức tạp:
@Configuration
public class CacheConfig {
@Bean
@ConditionalOnClass(name = "org.springframework.data.redis.core.RedisTemplate")
public CacheManager redisCacheManager(RedisConnectionFactory factory) {
return new RedisCacheManager(factory);
}
@Bean
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager fallbackCacheManager() {
return new ConcurrentMapCacheManager("default");
}
}
Nếu Redis không có mặt trên classpath, phương thức redisCacheManager bị bỏ qua và fallbackCacheManager được kích hoạt. Ngược lại, nếu Redis tồn tại, redisCacheManager được nạp vào container khiến fallbackCacheManager tự động rút lui.
2. Quá trình đánh giá Condition diễn ra ở giai đoạn nào?
Nhiều lập trình viên nhầm lẫn rằng @Conditional kiểm tra điều kiện khi đối tượng bean được khởi tạo. Thực tế, @Conditional được đánh giá ngay tại pha đăng ký BeanDefinition:

Bộ xử lý ConfigurationClassPostProcessor (một BeanFactoryPostProcessor) thẩm định điều kiện trước khi bất kỳ constructor nào được gọi:
- Pha đăng ký: Kiểm tra
Condition.matches(). Nếu trả vềtrue,BeanDefinitionđược thêm vàobeanDefinitionMap. Nếufalse, bean bị bỏ qua ngay lập tức. - Pha tạo đối tượng: Container chỉ duyệt qua những
BeanDefinitionđã được chấp thuận để thực hiện tạo instance.
3. Tự viết bộ kiểm tra điều kiện với interface Condition ra sao?
Mọi biến thể của @Conditional đều bắt nguồn từ interface functional cốt lõi:
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
Đối tượng ConditionContext cung cấp toàn quyền tra cứu:
getBeanFactory(): Tra cứu các bean vàBeanDefinitionhiện có.getEnvironment(): Đọc các thuộc tính cấu hình từapplication.propertieshoặc biến môi trường.getClassLoader(): Kiểm tra sự tồn tại của các class trong classpath.
Ví dụ tạo một điều kiện chỉ kích hoạt bean khi ứng dụng chạy trên hệ điều hành Linux:
public class OnLinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String os = System.getProperty("os.name", "").toLowerCase();
return os.contains("linux");
}
}
@Configuration
public class StorageConfig {
@Bean
@Conditional(OnLinuxCondition.class)
public FileWatcher linuxWatcher() {
return new LinuxFileWatcher();
}
}
4. Họ annotation @ConditionalOn* gồm những biến thể quan trọng nào?
Spring Boot cung cấp sẵn các annotation chuyên biệt đáp ứng hầu hết nhu cầu cấu hình:
| Annotation | Điều kiện kích hoạt | Tình huống sử dụng |
|---|---|---|
@ConditionalOnClass | Class chỉ định có mặt trong classpath | Tự bật tính năng khi người dùng thêm starter JAR |
@ConditionalOnMissingClass | Class chỉ định vắng mặt trong classpath | Cung cấp phương án dự phòng khi thiếu driver |
@ConditionalOnBean | Bean kiểu chỉ định đã tồn tại trong container | Định nghĩa bean phụ thuộc vào một bean khác |
@ConditionalOnMissingBean | Chưa có bean nào thuộc kiểu này trong container | Cơ chế cung cấp cấu hình mặc định (Back-off) |
@ConditionalOnProperty | Thuộc tính cấu hình khớp với giá trị mong muốn | Bật tắt tính năng bằng Feature Flag |
@ConditionalOnWebApplication | Ứng dụng đang chạy trong môi trường Web | Khai báo các bean dành riêng cho tầng HTTP |
Ví dụ sử dụng @ConditionalOnProperty làm feature flag:
@Bean
@ConditionalOnProperty(
name = "app.notification.sms.enabled",
havingValue = "true",
matchIfMissing = false // Bo qua neu khong khai bao thuoc tinh
)
public SmsSender smsSender() {
return new TwilioSmsSender();
}
5. Áp dụng @Conditional ở cấp độ Class mang lại lợi ích gì?
Bạn có thể đặt @Conditional lên trên cả lớp @Configuration. Khi đó, toàn bộ các phương thức @Bean bên trong lớp sẽ bị bỏ qua nếu điều kiện của lớp không thỏa mãn:
@Configuration
@ConditionalOnClass(name = "org.apache.kafka.clients.producer.Producer")
public class KafkaAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public KafkaProducer<String, String> kafkaProducer() {
return new DefaultKafkaProducer();
}
}
Mô hình này giúp mã nguồn gọn gàng: kiểm tra sự tồn tại của thư viện ở cấp độ lớp, và cho phép người dùng tùy biến từng bean ở cấp độ phương thức.
6. Những cạm bẫy thứ tự phổ biến khi dùng @ConditionalOnMissingBean?

Cạm bẫy lớn nhất là thứ tự thẩm định cấu hình:
- Nếu cấu hình mặc định của thư viện chạy trước cấu hình của người dùng,
@ConditionalOnMissingBeansẽ thấy container chưa có bean và lập tức đăng ký bean mặc định. - Khi cấu hình của người dùng chạy sau, container sẽ xuất hiện 2 bean cùng kiểu, dẫn tới lỗi
NoUniqueBeanDefinitionException.
Để khắc phục trong các starter tự viết, luôn sử dụng @AutoConfigureBefore hoặc @AutoConfigureAfter để đảm bảo cấu hình của người dùng luôn được xử lý trước cấu hình mặc định.
7. Nguyên lý Back-Off hoạt động thế nào trong Spring Boot Auto-configuration?
Nguyên lý cốt lõi của Spring Boot là: "Cung cấp mặc định thông minh nhưng luôn sẵn sàng rút lui khi lập trình viên có lựa chọn riêng".
Luồng hoạt động diễn ra như sau:
- Spring Boot quét classpath (
@ConditionalOnClass). Nếu có thư viện H2 Database, nó chuẩn bị cấu hình DataSource in-memory. - Nó kiểm tra xem người dùng đã tự định nghĩa
DataSourcechưa (@ConditionalOnMissingBean). - Nếu người dùng đã khai báo bean kết nối PostgreSQL trong mã nguồn của họ, Spring Boot nhận thấy bean đã có trong
beanDefinitionMapvà tự động rút lui hoàn toàn.
Tóm tắt
@Conditionalthẩm định điều kiện ở pha đăng kýBeanDefinitioncủaBeanFactoryPostProcessor, trước khi bất kỳ bean nào được tạo.- Giao diện
Conditioncung cấpConditionContextđể truy xuất môi trường, classpath và danh sách bean đã đăng ký. @ConditionalOnMissingBeanlà nền tảng cho cơ chế "back off" của Spring Boot, giúp người dùng dễ dàng ghi đè cấu hình mặc định.- Đặt điều kiện ở cấp độ
@Configurationgiúp bảo vệ toàn bộ lớp cấu hình không bị nạp khi thiếu thư viện ngoài. - Thứ tự thẩm định quyết định tính chính xác của các điều kiện phụ thuộc; cần kiểm soát thứ tự bằng
@AutoConfigureAfter.
Tự kiểm tra
- Q1Điều kiện trong annotation @Conditional được Spring Container thẩm định tại giai đoạn nào trong vòng đời khởi động?
- Q2Cơ chế "Back-Off" của Spring Boot Auto-configuration dựa vào annotation nào để nhường quyền cấu hình cho người dùng?
- Q3Sự khác biệt căn bản giữa @ConditionalOnClass và @ConditionalOnBean là gì?
- Q4Nếu bạn đặt @ConditionalOnClass trên một lớp mang annotation @Configuration, điều gì sẽ xảy ra nếu class chỉ định không tồn tại trên classpath?
- Q5Tại sao thứ tự thẩm định lại mang tính quyết định đối với annotation @ConditionalOnMissingBean giữa cấu hình người dùng và cấu hình của thư viện?
Bài tiếp theo: Tổng Kết Module 04: Cấu Hình Và Khai Báo Spring Bean
Bài này có giúp bạn hiểu bản chất không?
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
@Bean & @Configuration: Cơ Chế CGLIB Full Và Lite Mode
Bài tiếpTổng Kết Module 04: Cấu Hình Và Khai Báo Spring Bean
Lộ trình Junior Java Backend Developer
Chặng 13/21 · Spring IoC, DI & Bean — bài 16/17 của chặng
Thảo luận & góp ý
Hỏi đáp về bài này