抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

SpringBoot源码分析-Condition原理

概要

在项目中,有时会遇到我们的Configuration、Bean、Service等等的bean组件需要依条件按需加载的情况。

springboot中提供了一系列@Condition* 注解来处理有条件注入的情况。

​ Spring4中增加了@Condition annotation, 使用该Annotation之后,在做依赖注入的时候,会检测是否满足某个条件来决定是否注入某个类。

​ springboot基于spring4的这个注解实现了多个用于判断的条件注解,如果我们在使用中这些注解无法满足我们的要求还可以使用@Conditional自定义条件注解

常用的条件注解

条件注解 对应的Condition 处理类 处理逻辑
@ConditionalOnBean OnBeanCondition Spring容器中是否存在对应的实例。可以通过实例的类型、类名、注解、昵称去容器中查找(可以配置从当前容器中查找或者父容器中查找或者两者一起查找)
@ConditionalOnClass OnClassCondition 类加载器中是否存在对应的类。可以通过Class指定(value属性)或者Class的全名指定(name属性)如果是多个类或者多个类名的话,关系是”与”关系,也就是说这些类或者类名都必须同时在类加载器中存在
@ConditionalOnExpression OnExpressionCondition 判断SpEL 表达式是否成立
@ConditionalOnMissingBean OnBeanCondition Spring容器中是否缺少对应的实例。可以通过实例的类型、类名、注解、昵称去容器中查找(可以配置从当前容器中查找或者父容器中查找或者两者一起查找)
@ConditionalOnMissingClass OnClassCondition 跟ConditionalOnClass的处理逻辑一样,只是条件相反,在类加载器中不存在对应的类
@ConditionalOnProperty OnPropertyCondition 应用环境中的屬性是否存在。提供prefix、name、havingValue以及matchIfMissing属性。prefix表示属性名的前缀,name是属性名,havingValue是具体的属性值,matchIfMissing是个boolean值,如果属性不存在,这个matchIfMissing为true的话,会继续验证下去,否则属性不存在的话直接就相当于匹配不成功
@ConditionalOnResource OnResourceCondition 是否存在指定的资源文件。只有一个属性resources,是个String数组。会从类加载器中去查询对应的资源文件是否存在
@ConditionalOnSingleCandidate OnBeanCondition Spring容器中是否存在且只存在一个对应的实例。只有3个属性value、type、search。跟ConditionalOnBean中的这3种属性值意义一样
@ConditionalOnWebApplication OnWebApplicationCondition 应用程序是否是Web程序,没有提供属性,只是一个标识。会从判断Web程序特有的类是否存在,环境是否是Servlet环境,容器是否是Web容器等

举例

例子 说明
@ConditionalOnBean(javax.sql.DataSource.class) Spring容器或者所有父容器中需要存在至少一个javax.sql.DataSource类的实例
@ConditionalOnClass({ Configuration.class,FreeMarkerConfigurationFactory.class }) 类加载器中必须存在Configuration和FreeMarkerConfigurationFactory这两个类
@ConditionalOnExpression(“’${server.host}’==’localhost’”) server.host配置项的值需要是localhost
ConditionalOnJava(JavaVersion.EIGHT) Java版本至少是8
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT) Spring当前容器中不存在ErrorController类型的bean
@ConditionalOnMissingClass(“GenericObjectPool”) 类加载器中不能存在GenericObjectPool这个类
@ConditionalOnNotWebApplication 必须在非Web应用下才会生效
@ConditionalOnProperty(prefix = “spring.aop”, name = “auto”, havingValue = “true”, matchIfMissing = true) 应用程序的环境中必须有spring.aop.auto这项配置,且它的值是true或者环境中不存在spring.aop.auto配置(matchIfMissing为true)
@ConditionalOnResource(resources=”mybatis.xml”) 类加载路径中必须存在mybatis.xml文件
@ConditionalOnSingleCandidate(PlatformTransactionManager.class) Spring当前或父容器中必须存在PlatformTransactionManager这个类型的实例,且只有一个实例
@ConditionalOnWebApplication 必须在Web应用下才会生效

自定义条件注解

实现条件化注解我们需要两个类

  • 自定义注解类
    定义注解,指定判断用的条件类
  • 条件类
    实现org.springframework.context.annotation.Condition接口,定义判断条件

实现

定义注解类

要使用注解@Conditional(CustomConditionalOnProperty.class),CustomConditional为我们要定义的条件类

1
2
3
4
5
6
7
8
9
10
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(CustomConditional.class)
public @interface CustomConditionalOnProperty {
String value();

String havingValue() default "";

}
定义条件类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class CustomConditional implements Condition {

@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(CustomConditionalOnProperty.class.getName());
String propertyName = (String) annotationAttributes.get("value");
String value = (String) annotationAttributes.get("havingValue");
//获取环境变量中的参数
String propertyValue = context.getEnvironment().getProperty(propertyName);
//如果环境变量的参数是对应的value值则匹配
if (propertyValue.equalsIgnoreCase(value)) {
return true;
}
return false;
}
}
配置配置文件

因为是根据属性文件创建Bean,所以需要在先在application.properties 中增加custom.user=create

1
custom.user=create
测试
1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class ConditionalConfigTest {
@Bean
@CustomConditionalOnProperty(value = "custom.user", havingValue = "create")
public User getUser() {
User user = new User();
user.setUserName("baiyp");
user.setPassword("123456");
System.out.println("xxxxxxxxxxxxxxxxxxx");
return user;
}
}

打断点发现,如果custom.user=create就会创建bean,否则不会创建bean

Conditional注解的原理

Condition介绍

Condition类中只有一个方法matches方法。这类的作用是,在bean的定义即将被注册之前,会检查条件是否匹配,然后根据匹配的结果决定是否注册bean。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* A single {@code condition} that must be {@linkplain #matches matched} in order
* for a component to be registered.
*
* <p>Conditions are checked immediately before the bean-definition is due to be
* registered and are free to veto registration based on any criteria that can
* be determined at that point.
*
* <p>Conditions must follow the same restrictions as {@link BeanFactoryPostProcessor}
* and take care to never interact with bean instances. For more fine-grained control
* of conditions that interact with {@code @Configuration} beans consider the
* {@link ConfigurationCondition} interface.
*
* @author Phillip Webb
* @since 4.0
* @see ConfigurationCondition
* @see Conditional
* @see ConditionContext
*/
@FunctionalInterface
public interface Condition {

/**
* Determine if the condition matches.
* @param context the condition context
* @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
* or {@link org.springframework.core.type.MethodMetadata method} being checked
* @return {@code true} if the condition matches and the component can be registered,
* or {@code false} to veto the annotated component's registration
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

检查条件是否匹配的方法就是matches,在源码的类描述中提到了一点。如果实现的是这个类,那么必须注意,在实现方法也就是matches中不能与bean的实例交互。之所以要注意这一点是因为这个方法的调用时间在bean的实例化之前的,此时如果跟实例交互就会提前实例化bean,可能会引起错误。如果想要对贴有@Configuration标签的bean更细粒度的控制可以通过实现ConfigurationCondition来完成。

找到入口

经过上面的简单示例,对于@Conditional注解的使用大家应该清楚了,如果matches方法返回false,那么这个类就不会被扫描,反之则会被扫描进spring容器。下面就来了解一下他们的原理。

我们在自动装配的地方讲过一个地方过滤类,关键地方是在conditionEvaluator.shouldSkip这里面讲解下

代码的位置是在

run->refreshContext->refresh->AbstractApplicationContext#refresh->invokeBeanFactoryPostProcessors->invokeBeanFactoryPostProcessors->invokeBeanDefinitionRegistryPostProcessors->ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry->processConfigBeanDefinitions->ConfigurationClassParser#parse->parse->processConfigurationClass->shouldSkip

1
2
3
4
5
6
7
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
//过滤bean
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
.......
}

ConditionEvaluator实例化

shouldSkip方法就是判断@Conditional注解的地方(这个shouldSkip方法其他地方也有,但是基本原理都是一样的,或者说就是一样的),在进入之前,我们先了解一下他的参数以及conditionEvaluator。找到当前类的构造函数,发现如下信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//ConfigurationClassParser构造方法
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {

.....
this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}

//conditionEvaluator构造方法
public ConditionEvaluator(@Nullable BeanDefinitionRegistry registry,
@Nullable Environment environment, @Nullable ResourceLoader resourceLoader) {

this.context = new ConditionContextImpl(registry, environment, resourceLoader);
}

ConditionEvaluator类处理match方法

通过查看Conditionmatches在哪里被调用。发现整个spring中只有在ConditionEvaluator中调用了这个方法。这个类的作用是评估一个贴了Conditional注解的类是否需要跳过。通过类上面的注解来判断。进入到类方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
//检查注解中是否包含@Conditional类型的注解
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}

//如果没有指定了当前bean是解析还是注册
if (phase == null) {
//bean的注解信息封装对象是AnnotationMetadata类型并且,类上有@Component,@ComponentScan,@Import,@ImportResource,则表示为解析类型
if (metadata instanceof AnnotationMetadata &&
ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
}
return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
}

List<Condition> conditions = new ArrayList<>();
//从bean的注解信息封装对象中获取所有的Conditional类型或者Conditional的派生注解
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
//实例化Conditional中的条件判断类(Condition的子类)
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
//添加到条件集合中
conditions.add(condition);
}
}
//根据Condition的优先级进行排序
AnnotationAwareOrderComparator.sort(conditions);

//依次判断每个类的matches方法,有一个方法返回false则跳过这个类
for (Condition condition : conditions) {
ConfigurationPhase requiredPhase = null;
//如果是ConfigurationCondition类型的Condition
if (condition instanceof ConfigurationCondition) {
//获取需要对bean进行的操作,是解析还是注册
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
//(如果requiredPhase==null或者指定的操作类型是目前阶段的操作类型)并且不符合设置的条件则跳过
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
}

return false;
}

 上面这个方法作用就是判断当前bean处于解析还是注册,如果处于解析阶段则跳过,如果处于注册阶段则不跳过。其中Conditionmatches方法就起到了判断的是否符合的作用,进而觉得是否跳过当前bean。

​ shouldSkip方法的逻辑不复杂,获取所有conditional注解里的参数类,依次调用matches方法,如果任意方法返回false则跳过该类。所以在这儿,我们就看到了matches方法的参数以及调用。这样的话,conditional注解的原理大家应该没啥问题了。

springboot中的扩展

在spring中实现Condition接口的类很少,在springboot中才广泛的运用到了。而springboot也在spring的基础上做了一个基础的扩展实现,然后再在这个基础的扩展实现上进一步扩展的。这个扩展的实现类就是SpringBootCondition类。现在进入到这个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public abstract class SpringBootCondition implements Condition {
@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//从封装condition注解信息的类中获取指定的Condition类的实现类
String classOrMethodName = getClassOrMethodName(metadata);
try {
//确定匹配结果以及日志输出对象
ConditionOutcome outcome = getMatchOutcome(context, metadata);
//打印匹配的情况,如果是不匹配会打印不匹配的原因
logOutcome(classOrMethodName, outcome);
//将匹配结果进行存储
recordEvaluation(context, classOrMethodName, outcome);
//返回匹配的结果
return outcome.isMatch();
}
catch (NoClassDefFoundError ex) {
throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to "
+ ex.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)", ex);
}
catch (RuntimeException ex) {
throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);
}
}

public abstract ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata);

}

 可以看到这里的扩展的就是对匹配的结果进行封装,然后打印以及存储。其中getMatchOutcome是由各个SpringBootCondition的实现类去实现的。作用就是判断各自按照各自的使用条件来判断是否符合来返回匹配的结果。

常用注解的原理

ConditionalOnClass

打开ConditionalOnClass注解的源代码,本身带有两个属性,一个class类型的value,一个String类型的name。同时ConditionalOnClass注解本身还带了一个@Conditional(OnClassCondition.class)注解。所以,其实ConditionalOnClass注解的判断条件就在于OnClassCondition这个类的matches方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {

/**
* The classes that must be present. Since this annotation is parsed by loading class
* bytecode, it is safe to specify classes here that may ultimately not be on the
* classpath, only if this annotation is directly on the affected component and
* <b>not</b> if this annotation is used as a composed, meta-annotation. In order to
* use this annotation as a meta-annotation, only use the {@link #name} attribute.
* @return the classes that must be present
*/
Class<?>[] value() default {};

/**
* The classes names that must be present.
* @return the class names that must be present.
*/
String[] name() default {};

}

直接进入OnClassCondition类,寻找matches方法。最终,在他的父类SpringBootCondition中,找到了matches方法。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取加上了@ConditionalOnClass注解的类或者方法的名称(我们就以类分析,加在方法上是一个原理)
String classOrMethodName = getClassOrMethodName(metadata);
try {
// 典型的钩子方法,调到用具体子类中方法。ConditionOutcome 这个类里面包装了是否需
// 跳过和打印的日志
ConditionOutcome outcome = getMatchOutcome(context, metadata);
logOutcome(classOrMethodName, outcome);
recordEvaluation(context, classOrMethodName, outcome);
return outcome.isMatch();
}
catch (NoClassDefFoundError ex) {
throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to "
+ ex.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)", ex);
}
catch (RuntimeException ex) {
throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);
}
}

从代码不难看出,关键方法在getMatchOutcome里,所以进入该方法。

找到实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ClassLoader classLoader = context.getClassLoader();
ConditionMessage matchMessage = ConditionMessage.empty();
List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
if (onClasses != null) {
// 核心方法,过滤一下,其实就是Class.forname一下ConditionalOnClass注解中的类,如果有异常就说明
// 上下文中没这个类,没有就过滤掉
List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);
if (!missing.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
.didNotFind("required class", "required classes").items(Style.QUOTE, missing));
}
matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
.found("required class", "required classes")
.items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader));
}
List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class);
if (onMissingClasses != null) {
List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader);
if (!present.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class)
.found("unwanted class", "unwanted classes").items(Style.QUOTE, present));
}
matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
.didNotFind("unwanted class", "unwanted classes")
.items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING, classLoader));
}
return ConditionOutcome.match(matchMessage);
}

该方法并不复杂,和ConditionalOnClass有关的代码主要有两行,getCandidates和filter。 首先看看getCandidates:

1
2
3
4
5
6
7
8
9
10
private List<String> getCandidates(AnnotatedTypeMetadata metadata, Class<?> annotationType) {
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(annotationType.getName(), true);
if (attributes == null) {
return null;
}
List<String> candidates = new ArrayList<>();
addAll(candidates, attributes.get("value"));
addAll(candidates, attributes.get("name"));
return candidates;
}

主要是获取了ConditionalOnClass的name属性和value属性。

接下来看看filter方法,在进入filter方法前,先看一下判断条件ClassNameFilter.MISSING

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
MISSING {

@Override
public boolean matches(String className, ClassLoader classLoader) {
return !isPresent(className, classLoader);
}

};
static boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
try {
resolve(className, classLoader);
return true;
}
catch (Throwable ex) {
return false;
}
}

protected static Class<?> resolve(String className, ClassLoader classLoader) throws ClassNotFoundException {
if (classLoader != null) {
return classLoader.loadClass(className);
}
return Class.forName(className);
}

逻辑很清晰,如果该类能被加载则判断成功,否则判断失败。现在进入filter方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
protected final List<String> filter(Collection<String> classNames, ClassNameFilter classNameFilter,
ClassLoader classLoader) {
if (CollectionUtils.isEmpty(classNames)) {
return Collections.emptyList();
}
List<String> matches = new ArrayList<>(classNames.size());
for (String candidate : classNames) {
if (classNameFilter.matches(candidate, classLoader)) {
matches.add(candidate);
}
}
return matches;
}

filter方法就是利用刚刚的判断条件进行判断,发现不符合的添加进list一并返回,最后生成结果。

ConditionalOnBean

跟ConditionalOnClass类似打开注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {

/**
* The class types of beans that should be checked. The condition matches when beans
* of all classes specified are contained in the {@link BeanFactory}.
* @return the class types of beans to check
*/
Class<?>[] value() default {};

/**
* The class type names of beans that should be checked. The condition matches when
* beans of all classes specified are contained in the {@link BeanFactory}.
* @return the class type names of beans to check
*/
String[] type() default {};

/**
* The annotation type decorating a bean that should be checked. The condition matches
* when all of the annotations specified are defined on beans in the
* {@link BeanFactory}.
* @return the class-level annotation types to check
*/
Class<? extends Annotation>[] annotation() default {};

/**
* The names of beans to check. The condition matches when all of the bean names
* specified are contained in the {@link BeanFactory}.
* @return the names of beans to check
*/
String[] name() default {};

/**
* Strategy to decide if the application context hierarchy (parent contexts) should be
* considered.
* @return the search strategy
*/
SearchStrategy search() default SearchStrategy.ALL;

/**
* Additional classes that may contain the specified bean types within their generic
* parameters. For example, an annotation declaring {@code value=Name.class} and
* {@code parameterizedContainer=NameRegistration.class} would detect both
* {@code Name} and {@code NameRegistration<Name>}.
* @return the container types
* @since 2.1.0
*/
Class<?>[] parameterizedContainer() default {};

}

进入OnBeanCondition类找matchs方法,最终,在他的父类SpringBootCondition中,找到了matches方法。跟上面一样

找到关键实现类的getMatchOutcome方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage matchMessage = ConditionMessage.empty();
// 获取类上面的注解
MergedAnnotations annotations = metadata.getAnnotations();
if (annotations.isPresent(ConditionalOnBean.class)) {
// 包装一下
Spec<ConditionalOnBean> spec = new Spec<>(context, metadata, annotations, ConditionalOnBean.class);
// 核心方法,其实就是判断spring容器中是否有ConditionalOnBean注解中的bean
MatchResult matchResult = getMatchingBeans(context, spec);
if (!matchResult.isAllMatched()) {
String reason = createOnBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(spec.message().because(reason));
}
matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE,
matchResult.getNamesOfAllMatches());
}
if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
Spec<ConditionalOnSingleCandidate> spec = new SingleCandidateSpec(context, metadata, annotations);
MatchResult matchResult = getMatchingBeans(context, spec);
if (!matchResult.isAllMatched()) {
return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll());
}
else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matchResult.getNamesOfAllMatches(),
spec.getStrategy() == SearchStrategy.ALL)) {
return ConditionOutcome.noMatch(spec.message().didNotFind("a primary bean from beans")
.items(Style.QUOTE, matchResult.getNamesOfAllMatches()));
}
matchMessage = spec.message(matchMessage).found("a primary bean from beans").items(Style.QUOTE,
matchResult.getNamesOfAllMatches());
}
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations,
ConditionalOnMissingBean.class);
MatchResult matchResult = getMatchingBeans(context, spec);
if (matchResult.isAnyMatched()) {
String reason = createOnMissingBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(spec.message().because(reason));
}
matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
}
return ConditionOutcome.match(matchMessage);
}

核心方法是getMatchingBeans,核心逻辑是从BeanFactory中判断给类型的bean是否存在,如果存在则继续执行,否则就不执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
protected final MatchResult getMatchingBeans(ConditionContext context, Spec<?> spec) {
ClassLoader classLoader = context.getClassLoader();
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT;
Set<Class<?>> parameterizedContainers = spec.getParameterizedContainers();
if (spec.getStrategy() == SearchStrategy.ANCESTORS) {
BeanFactory parent = beanFactory.getParentBeanFactory();
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
"Unable to use SearchStrategy.ANCESTORS");
beanFactory = (ConfigurableListableBeanFactory) parent;
}
MatchResult result = new MatchResult();
Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,
spec.getIgnoredTypes(), parameterizedContainers);
for (String type : spec.getTypes()) {
// 这里明显是根据注解中的类型从spring容器中获取bean,如果能获取到说明是匹配的
Collection<String> typeMatches = getBeanNamesForType(classLoader, considerHierarchy, beanFactory, type,
parameterizedContainers);
typeMatches.removeAll(beansIgnoredByType);
if (typeMatches.isEmpty()) {
result.recordUnmatchedType(type);
}
else {
result.recordMatchedType(type, typeMatches);
}
}
for (String annotation : spec.getAnnotations()) {
Set<String> annotationMatches = getBeanNamesForAnnotation(classLoader, beanFactory, annotation,
considerHierarchy);
annotationMatches.removeAll(beansIgnoredByType);
if (annotationMatches.isEmpty()) {
result.recordUnmatchedAnnotation(annotation);
}
else {
result.recordMatchedAnnotation(annotation, annotationMatches);
}
}
for (String beanName : spec.getNames()) {
if (!beansIgnoredByType.contains(beanName) && containsBean(beanFactory, beanName, considerHierarchy)) {
result.recordMatchedName(beanName);
}
else {
result.recordUnmatchedName(beanName);
}
}
return result;
}

评论