更多Spring文章,歡迎點擊 一灰灰Blog-Spring專題java
bean的條件注入,除了前面一篇博文中介紹的經過@Conditional
註解配合Condition
接口的實現以外,還提供了更多簡化的註解使用方式,省略了本身實現Condtion
接口,本篇博文主要介紹下面幾個經常使用的註解使用方式git
@ConditionalOnBean
@ConditionalOnMissingBean
@ConditionalOnClass
@ConditionalOnMissingClass
@ConditionalOnProperty
@ConditionalOnExpression
<!-- more -->github
當Bean不存在時,建立一個默認的Bean,在Spring的生態中能夠說比較常見了;接下來看下這種方式能夠怎麼用redis
@ConditionalOnBean
要求bean存在時,纔會建立這個bean;如我提供了一個bean名爲RedisOperBean
,用於封裝redis相關的操做;可是我這個bean須要依賴restTemplate
這個bean,只有當應用引入了redis的相關依賴,並存在RestTemplate
這個bean的時候,我這個bean纔會生效spring
假設bean的定義以下express
@Component @ConditionalOnBean(name="redisTemplate") public class RedisOperBean { private final RedisTemplate redisTemplate; public RedisOperBean(RedisTemplate redisTemplate) { // ... } }
這樣的好處就是我提供的這個第三方包,若是被用戶A間接依賴(可是A自己不須要操做redis),也不會由於建立RedisOperBean
而拋異常app
產生異常的緣由是由於找不到RestTemplate的bean,所以沒法實例化RedisOperBean,從而拋出異常ide
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnBeanCondition.class) public @interface ConditionalOnBean { // bean類型 Class<?>[] value() default {}; // bean類型 String[] type() default {}; // 要求bean上擁有指定的註解 Class<? extends Annotation>[] annotation() default {}; // bean names String[] name() default {}; SearchStrategy search() default SearchStrategy.ALL; }
構建一個簡單的測試用例,先定義一個基礎的beanspring-boot
public class DependedBean { }
再定義一個依賴只有上面的bean存在時,纔會加載的bean學習
public class LoadIfBeanExist { private String name; public LoadIfBeanExist(String name) { this.name = name; } public String getName() { return "load if bean exists: " + name; } }
接下來就是bean的定義了
@Bean public DependedBean dependedBean() { return new DependedBean(); } /** * 只有當DependedBean 存在時,纔會建立bean: `LoadIfBeanExist` * * @return */ @Bean @ConditionalOnBean(name = "dependedBean") public LoadIfBeanExist loadIfBeanExist() { return new LoadIfBeanExist("dependedBean"); }
根據上面的測試用例,LoadIfBeanExist
是會被正常加載的; 具體結果看後面的實例演示
ConditionalOnMissingBean
和前面一個做用正好相反的,上面是要求存在bean,而這個是要求不存在
public @interface ConditionalOnMissingBean { Class<?>[] value() default {}; String[] type() default {}; /** * The class type of beans that should be ignored when identifying matching beans. */ Class<?>[] ignored() default {}; /** * The class type names of beans that should be ignored when identifying matching * beans. */ String[] ignoredType() default {}; Class<? extends Annotation>[] annotation() default {}; String[] name() default {}; SearchStrategy search() default SearchStrategy.ALL; }
一樣定義一個bean不存在時,才建立的bean
public class LoadIfBeanNotExists { public String name; public LoadIfBeanNotExists(String name) { this.name = name; } public String getName() { return "load if bean not exists: " + name; } }
對應的bean配置以下
/** * 只有當沒有notExistsBean時,纔會建立bean: `LoadIfBeanNotExists` * * @return */ @Bean @ConditionalOnMissingBean(name = "notExistsBean") public LoadIfBeanNotExists loadIfBeanNotExists() { return new LoadIfBeanNotExists("notExistsBean"); }
由於沒有notExistsBean,因此上面這個bean也應該被正常註冊
由於bean的是否存在和class的是否存在有較大的類似性,所以實例演示放在下一小節,一塊兒測試
從使用來看,和前面基本上沒有太大的區別,無非就是將bean換成了class;這樣就能夠避免由於Class Not Found
致使的編譯異常了
@ConditionalOnClass
要求class存在
public @interface ConditionalOnClass { Class<?>[] value() default {}; /** * The classes names that must be present. * @return the class names that must be present. */ String[] name() default {}; }
先定義一個class
public class DependedClz { }
而後依賴class存在的bean
public class LoadIfClzExists { private String name; public LoadIfClzExists(String name) { this.name = name; } public String getName() { return "load if exists clz: " + name; } }
接下來就是Bean的配置
/** * 當引用了 {@link DependedClz} 類以後,纔會建立bean: `LoadIfClzExists` * * @return */ @Bean @ConditionalOnClass(DependedClz.class) public LoadIfClzExists loadIfClzExists() { return new LoadIfClzExists("dependedClz"); }
由於類存在,因此測試時,這個bean應該被正常註冊
@ConditionalOnMissingClass
class不存在時,纔會加載bean
public @interface ConditionalOnMissingClass { String[] value() default {}; }
定義一個class缺乏時纔會建立的bean
public class LoadIfClzNotExists { private String name; public LoadIfClzNotExists(String name) { this.name = name; } public String getName() { return "load if not exists clz: " + name; } }
bean的配置以下
/** * 當系統中沒有 com.git.hui.boot.conditionbean.example.depends.clz.DependedClz類時,纔會建立這個bean * * @return */ @Bean @ConditionalOnMissingClass("com.git.hui.boot.conditionbean.example.depends.clz.DependedClz") public LoadIfClzNotExists loadIfClzNotExists() { return new LoadIfClzNotExists("com.git.hui.boot.conditionbean.example.depends.clz.DependedClz"); }
由於上面這個類存在,因此這個bean不該該被正常註冊
起一個rest服務,測試下上面的四個bean是否正常
@RestController @RequestMapping("depends") public class DependRest { @Autowired private LoadIfBeanExist loadIfBeanExist; @Autowired private LoadIfBeanNotExists loadIfBeanNotExists; @Autowired private LoadIfClzExists loadIfClzExists; @Autowired(required = false) private LoadIfClzNotExists loadIfClzNotExists; @GetMapping(path = "show") public String show() { Map<String, String> result = new HashMap<>(4); // 存在 result.put("loadIfBeanExist", loadIfBeanExist == null ? "null ==> false!" : loadIfBeanExist.getName()); // 存在 result.put("loadIfBeanNotExists", loadIfBeanNotExists == null ? "null ==> false!" : loadIfBeanNotExists.getName()); // 存在 result.put("loadIfClzExists", loadIfClzExists == null ? "null ==> false!" : loadIfClzExists.getName()); // 不存在 result.put("loadIfClzNotExists", loadIfClzNotExists == null ? "null ==> true!" : loadIfClzNotExists.getName()); return JSONObject.toJSONString(result); } }
根據前面的分析,返回的結果應該是三個存在,一個不存在;下圖執行和咱們預期一致
主要是根據配置參數,來決定是否須要建立這個bean,這樣就給了咱們一個根據配置來控制Bean的選擇的手段了,如前面一篇博文中根據配置來選擇是隨機生成boolean仍是隨機生成int;只須要更改配置便可
@ConditionalOnProperty
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(OnPropertyCondition.class) public @interface ConditionalOnProperty { /** * Alias for {@link #name()}. * @return the names */ String[] value() default {}; // 配置前綴 String prefix() default ""; // 配置名 String[] name() default {}; // 要求配置存在,且包含某個值 String havingValue() default ""; // 即使沒有配置,也依然建立 boolean matchIfMissing() default false; }
測試幾個經常使用的姿式,一是根據配置是否存在,來決定是否建立
public class PropertyExistBean { private String name; public PropertyExistBean(String name) { this.name = name; } public String getName() { return "property : " + name; } } public class PropertyNotExistBean { private String name; public PropertyNotExistBean(String name) { this.name = name; } public String getName() { return "no property" + name; } }
對應的bean配置以下
/** * 配置存在時纔會加載這個bean * * @return */ @Bean @ConditionalOnProperty("conditional.property") public PropertyExistBean propertyExistBean() { return new PropertyExistBean(environment.getProperty("conditional.property")); } /** * 即使配置不存在時,也能夠加載這個bean * * @return */ @Bean @ConditionalOnProperty(name = "conditional.property.no", matchIfMissing = true) public PropertyNotExistBean propertyNotExistBean() { return new PropertyNotExistBean("conditional.property"); }
當配置存在,且value匹配時
public class PropertyValueExistBean { public String name; public PropertyValueExistBean(String name) { this.name = name; } public String getName() { return "property value exist: " + name; } } public class PropertyValueNotExistBean { public String name; public PropertyValueNotExistBean(String name) { this.name = name; } public String getName() { return "property value not exist: " + name; } }
對應的配置以下
@Bean @ConditionalOnProperty(name = {"conditional.property"}, havingValue = "properExists") public PropertyValueExistBean propertyValueExistBean() { return new PropertyValueExistBean("properExists"); } @Bean @ConditionalOnProperty(name = {"conditional.property"}, havingValue = "properNotExists") public PropertyValueNotExistBean propertyValueNotExistBean() { return new PropertyValueNotExistBean("properNotExists"); }
接下來就是配置的參數
conditional.property=properExists
根據前面的分析,上面的四個bean中,PropertyExistBean
, PropertyNotExistBean
, PropertyValueExistBean
應該存在;而PropertyValueNotExistBean
由於配置值不匹配,不會建立
測試代碼以下
@RestController @RequestMapping(path = "property") public class PropertyRest { @Autowired(required = false) private PropertyExistBean propertyExistBean; @Autowired(required = false) private PropertyNotExistBean propertyNotExistBean; @Autowired(required = false) private PropertyValueExistBean propertyValueExistBean; @Autowired(required = false) private PropertyValueNotExistBean propertyValueNotExistBean; @GetMapping(path = "show") public String show() { Map<String, String> result = new HashMap<>(4); // 存在 result.put("propertyExistBean", propertyExistBean == null ? "null ===> false" : propertyExistBean.getName()); // 存在 result.put("propertyNotExistBean", propertyNotExistBean == null ? "null ===> false" : propertyNotExistBean.getName()); // 存在 result.put("propertyValueExistBean", propertyValueExistBean == null ? "null ==> false" : propertyValueExistBean.getName()); // 不存在 result.put("propertyValueNotExistBean", propertyValueNotExistBean == null ? "null ==> true" : propertyValueNotExistBean.getName()); return JSONObject.toJSONString(result); } }
執行後結果以下,一如預期
相比較前面的Bean,Class是否存在,配置參數是否存在或者有某個值而言,這個依賴SPEL表達式的,就顯得更加的高級了;其主要就是執行Spel表達式,根據返回的true/false來判斷是否知足條件
至於SPEL是什麼東西,後面會有專文進行解釋,此處不加以展開。下面以一個簡單的demo進行演示它的使用姿式
@ConditionalOnExpression
接口定義
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @Documented @Conditional(OnExpressionCondition.class) public @interface ConditionalOnExpression { /** * The SpEL expression to evaluate. Expression should return {@code true} if the * condition passes or {@code false} if it fails. * @return the SpEL expression */ String value() default "true"; }
用一個簡單的例子,當配置參數中,根據是否知足某個條件來決定是否須要加載bean
定義一個知足條件和一個不知足的bean
public class ExpressFalseBean { private String name; public ExpressFalseBean(String name) { this.name = name; } public String getName() { return "express bean :" + name; } } public class ExpressTrueBean { private String name; public ExpressTrueBean(String name) { this.name = name; } public String getName() { return "express bean :" + name; } }
重點關注下bean的配置
@Configuration public class ExpressAutoConfig { /** * 當存在配置,且配置爲true時才建立這個bean * @return */ @Bean @ConditionalOnExpression("#{'true'.equals(environment['conditional.express'])}") public ExpressTrueBean expressTrueBean() { return new ExpressTrueBean("express true"); } /** * 配置不存在,或配置的值不是true時,才建立bean * @return */ @Bean @ConditionalOnExpression("#{!'true'.equals(environment.getProperty('conditional.express'))}") public ExpressFalseBean expressFalseBean() { return new ExpressFalseBean("express != true"); } }
對應的配置以下
conditional.express=true
@RestController @RequestMapping(path = "express") public class ExpressRest { @Autowired(required = false) private ExpressTrueBean expressTrueBean; @Autowired(required = false) private ExpressFalseBean expressFalseBean; @GetMapping(path = "show") public String show() { Map<String, String> result = new HashMap<>(4); result.put("expressTrueBean", expressTrueBean == null ? "null ==> false" : expressTrueBean.getName()); result.put("expressFalseBean", expressFalseBean == null ? "null ==> true": expressFalseBean.getName()); return JSONObject.toJSONString(result); } }
上面的執行,expressTrueBean
應該存在,另一個爲null,運行結果以下
基礎篇
應用篇
一灰灰的我的博客,記錄全部學習和工做中的博文,歡迎你們前去逛逛
盡信書則不如,以上內容,純屬一家之言,因我的能力有限,不免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
一灰灰blog
知識星球