Spring Core & Boot
24/41
Bài 24 / 41~12 phútConfiguration & Bean DeclarationLộ trình · chặng 13/21Miễn phí… lượt xem

@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:

Java
@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:

Sơ đồ nhánh cho thấy khi Condition sai thì không có BeanDefinition nào vào beanDefinitionMap, nên createBean không có gì để duyệt tới

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:

  1. Pha đăng ký: Kiểm tra Condition.matches(). Nếu trả về true, BeanDefinition được thêm vào beanDefinitionMap. Nếu false, bean bị bỏ qua ngay lập tức.
  2. 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:

Java
@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à BeanDefinition hiện có.
  • getEnvironment(): Đọc các thuộc tính cấu hình từ application.properties hoặ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:

Java
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ạtTình huống sử dụng
@ConditionalOnClassClass chỉ định có mặt trong classpathTự bật tính năng khi người dùng thêm starter JAR
@ConditionalOnMissingClassClass chỉ định vắng mặt trong classpathCung cấp phương án dự phòng khi thiếu driver
@ConditionalOnBeanBean kiểu chỉ định đã tồn tại trong containerĐịnh nghĩa bean phụ thuộc vào một bean khác
@ConditionalOnMissingBeanChưa có bean nào thuộc kiểu này trong containerCơ chế cung cấp cấu hình mặc định (Back-off)
@ConditionalOnPropertyThuộc tính cấu hình khớp với giá trị mong muốnBật tắt tính năng bằng Feature Flag
@ConditionalOnWebApplicationỨng dụng đang chạy trong môi trường WebKhai báo các bean dành riêng cho tầng HTTP

Ví dụ sử dụng @ConditionalOnProperty làm feature flag:

Java
@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:

Java
@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?

Hai cột cùng một ConditionalOnMissingBean: autoconfig chạy trước thì app có hai bean DataSource, user config chạy trước thì autoconfig lùi lại

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, @ConditionalOnMissingBean sẽ 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:

  1. 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.
  2. Nó kiểm tra xem người dùng đã tự định nghĩa DataSource chưa (@ConditionalOnMissingBean).
  3. 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 beanDefinitionMap và tự động rút lui hoàn toàn.

Tóm tắt

  • @Conditional thẩm định điều kiện ở pha đăng ký BeanDefinition của BeanFactoryPostProcessor, trước khi bất kỳ bean nào được tạo.
  • Giao diện Condition cung cấp ConditionContext để truy xuất môi trường, classpath và danh sách bean đã đăng ký.
  • @ConditionalOnMissingBean là 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 độ @Configuration giú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

Tự kiểm tra
0/5 câu đã trả lời
  1. 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?
  2. Q2
    Cơ 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?
  3. Q3
    Sự khác biệt căn bản giữa @ConditionalOnClass và @ConditionalOnBean là gì?
  4. Q4
    Nế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?
  5. Q5
    Tạ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.

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

Bài tiếp theo

Tổng Kết Module 04: Cấu Hình Và Khai Báo Spring Bean