spring的@ConditionalOnMissingBean注解


Spring4推出了@Conditional注解,方便程序根據當前環境或者容器情況來動態注入bean,對@Conditional注解不熟悉的朋友可移步至 Spring @Conditional注解 詳細講解及示例 這篇博客進行學習。

繼@Conditional注解后,又基於此注解推出了很多派生注解,比如@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnExpression、@ConditionalOnClass......動態注入bean變得更方便了。本篇將講解@ConditionalOnBean注解。

配置類中有兩個Computer類的bean,一個是筆記本電腦,一個是備用電腦。如果當前容器中已經有電腦bean了,就不注入備用電腦,如果沒有,則注入備用電腦,這里需要使用到@ConditionalOnMissingBean。

@Configuration
public class BeanConfig {

@Bean(name = "notebookPC")
public Computer computer1(){
return new Computer("筆記本電腦");
}

@ConditionalOnMissingBean(Computer.class)
@Bean("reservePC")
public Computer computer2(){
return new Computer("備用電腦");
}
}
這個注解就實現了功能,這個@ConditionalOnMissingBean為我們做了什么呢?我們來一探究竟.。

一探究竟:
首先,來看@ConditionalOnMissingBean的聲明:

//可以標注在類和方法上
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
//使用了@Conditional注解,條件類是OnBeanCondition
@Conditional({OnBeanCondition.class})
public @interface ConditionalOnMissingBean {
Class<?>[] value() default {};

String[] type() default {};

Class<?>[] ignored() default {};

String[] ignoredType() default {};

Class<? extends Annotation>[] annotation() default {};

String[] name() default {};

SearchStrategy search() default SearchStrategy.ALL;
}
這時候,我們就看到了我們熟悉的@Conditional注解,OnBeanCondition作為條件類。

OnBeanCondition類的聲明:

//定義帶注釋的組件的排序順序,2147483647即為默認值
@Order(2147483647)
class OnBeanCondition extends SpringBootCondition implements ConfigurationCondition {
它繼承了SpringBootCondition類,OnBeanCondition類中沒有matches方法,而SpringBootCondition類中有實現matches方法。OnBeanCondition還實現了ConfigurationCondition,ConfigurationCondition接口不熟悉的讀者可以到Spring ConfigurationCondition接口詳解 了解接口。OnBeanCondition類重寫了getConfigurationPhase()方法,表示在注冊bean的時候注解生效:

public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
就從matches方法開始:

//SpringBootCondition類中的matches方法
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//獲取當前的類名或者方法名(由標注的位置決定)
String classOrMethodName = getClassOrMethodName(metadata);

try {
//關鍵代碼:這里就會判斷出結果
ConditionOutcome outcome = this.getMatchOutcome(context, metadata);
//存入日志
this.logOutcome(classOrMethodName, outcome);
//存入記錄
this.recordEvaluation(context, classOrMethodName, outcome);
//最后返回ConditionOutcome的isMatch就是返回boolean類型結果
return outcome.isMatch();
} catch (NoClassDefFoundError var5) {
throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to " + var5.getMessage() + " not found. Make sure your own configuration does not rely on that class. This can also happen if you are @ComponentScanning a springframework package (e.g. if you put a @ComponentScan in the default package by mistake)", var5);
} catch (RuntimeException var6) {
throw new IllegalStateException("Error processing condition on " + this.getName(metadata), var6);
}
}
關鍵代碼在OnBeanCondition的getMatchOutcome方法上:

/**
* 獲得判斷結果的方法,ConditionOutcome類中存着boolean類型的結果
*/
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
//返回一個新的ConditionMessage
ConditionMessage matchMessage = ConditionMessage.empty();
OnBeanCondition.BeanSearchSpec spec;
List matching;
//這是metadata會調用isAnnotated方法判斷當前標注的注解是不是ConditionalOnMissingBean
//其實@ConditionalOnBean、@ConditionalOnMissingBean和@ConditionalOnSingleCandidate都是使用這個條件類,所以這里做判斷
if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
spec = new OnBeanCondition.BeanSearchSpec(context, metadata, ConditionalOnBean.class);
matching = this.getMatchingBeans(context, spec);
if (matching.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnBean.class, new Object[]{spec}).didNotFind("any beans").atAll());
}

matchMessage = matchMessage.andCondition(ConditionalOnBean.class, new Object[]{spec}).found("bean", "beans").items(Style.QUOTE, matching);
}

if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
OnBeanCondition.BeanSearchSpec spec = new OnBeanCondition.SingleCandidateBeanSearchSpec(context, metadata, ConditionalOnSingleCandidate.class);
matching = this.getMatchingBeans(context, spec);
if (matching.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSingleCandidate.class, new Object[]{spec}).didNotFind("any beans").atAll());
}

if (!this.hasSingleAutowireCandidate(context.getBeanFactory(), matching, spec.getStrategy() == SearchStrategy.ALL)) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSingleCandidate.class, new Object[]{spec}).didNotFind("a primary bean from beans").items(Style.QUOTE, matching));
}

matchMessage = matchMessage.andCondition(ConditionalOnSingleCandidate.class, new Object[]{spec}).found("a primary bean from beans").items(Style.QUOTE, matching);
}

//如果當前注入的bean是@ConditionalOnMissingBean
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
//返回一個spec(說明),這里的spec規定了搜索的內容,比如搜索策略、需要搜索的類名......
spec = new OnBeanCondition.BeanSearchSpec(context, metadata, ConditionalOnMissingBean.class);
//主要的搜索實現在這個方法里,最后返回一個list
matching = this.getMatchingBeans(context, spec);
//判斷搜索出來的結果
if (!matching.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingBean.class, new Object[]{spec}).found("bean", "beans").items(Style.QUOTE, matching));
}

matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, new Object[]{spec}).didNotFind("any beans").atAll();
}

return ConditionOutcome.match(matchMessage);
}
spec = new OnBeanCondition.BeanSearchSpec(context, metadata, ConditionalOnBean.class);

這句中,相當於從內部類中將標注@ConditionalOnMissingBean注解時的屬性都取出來:

BeanSearchSpec(ConditionContext context, AnnotatedTypeMetadata metadata, Class<?> annotationType) {
this.annotationType = annotationType;
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(annotationType.getName(), true);
//將attributes這個map中的數據放到對應的list成員變量中
this.collect(attributes, "name", this.names);
this.collect(attributes, "value", this.types);
this.collect(attributes, "type", this.types);
this.collect(attributes, "annotation", this.annotations);
this.collect(attributes, "ignored", this.ignoredTypes);
this.collect(attributes, "ignoredType", this.ignoredTypes);
this.strategy = (SearchStrategy)metadata.getAnnotationAttributes(annotationType.getName()).get("search");
OnBeanCondition.BeanTypeDeductionException deductionException = null;

try {
if (this.types.isEmpty() && this.names.isEmpty()) {
this.addDeducedBeanType(context, metadata, this.types);
}
} catch (OnBeanCondition.BeanTypeDeductionException var7) {
deductionException = var7;
}

this.validate(deductionException);
}

//驗證的方法
protected void validate(OnBeanCondition.BeanTypeDeductionException ex) {
if (!this.hasAtLeastOne(this.types, this.names, this.annotations)) {
String message = this.annotationName() + " did not specify a bean using type, name or annotation";
if (ex == null) {
throw new IllegalStateException(message);
} else {
throw new IllegalStateException(message + " and the attempt to deduce the bean's type failed", ex);
}
}
}
看一下OnBeanCondition類中的getMatchingBeans方法,里面有用到搜索策略,詳見搜索策略介紹

private List<String> getMatchingBeans(ConditionContext context, OnBeanCondition.BeanSearchSpec beans) {
//獲得當前bean工廠
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//判斷當前的搜索策略是否是PARENTS或者ANCESTORS,默認是ALL
if (beans.getStrategy() == SearchStrategy.PARENTS || beans.getStrategy() == SearchStrategy.ANCESTORS) {
BeanFactory parent = beanFactory.getParentBeanFactory();
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent, "Unable to use SearchStrategy.PARENTS");
//如果是PARENTS或者ANCESTORS,當前bean工廠就用父工廠
beanFactory = (ConfigurableListableBeanFactory)parent;
}

if (beanFactory == null) {
return Collections.emptyList();
} else {
List<String> beanNames = new ArrayList();
//如果當前搜索策略等於CURRENT,為true
boolean considerHierarchy = beans.getStrategy() != SearchStrategy.CURRENT;
//這里的type就是需要查找的bean的類型
//下面,會從屬性中找bean
Iterator var6 = beans.getTypes().iterator();

String beanName;
while(var6.hasNext()) {
beanName = (String)var6.next();
//如果找到了類型,接下來就是根據類型找bean的實例名,找示例名的方法在下方,實際上就是一個getNamesForType
beanNames.addAll(this.getBeanNamesForType(beanFactory, beanName, context.getClassLoader(), considerHierarchy));
}

var6 = beans.getIgnoredTypes().iterator();

while(var6.hasNext()) {
beanName = (String)var6.next();
beanNames.removeAll(this.getBeanNamesForType(beanFactory, beanName, context.getClassLoader(), considerHierarchy));
}

var6 = beans.getAnnotations().iterator();

while(var6.hasNext()) {
beanName = (String)var6.next();
beanNames.addAll(Arrays.asList(this.getBeanNamesForAnnotation(beanFactory, beanName, context.getClassLoader(), considerHierarchy)));
}

var6 = beans.getNames().iterator();

while(var6.hasNext()) {
beanName = (String)var6.next();
if (this.containsBean(beanFactory, beanName, considerHierarchy)) {
beanNames.add(beanName);
}
}
//將存放bean實例名的list返回
return beanNames;
}
}



//根據類型獲取bean的name
private Collection<String> getBeanNamesForType(ListableBeanFactory beanFactory, String type, ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {
try {
Set<String> result = new LinkedHashSet();
this.collectBeanNamesForType(result, beanFactory, ClassUtils.forName(type, classLoader), considerHierarchy);
return result;
} catch (ClassNotFoundException var6) {
return Collections.emptySet();
} catch (NoClassDefFoundError var7) {
return Collections.emptySet();
}
}

private void collectBeanNamesForType(Set<String> result, ListableBeanFactory beanFactory, Class<?> type, boolean considerHierarchy) {
result.addAll(BeanTypeRegistry.get(beanFactory).getNamesForType(type));
if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
BeanFactory parent = ((HierarchicalBeanFactory)beanFactory).getParentBeanFactory();
if (parent instanceof ListableBeanFactory) {
this.collectBeanNamesForType(result, (ListableBeanFactory)parent, type, considerHierarchy);
}
}

}
找完bean了之后,回到剛才的代碼里:

//如果當前注入的bean是@ConditionalOnMissingBean
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
//返回一個spec(說明),這里的spec規定了搜索的內容,比如搜索策略、需要搜索的類名......
spec = new OnBeanCondition.BeanSearchSpec(context, metadata, ConditionalOnMissingBean.class);
matching = this.getMatchingBeans(context, spec);
if (!matching.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingBean.class, new Object[]{spec}).found("bean", "beans").items(Style.QUOTE, matching));
}

matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, new Object[]{spec}).didNotFind("any beans").atAll();
}
如果第5行返回的list不是空的,就會返回ConditionOutcome對象noMatch方法,表示不匹配。ConditionOutcome類用於存放過濾結果,只有兩個變量:

/**
* 過濾結果類
*/
public class ConditionOutcome {
/**
* 匹配結果 true or false
*/
private final boolean match;
/**
* 匹配結果信息
*/
private final ConditionMessage message;
兩者區別:
@ConditionOnBean在判斷list的時候,如果list沒有值,返回false,否則返回true

@ConditionOnMissingBean在判斷list的時候,如果list沒有值,返回true,否則返回false,其他邏輯都一樣

例子:
@ConditionalOnBean(javax.sql.DataSource.class)    
Spring容器或者所有父容器中需要存在至少一個javax.sql.DataSource類的實例

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM