bean的條件注入,除了前面一篇博文中介紹的經過@Conditional
註解配合Condition
接口的實現以外,還提供了更多簡化的註解使用方式,省略了本身實現Condtion
接口,本篇博文主要介紹下面幾個經常使用的註解使用方式java
@ConditionalOnBean
@ConditionalOnMissingBean
@ConditionalOnClass
@ConditionalOnMissingClass
@ConditionalOnProperty
@ConditionalOnExpression
當Bean不存在時,建立一個默認的Bean,在Spring的生態中能夠說比較常見了;接下來看下這種方式能夠怎麼用git
@ConditionalOnBean
要求bean存在時,纔會建立這個bean;如我提供了一個bean名爲RedisOperBean
,用於封裝redis相關的操做;可是我這個bean須要依賴restTemplate
這個bean,只有當應用引入了redis的相關依賴,並存在RestTemplate
這個bean的時候,我這個bean纔會生效github
假設bean的定義以下redis
@Component
@ConditionalOnBean(name="redisTemplate")
public class RedisOperBean {
private final RedisTemplate redisTemplate;
public RedisOperBean(RedisTemplate redisTemplate) {
// ...
}
}
複製代碼
這樣的好處就是我提供的這個第三方包,若是被用戶A間接依賴(可是A自己不須要操做redis),也不會由於建立RedisOperBean
而拋異常spring
產生異常的緣由是由於找不到RestTemplate的bean,所以沒法實例化RedisOperBean,從而拋出異常express
@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;
}
複製代碼
構建一個簡單的測試用例,先定義一個基礎的beanbash
public class DependedBean {
}
複製代碼
再定義一個依賴只有上面的bean存在時,纔會加載的beanapp
public class LoadIfBeanExist {
private String name;
public LoadIfBeanExist(String name) {
this.name = name;
}
public String getName() {
return "load if bean exists: " + name;
}
}
複製代碼
接下來就是bean的定義了ide
@Bean
public DependedBean dependedBean() {
return new DependedBean();
}
/** * 只有當DependedBean 存在時,纔會建立bean: `LoadIfBeanExist` * * @return */
@Bean
@ConditionalOnBean(name = "dependedBean")
public LoadIfBeanExist loadIfBeanExist() {
return new LoadIfBeanExist("dependedBean");
}
複製代碼
根據上面的測試用例,LoadIfBeanExist
是會被正常加載的; 具體結果看後面的實例演示spring-boot
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