99 Ngày Spring — Ngày 10: @Conditional & cách auto-configuration hoạt động
Ngày 02, khi ứng dụng đầu tiên chạy mà chưa viết dòng cấu hình nào, ta đã treo câu hỏi: "Boot tự cấu hình bằng cách nào?" Ngày 09 hé lộ một nửa: @Profile bật tắt bean theo môi trường. Hôm nay là nửa còn lại — @Profile chỉ là trường hợp đặc biệt của @Conditional: cơ chế cho phép mọi bean ra đời kèm điều kiện. Toàn bộ auto-configuration của Spring Boot — hàng trăm bean tự dựng — thực chất chỉ là một rừng điều kiện được tính lúc khởi động. Mở nắp máy thôi!

@Conditional — bean có điều kiện
Ý tưởng chỉ một câu: trước khi tạo bean, container kiểm tra điều kiện — rớt là bean không tồn tại, y như @Profile Ngày 09 nhưng linh hoạt hơn nhiều vì điều kiện có thể là bất cứ thứ gì: một dòng cấu hình, một lớp trên classpath, một bean khác đã/chưa tồn tại:
// Bean chỉ sinh ra khi cờ trong cấu hình bật — feature flag kinh điển@Configurationpublic class GreetingConfig {
@Bean
@ConditionalOnProperty(name = "app.greeting.enabled", havingValue = "true")
GreetingService greetingService() {
return new GreetingService();
}
}
# application.yml (Ngày 08)
app:
greeting:
enabled: true # false / thiếu → bean COI NHƯ KHÔNG TỒN TẠI
// Họ hàng @Conditional hay gặp nhất:// @ConditionalOnClass — classpath CÓ lớp này (starter có mặt?)// @ConditionalOnMissingBean — CHƯA ai khai bean kiểu này// @ConditionalOnProperty — cấu hình bật cờ này// @ConditionalOnWebApplication — đang là ứng dụng web@ConditionalOnPropertylà feature flag rẻ nhất: bật tắt cả cụm tính năng bằng một dòng yml — không sửa code, không build lại (Ngày 08 đã dặn: cấu hình nằm ngoài code).@ConditionalOnClassnghe lạ mà quen: nó chính là phép màu đằng sau chuyện "thêm starter là có tính năng" — thêm dependency, classpath có lớp, điều kiện đậu, bean mọc ra.Muốn điều kiện "nhà làm"? Implement
Conditionrồi gắn@Conditional(MyCondition.class)— nhưng 99% nhu cầu đã có sẵn trong họ@ConditionalOn...của Boot.
Auto-configuration: rừng điều kiện của Boot
Giờ ghép hình lớn. Đằng sau @SpringBootApplication (Ngày 02) có @EnableAutoConfiguration — nó nạp một danh sách ~150 lớp cấu hình từ file AutoConfiguration.imports trong jar của Boot. Mỗi lớp là một @Configuration (Ngày 06) dát đầy điều kiện:
// Phiên bản rút gọn của auto-config thật trong Spring Boot:@AutoConfiguration@ConditionalOnClass(DataSource.class) // classpath có JDBC?@EnableConfigurationProperties(DataSourceProperties.class) // gom spring.datasource.*public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean // bạn CHƯA tự khai DataSource?
DataSource dataSource(DataSourceProperties props) {
return props.initializeDataSourceBuilder().build(); // Boot làm hộ
}
}
// Hành trình lúc khởi động:// @SpringBootApplication// → @EnableAutoConfiguration// → đọc danh sách trong META-INF/.../AutoConfiguration.imports (~150 lớp)// → lớp nào ĐẬU hết vòng điều kiện mới được tạo beanĐọc thành lời: "Nếu classpath có JDBC, và người dùng chưa tự khai DataSource — tôi tạo giúp một cái từ spring.datasource.*". Ngày 08 bạn đặt
spring.datasource.urllà nó chảy vào đây.Không có gì ma thuật: cũng
@Bean, cũng@Configurationnhư bạn tự viết — chỉ khác là được bảo vệ bằng điều kiện để chỉ kích hoạt đúng lúc.Đây là lý do Boot "opinionated nhưng không độc đoán": mặc định hợp lý mọc tự động, nhưng mọi mặc định đều có đường lui — ngay bên dưới.
Nghệ thuật rút lui: @ConditionalOnMissingBean
Câu hỏi quan trọng nhất khi dùng Boot: "nếu tôi muốn tự cấu hình thì sao?" Câu trả lời nằm ở một điều kiện xuất hiện trong hầu hết auto-config: @ConditionalOnMissingBean — chỉ tạo khi bạn chưa tạo:
// Bạn tự khai một DataSource trong config của mình (Ngày 06):@Configurationpublic class MyDataConfig {
@Bean
DataSource dataSource() { // bean CỦA BẠN
return DataSourceBuilder.create()
.url("jdbc:postgresql://localhost/blog")
.build();
}
}
// → @ConditionalOnMissingBean bên auto-config thấy "đã có người khai"// → Boot RÚT LUI (back off) — không tạo bản mặc định nữa// → Không xung đột, không cần tắt gì cả: bean của bạn LUÔN thắng
// Muốn tắt hẳn một auto-config (hiếm khi cần):@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)Hợp đồng ngầm của Boot: bean của bạn luôn được ưu tiên — auto-config chỉ lấp chỗ trống, không bao giờ tranh chỗ.
Vì vậy thứ tự nạp mới quan trọng: cấu hình của bạn được xử lý trước, auto-config sau — để nó còn kịp "nhìn thấy" bean của bạn mà nhường.
excludelà nước cờ cuối — hiếm khi cần; đa số tình huống chỉ cần khai bean đè hoặc chỉnh property là auto-config tự thích nghi.
Soi máy: báo cáo điều kiện
Khi "phép màu" không như ý — bean mong đợi không xuất hiện, hoặc xuất hiện thứ không mời — đừng đoán, hỏi thẳng Boot:
# Chạy app với cờ --debug (hoặc debug: true trong yml):java -jar blog.jar --debug
# Boot in CONDITIONS EVALUATION REPORT — ai đậu, ai rớt, vì sao:Positive matches:
DataSourceAutoConfiguration matched:
- @ConditionalOnClass found 'javax.sql.DataSource'
Negative matches:
KafkaAutoConfiguration:
- @ConditionalOnClass did not find 'org.apache.kafka...'
# → không có starter Kafka thì cả cụm im lặng nghỉ
# Có Actuator (hẹn Ngày 58)? GET /actuator/conditions — cùng báo cáo, dạng JSONPositive matches: auto-config đã kích hoạt và vì sao. Negative matches: ai ngồi ngoài và rớt ở điều kiện nào — đọc mục này là giải được đa số ca "sao bean không thấy?".
Kỹ năng đọc báo cáo này chính là ranh giới giữa "dùng Boot như hộp đen" và "hiểu Boot như đồng nghiệp" — đáng luyện từ sớm.
Bài tập nhỏ
Viết bean
GreetingServicegắn@ConditionalOnProperty(name = "app.greeting.enabled")— chạy 2 lần với cờ bật/tắt, quan sát bean có mặt hay vắng mặt.Tự khai một
ObjectMapperbằng@Bean— chạy--debugvà tìm dòng Jackson auto-config rút lui trong báo cáo.Mở jar
spring-boot-autoconfiguretrong IDE, tìmDataSourceAutoConfiguration— đối chiếu từng annotation với bài hôm nay.Đếm xem app của bạn kích hoạt bao nhiêu auto-config (đếm Positive matches) — con số thường làm người mới bất ngờ.
Kết luận
Câu hỏi treo từ Ngày 02 đã có lời giải trọn vẹn: Boot tự cấu hình bằng một rừng bean có điều kiện — @ConditionalOnClass dò starter, @ConditionalOnProperty nghe cấu hình, @ConditionalOnMissingBean nhường bạn quyền tối thượng, và --debug kể hết mọi quyết định. Đây cũng là mảnh cuối của Giai đoạn 1 — Spring Core: container, bean, DI, cấu hình, profile, điều kiện — đủ đồ nghề rồi. Từ Ngày 11, ta bắt đầu xây thứ người dùng thật sự chạm vào: REST API — khởi đầu bằng controller đầu tiên và @RestController. Hẹn gặp lại!
Site Admin
Engineer and writer. Building things with TypeScript and distributed systems.
Bình luận (0)
Bạn cần đăng nhập bằng Google để bình luận.
Hãy là người bình luận đầu tiên.


