Backend

99 Ngày Spring — Ngày 10: @Conditional & cách auto-configuration hoạt động

SSite Admin
6 tháng 08, 2026 6 phút đọc 63 lượt xem
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!

Sketchnote Ngày 10: @Conditional và cách auto-configuration của Spring Boot hoạt động

@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
  • @ConditionalOnProperty là 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).

  • @ConditionalOnClass nghe 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 Condition rồ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.url là nó chảy vào đây.

  • Không có gì ma thuật: cũng @Bean, cũng @Configuration như 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: @ConditionalOnMissingBeanchỉ 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.

  • exclude là 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 JSON
  • Positive 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 GreetingService gắ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 ObjectMapper bằng @Bean — chạy --debug và tìm dòng Jackson auto-config rút lui trong báo cáo.

  • Mở jar spring-boot-autoconfigure trong IDE, tìm DataSourceAutoConfiguration — đố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!

S

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.

Bài viết liên quan

99 Ngày Spring — Ngày 14: Bean Validation — @Valid & bộ constraint chuẩn

Ngày 14 của 99 Ngày Spring: Bean Validation khai báo luật ngay trên DTO — @Valid kích hoạt, bộ constraint chuẩn (@NotBlank vs @NotEmpty vs @NotNull, @Size, @Email, @Min/@Max), bẫy int vs Integer, validate object lồng nhau không tự lan, và lỗi 400 gom một lượt.

10 thg 8, 20265 phút0
99 Ngày Java — Ngày 14: Kế thừa (Inheritance)

Ngày 14 của 99 Ngày Java: extends trao gia tài từ lớp cha, override với @Override và super., thứ tự khai sinh cha trước con sau qua super(...), protected trả nợ Ngày 13, và phép thử is-a vs has-a — khi nào nên composition thay vì kế thừa.

10 thg 8, 20266 phút0
99 Ngày Spring — Ngày 13: @RequestBody & DTO — đừng công khai entity ra API

Ngày 13 của 99 Ngày Spring: @RequestBody + Jackson deserialize JSON thành object, DTO là hợp đồng API — chặn lỗ hổng mass assignment chiều vào và rò rỉ dữ liệu chiều ra, request/response DTO khác nhau là bình thường, và Jackson annotations tinh chỉnh hợp đồng JSON.

9 thg 8, 20266 phút9