SpringBoot(15)—@Conditional註解
作用
@Conditional是Spring4新提供的註解,它的作用是按照一定的條件進行判斷,滿足條件的才給容器註冊Bean。
一、概述
1、@Conditional註解定義
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
2、Condition
我們點進去看後,發現它是一個介面,有一個方法。
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
3、ConditionContext
它持有不少有用的物件,可以用來獲取很多系統相關的資訊,來豐富條件判斷,介面定義如下
public interface ConditionContext {
/**
* 獲取Bean定義
*/
BeanDefinitionRegistry getRegistry();
/**
* 獲取Bean工程,因此就可以獲取容器中的所有bean
*/
@Nullable
ConfigurableListableBeanFactory getBeanFactory();
/**
* environment 持有所有的配置資訊
*/
Environment getEnvironment();
/**
* 資源資訊
*/
ResourceLoader getResourceLoader();
/**
* 類載入資訊
*/
@Nullable
ClassLoader getClassLoader();
}
二、案例
需求
根據當前系統環境的的不同例項不同的Bean,比如現在是Mac
那就例項一個Bean,如果是Window
系統例項另一個Bean。
1、SystemBean
首先建立一個Bean類
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class SystemBean {
/**
* 系統名稱
*/
private String systemName;
/**
* 系統code
*/
private String systemCode;
}
2、通過Configuration配置例項化Bean
@Slf4j
@Configuration
public class ConditionalConfig {
/**
* 如果WindowsCondition的實現方法返回true,則注入這個bean
*/
@Bean("windows")
@Conditional({WindowsCondition.class})
public SystemBean systemWi() {
log.info("ConditionalConfig方法注入 windows實體");
return new SystemBean("windows系統","002");
}
/**
* 如果LinuxCondition的實現方法返回true,則注入這個bean
*/
@Bean("mac")
@Conditional({MacCondition.class})
public SystemBean systemMac() {
log.info("ConditionalConfig方法注入 mac實體");
return new SystemBean("Mac ios系統","001");
}
}
3、WindowsCondition和MacCondition
這兩個類都實現了Condition介面, 只有matches方法返回true才會例項化當前Bean
。
1)WindowsCondition
@Slf4j
public class WindowsCondition implements Condition {
/**
* @param conditionContext:判斷條件能使用的上下文環境
* @param annotatedTypeMetadata:註解所在位置的註釋資訊
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
//獲取ioc使用的beanFactory
ConfigurableListableBeanFactory beanFactory = conditionContext.getBeanFactory();
//獲取類載入器
ClassLoader classLoader = conditionContext.getClassLoader();
//獲取當前環境資訊
Environment environment = conditionContext.getEnvironment();
//獲取bean定義的註冊類
BeanDefinitionRegistry registry = conditionContext.getRegistry();
//獲得當前系統名
String property = environment.getProperty("os.name");
//包含Windows則說明是windows系統,返回true
if (property.contains("Windows")){
log.info("當前作業系統是:Windows");
return true;
}
return false;
}
}
2) MacCondition
@Slf4j
public class MacCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
if (property.contains("Mac")) {
log.info("當前作業系統是:Mac OS X");
return true;
}
return false;
}
}
4、測試類測試
/**
* @author xub
* @date 2019/6/13 下午10:42
*/
@SpringBootTest(classes = Application.class)
@RunWith(SpringRunner.class)
public class TestConditionOn {
@Autowired
private SystemBean windows;
@Autowired
private SystemBean mac;
@Test
public void test() {
if (windows != null) {
System.out.println("windows = " + windows);
}
if (mac != null) {
System.out.println("linux = " + mac);
}
}
}
執行結果
通過執行結果可以看出
1、雖然配置兩個Bean,但這裡只例項化了一個Bean,因為我這邊是Mac電腦,所以例項化的是mac的SystemBean
2、注意一點,我們可以看出 window
並不為null,而是mac例項化的Bean。說明 只要例項化一個Bean的,不管你命名什麼,都可以注入這個Bean。
修改一下
這裡做一個修改,我們把ConditionalConfig
中的這行程式碼註釋掉。
// @Conditional({WindowsCondition.class})
再執行下程式碼
通過執行結果可以看出,配置類的兩個Bean都已經注入成功了。
注意
當同一個物件被注入兩次及以上的時候,那麼你在使用當前物件的時候,名稱一定要是兩個bean名稱的一個,否則報錯。比如修改為
@Autowired
private SystemBean windows;
@Autowired
private SystemBean mac;
@Autowired
private SystemBean linux;
在啟動發現,報錯了。
意思很明顯,就是上面只例項化成功一個SystemBean的時候,你取任何名字,反正就是把當前已經例項化的物件注入給你就好了。
但是你現在同時注入了兩個SystemBean,你這個時候有個名稱為linux,它不知道應該注入那個Bean,所以採用了報錯的策略。
GitHub原始碼
https://github.com/yudiandemingzi/SpringBootBlog
專案名稱 03-conditional
參考
只要自己變優秀了,其他的事情才會跟著好起來(中將3)