Quản lý cấu hình Bean linh hoạt với annotation @Conditional trong Spring

Trong hệ sinh thái Spring Framework, @Conditional là một annotation mạnh mẽ cho phép các nhà phát triển kiểm soát việc khởi tạo và đăng ký Bean vào Application Context dựa trên các điều kiện cụ thể. Thay vì cấu hình cứng, cơ chế này giúp ứng dụng trở nên linh động hơn, dễ dàng thích ứng với nhiều môi trường triển khai khác nhau.

Cơ chế hoạt động của @Conditional

Annotation này hoạt động dựa trên việc kiểm tra kết quả trả về từ một lớp triển khai interface Condition. Nếu phương thức kiểm tra trả về true, Bean sẽ được đăng ký; ngược lại, Spring sẽ bỏ qua định nghĩa Bean đó.

1. Triển khai lớp điều kiện tùy chỉnh

Để tạo một điều kiện riêng, bạn cần hiện thực interface org.springframework.context.annotation.Condition và ghi đè phương thức matches. Dưới đây là ví dụ kiểm tra biến môi trường của hệ thống:

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class FeatureEnabledCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // Kiểm tra biến môi trường "APP_FEATURE_X" có bằng "enabled" hay không
        String featureFlag = context.getEnvironment().getProperty("app.feature.x");
        return "enabled".equalsIgnoreCase(featureFlag);
    }
}

2. Sử dụng @Conditional trong cấu hình

Sau khi đã định nghĩa logic điều kiện, bạn áp dụng nó trực tiếp lên phương thức @Bean hoặc lớp @Configuration:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ServiceModuleConfig {

    @Bean
    @Conditional(FeatureEnabledCondition.class)
    public AdvancedFeatureService advancedFeatureService() {
        return new AdvancedFeatureServiceImpl();
    }
}

3. Tạo Annotation điều kiện riêng

Để mã nguồn sạch hơn và tái sử dụng được nhiều nơi, bạn có thể đóng gói @Conditional vào một annotation tùy chỉnh:

import java.lang.annotation.*;
import org.springframework.context.annotation.Conditional;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(FeatureEnabledCondition.class)
public @interface ConditionalOnFeatureX {
}

Các biến thể phổ biến trong Spring Boot

Spring Boot mở rộng khả năng của @Conditional bằng cách cung cấp một bộ các annotation chuyên biệt, giúp xử lý hầu hết các tình huống cấu hình phổ biến:

  • @ConditionalOnProperty: Kiểm tra dựa trên giá trị của các tham số trong tệp application.properties hoặc yml.
  • @ConditionalOnClass: Chỉ kích hoạt cấu hình nếu một lớp cụ thể có mặt trong classpath (thường dùng cho các thư viện tùy chọn).
  • @ConditionalOnMissingBean: Chỉ tạo Bean nếu trong Container chưa tồn tại một Bean cùng loại, giúp người dùng dễ dàng ghi đè cấu hình mặc định.
  • @ConditionalOnWebApplication: Kiểm tra xem ứng dụng có đang chạy dưới dạng Web Application hay không.
  • @ConditionalOnExpression: Sử dụng SpEL (Spring Expression Language) để đánh giá các điều kiện phức tạp.

Các tình huống ứng dụng thực tế

Việc sử dụng điều kiện hóa giúp giải quyết nhiều bài toán kiến trúc phần mềm:

  • Đa môi trường: Tự động chuyển đổi giữa các triển khai Mock Service khi chạy ở môi trường Local/Test và Real Service khi chạy trên Production.
  • Tối ưu hóa tài nguyên: Chỉ khởi tạo các Bean kết nối Database khi ứng dụng thực sự yêu cầu hoặc khi các Driver tương ứng tồn tại.
  • Kiến trúc Plugin: Cho phép bật/tắt các module tính năng chỉ bằng cách thay đổi cấu hình mà không cần thay đổi mã nguồn.
  • Tương thích phiên bản Java: Đăng ký các Bean tận dụng tính năng mới của các bản Java cao hơn nếu môi trường runtime đáp ứng yêu cầu.

Thẻ: Spring Framework Spring Boot Dependency Injection Java

Đăng vào ngày 17 tháng 7 lúc 17:10