做用
@Conditional是Spring4新提供的註解,它的做用是按照必定的條件進行判斷,知足條件的纔給容器註冊Bean。java
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Conditional { Class<? extends Condition>[] value(); }
咱們點進去看後,發現它是一個接口,有一個方法。linux
@FunctionalInterface public interface Condition { boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2); }
它持有很多有用的對象,能夠用來獲取不少系統相關的信息,來豐富條件判斷,接口定義以下ios
public interface ConditionContext { /** * 獲取Bean定義 */ BeanDefinitionRegistry getRegistry(); /** * 獲取Bean工程,所以就能夠獲取容器中的全部bean */ @Nullable ConfigurableListableBeanFactory getBeanFactory(); /** * environment 持有全部的配置信息 */ Environment getEnvironment(); /** * 資源信息 */ ResourceLoader getResourceLoader(); /** * 類加載信息 */ @Nullable ClassLoader getClassLoader(); }
需求
根據當前系統環境的的不一樣實例不一樣的Bean,好比如今是Mac
那就實例一個Bean,若是是Window
系統實例另外一個Bean。git
首先建立一個Bean類github
@Data @AllArgsConstructor @NoArgsConstructor @ToString public class SystemBean { /** * 系統名稱 */ private String systemName; /** * 系統code */ private String systemCode; }
@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"); } }
這兩個類都實現了Condition接口, 只有matches方法返回true纔會實例化當前Bean
。spring
1)WindowsConditionwindows
@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) MacConditionspringboot
@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; } }
/** * @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); } } }
運行結果ide
經過運行結果能夠看出測試
一、雖然配置兩個Bean,但這裏只實例化了一個Bean,由於我這邊是Mac電腦,因此實例化的是mac的SystemBean
二、注意一點,咱們能夠看出 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)