@Conditional
:Spring4.0 介紹了一個新的注解@Conditional,它的邏輯語義可以作為"If…then…else…"來對bean的注冊起作用。
@Contidional 介紹
Conditional 是由 SpringFramework 提供的一個注解,位於 org.springframework.context.annotation 包內,定義如下。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {
Class<? extends Condition>[] value();
}
SpringBoot 模塊大量的使用@Conditional 注釋,我們可以將Spring的@Conditional注解用於以下場景:
- 可以作為類級別的注解直接或者間接的與@Component相關聯,包括@Configuration類;
- 可以作為元注解,用於自動編寫構造性注解;
- 作為方法級別的注解,作用在任何@Bean方法上。
Condition 接口
我們需要一個類實現Spring提供的Condition接口,它會匹配@Conditional所符合的方法,然后我們可以使用我們在@Conditional注解中定義的類來檢查。
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
Spring @Conditional注解實例
作用在方法上
先來看一個簡單一些的示例,我們假設有三個角色老師Teacher、學生Student和父母Parent,三種環境Linux、Windows和MacOSX,如果是Linux環境,就注冊Teacher,如果是Windows環境就注冊Parent,如果是Mac 環境就注冊Student。代碼示例如下:
- 首先創建Teacher和Student對象,沒有任何的屬性和方法,只是一個空類
//如果當前工程運行在Windows系統下,就注冊Student
public class Student {}
//如果當前工程運行在Linux系統下,就注冊Teacher
public class Teacher {}
// 如果是Mac OSX 系統,就注冊Parent
public class Parent {}
- 創建一個LinuxCondition和一個WindowsCondition,LinuxCondition能夠匹配Linux環境,WindowsCondition能夠匹配Windows環境,MacOSX 系統匹配mac環境。
public class LinuxCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 獲取系統環境的屬性
String systemName = context.getEnvironment().getProperty("os.name");
if(systemName.contains("Linux")){
return true;
}
return false;
}
}
//自定義一個判斷條件
public class WindowsCondition implements Condition {
/*
* ConditionContext context: spring容器上下文環境
* AnnotatedTypeMetadata metadata :@Conditional修飾類型信息
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String systemName = context.getEnvironment().getProperty("os.name");
if(systemName.contains("Windows")){
return true;
}
return false;
}
}
public class OsxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String property = context.getEnvironment().getProperty("os.name");
if(property.equals("Mac OS X")){
return true;
}
return false;
}
}
- 下面來新建匹配注冊環境,如果系統是Linux環境,就注冊Teacher,如果系統是Windows,就注冊Parent,如果是Mac 系統,就注冊Student
@Configuration
public class AppConfig {
@Conditional(OsxCondition.class)
@Bean
public Student student(){
return new Student();
}
@Conditional(LinuxCondition.class)
@Bean
public Teacher teacher(){
return new Teacher();
}
@Conditional(WindowsCondition.class)
@Bean
public Parent parent(){
return new Parent();
}
}
- 新建測試類進行測試
public class ConditionTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
String[] names = context.getBeanDefinitionNames();
for(String name : names){
System.out.println("name = " + name);
}
}
}
由輸出可以看出,name = student 被輸出到控制台,也就是說,我當前所用的系統環境是MacOSX環境,所以注冊的是OSXCondition,也就是student的bean。
手動設置系統環境
也可以進行手動修改vm.options,把當前的系統環境變為Linux 或者Windows,以Idea為例:
在Edit Configurations
中找到vm.options 選項,把系統環境改為 Linux,如下:
然后重新啟動測試,發現Teacher 被注入進來了,修改當前環境為Windows,觀察Parent也被注入進來並輸出了。
作用在類上
@Conditional 注解可以作用在類上,表示此類下面所有的bean滿足條件后都可以進行注入,通常與@Configuration注解一起使用。
- 新建一個
AppClassConfig
,在類上標注@Conditional()注解,並配置相關bean,如下:
@Conditional(value = OsxCondition.class)
上文表示如果是OsxCondition.class 的話,就注冊student、teacher、parent
- 測試類不用修改,直接用原測試類進行測試,發現student、 teacher、 parent 都被注冊進來了
多個條件類
因為@Conditional注解的value 方法默認傳遞一個數組,所以可以接受多個condition,為了測試如下情況,
新建一個 TestCondition
類,如下:
// 單純為了測試
public class TestCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 返回false,表示不匹配
return false;
}
}
修改一下AppClassConfig
@Conditional(value = {OsxCondition.class,TestCondition.class})
也就是給@Conditional 多加了一個參數 TestCondition.class
啟動之前的測試類,發現上述的bean都沒有注入,也就是說,只有在滿足OsxCondition.class 和 TestCondition.class 都為true的情況下,才會注入對應的bean,修改TestCondition.class的matches方法的返回值為true
,重新觀察返回結果,發現上述bean都被注入了。
@Conditional 與@Profile 的對比
@Spring3.0 也有一些和@Conditional 相似的注解,它們是Spring SPEL 表達式和Spring Profiles 注解 Spring4.0的@Conditional 注解要比@Profile 注解更加高級。@Profile 注解用來加載應用程序的環境。@Profile注解僅限於根據預定義屬性編寫條件檢查。 @Conditional注釋則沒有此限制。
Spring中的@Profile 和 @Conditional 注解用來檢查"If…then…else"的語義。然而,Spring4 @Conditional是@Profile 注解的更通用法。
- Spring 3中的 @Profiles僅用於編寫基於Environment變量的條件檢查。 配置文件可用於基於環境加載應用程序配置。
- Spring 4 @Conditional注解允許開發人員為條件檢查定義用戶定義的策略。 @Conditional可用於條件bean注冊。