1、簡述
在 Spring Boot 中,@Conditional 注解用于實現 條件化 Bean 裝配,即根據特定的條件來決定是否加載某個 Bean。它是 Spring 框架中的一個擴展機制,常用于實現模塊化、可配置的組件加載。
本文將詳細介紹 @Conditional 相關的注解,包括 @ConditionalOnClass、@ConditionalOnMissingBean、@ConditionalOnProperty 等,并結合實際應用示例講解其使用方式。
2、@Conditional 注解概述
@Conditional 是 Spring 4 引入的條件裝配注解,它可以根據外部環境或配置的狀態,決定是否創建 Bean。
其核心接口是:
public interface Condition {boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
任何實現 Condition
接口的類,都可以用于自定義條件判斷。
@Configuration
public class MyConfig {@Bean@Conditional(MyCondition.class) public MyService myService() {return new MyService();}
}
其中 MyCondition.class
需要實現 Condition
接口,并提供判斷邏輯。
3、Spring Boot 內置 @Conditional 相關注解
Spring Boot 提供了一些常見的 @Conditional
注解,簡化了條件判斷的邏輯:
注解 | 作用 |
---|---|
@ConditionalOnClass | 類路徑下存在某個類時,才加載該 Bean |
@ConditionalOnMissingClass | 類路徑下不存在某個類時,才加載該 Bean |
@ConditionalOnBean | 當容器中存在指定 Bean 時,才加載當前 Bean |
@ConditionalOnMissingBean | 當容器中不存在指定 Bean 時,才加載當前 Bean |
@ConditionalOnProperty | 當指定的配置屬性滿足條件時,才加載當前 Bean |
@ConditionalOnExpression | 當指定的 SpEL 表達式為 true 時,才加載當前 Bean |
@ConditionalOnJava | 當 Java 版本符合要求時,才加載當前 Bean |
@ConditionalOnWebApplication | 當應用是 Web 應用時,才加載當前 Bean |
@ConditionalOnNotWebApplication | 當應用不是 Web 應用時,才加載當前 Bean |
3.1 @ConditionalOnClass 使用示例(類路徑檢測)
我們希望在 Spring Boot 項目中,當類路徑下存在 com.example.MyLibrary
這個類時,才注冊 MyService
這個 Bean。
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyAutoConfiguration {@Bean@ConditionalOnClass(name = "com.example.MyLibrary")public MyService myService() {return new MyService();}
}
解釋:
- 如果
com.example.MyLibrary
存在,則MyService
會被創建。 - 如果
com.example.MyLibrary
不存在,則MyService
不會被加載。
3.2 @ConditionalOnMissingBean(Bean 缺失時加載)
如果用戶沒有手動定義 MyService
,則提供一個默認實現。
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic MyService myService() {return new MyService("Default Implementation");}
}
解釋:
- 如果 Spring 容器中已經存在
MyService
,則不會創建新的 Bean。 - 只有當
MyService
不存在時,才會注冊默認實現。
3.3 @ConditionalOnProperty(基于配置項條件加載 Bean)
我們希望 MyService
只有在 app.feature.enabled=true
時才被創建。
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyAutoConfiguration {@Bean@ConditionalOnProperty(name = "app.feature.enabled", havingValue = "true", matchIfMissing = false)public MyService myService() {return new MyService();}
}
解釋:
- 如果
application.properties
中包含app.feature.enabled=true
,則MyService
會被創建。 - 如果
app.feature.enabled=false
,或者該屬性未定義,則MyService
不會被加載。
在 application.properties
中啟用:
app.feature.enabled=true
3.4 @ConditionalOnBean(存在特定 Bean 時才加載)
當 UserService
存在時,才創建 OrderService
。
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class OrderServiceConfiguration {@Bean@ConditionalOnBean(UserService.class)public OrderService orderService() {return new OrderService();}
}
解釋:
- 如果
UserService
存在,則OrderService
也會被創建。 - 如果
UserService
不存在,則OrderService
不會被加載。
3.5 @ConditionalOnExpression(基于 SpEL 表達式加載)
當 server.port
大于 8080 時,才創建 AdvancedService
。
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class AdvancedConfig {@Bean@ConditionalOnExpression("#{T(java.lang.Integer).parseInt('${server.port:8080}') > 8080}")public AdvancedService advancedService() {return new AdvancedService();}
}
解釋:
server.port
> 8080 時,AdvancedService
會被加載。- 其他情況下,不會創建該 Bean。
在 application.properties
中配置:
server.port=9090
3.6 結合 @ConditionalOnClass 實現 Starter 組件的熱拔插
我們要創建一個 Spring Boot Starter 組件,如果用戶的 classpath
下存在 RedisTemplate
,則自動加載 Redis 相關的 Bean。
步驟:
創建 Starter 組件
@Configuration
@ConditionalOnClass(RedisTemplate.class)
public class RedisAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);return template;}
}
應用使用 Starter
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 如果引入 Redis 依賴,
RedisAutoConfiguration
會自動生效。 - 如果不引入 Redis 依賴,則
RedisTemplate
不會被創建。
4、總結
@Conditional
及其衍生注解使 Spring Boot 具備了自動配置和熱拔插的能力。@ConditionalOnClass
可用于判斷某個類是否存在,常用于 Starter 組件的自動裝配。@ConditionalOnProperty
適用于基于配置的條件加載,增強靈活性。@ConditionalOnBean
和@ConditionalOnMissingBean
適用于組件依賴管理。
合理使用這些注解,可以構建更加模塊化、靈活、可配置的 Spring Boot 應用。