[SpringBoot源码分析二]:@Condition
- 背景介绍
在整个SpringBoot项目中关于Bean的注册,我们可能需要指示Bean只有在所有条件满足的情况下才有资格注册到容器中,比如说像下面这个例子,如果说我们已经注册过ViewResolver自然不需要SpringMVC帮我们注册了
上面的
ConditionalOnBean
正是Conditional
的一种延伸本文将围绕着SpringBoot中如何利用
Conditional
及其子注解进行条件判断来讲解 -
@Conditional
我们先看看这个注解的样子,发现这个接口其实就是传入一个
Condition
类作为检查类... public @interface Conditional { /** * 检查类 */ 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(); // 获得所有可以忽略的Bean名称 Set beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy, spec.getIgnoredTypes(), parameterizedContainers); // 类型的匹配 for (String type : spec.getTypes()) { // 获得指定类型Bean的名称 Collection 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 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; }
-
@ConditionalOnClass
@ConditionalOnClass
:检查类是OnClassCondition
,要求能够通过ClassLoader
加载到指定的类public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ClassLoader classLoader = context.getClassLoader(); ConditionMessage matchMessage = ConditionMessage.empty(); // ConditionalOnClass的情况 List onClasses = getCandidates(metadata, ConditionalOnClass.class); if (onClasses != null) { // 对传入的类进行过滤,返回不能加载的类 List 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)); } // ConditionalOnMissingClass的情况 List onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class); if (onMissingClasses != null) { // 对传入的类进行过滤,返回能加载的类 List 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); }
其实这个类没什么好讲的,但是这个类实现了
FilteringSpringBootCondition
, 这里针对于自动配置类的过滤,做了特殊的设置,那么紧接着就来看看大家看我的注释就知道,这里可以通过两个线程进行检查自动配置类
protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) { //如果系统内核大于1,就多开一个线程进行匹配 if (Runtime.getRuntime().availableProcessors() > 1) { return resolveOutcomesThreaded(autoConfigurationClasses, autoConfigurationMetadata); } //一个线程进行匹配 else { OutcomesResolver outcomesResolver = new StandardOutcomesResolver(autoConfigurationClasses, 0, autoConfigurationClasses.length, autoConfigurationMetadata, getBeanClassLoader()); return outcomesResolver.resolveOutcomes(); } }
resolveOutcomesThreaded(...)方法中会将自动配置类分为两部分,分别交给了
StandardOutcomesResolver
和ThreadedOutcomesResolver
去处理private ConditionOutcome[] resolveOutcomesThreaded(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) { // 确定第一个线程的结束位置和第二个线程的开始位置 int split = autoConfigurationClasses.length / 2; // 创建一个多线程的匹配类 OutcomesResolver firstHalfResolver = createOutcomesResolver(autoConfigurationClasses, 0, split, autoConfigurationMetadata); // 创建第二个用当前线程的匹配类 OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(autoConfigurationClasses, split, autoConfigurationClasses.length, autoConfigurationMetadata, getBeanClassLoader()); // 开始匹配 ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes(); ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes(); // 封账匹配结果,并返回 ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length]; System.arraycopy(firstHalf, 0, outcomes, 0, firstHalf.length); System.arraycopy(secondHalf, 0, outcomes, split, secondHalf.length); return outcomes; }
StandardOutcomesResolver
其实就很简单就for循环检查,而ThreadedOutcomesResolver
就会利用多线程去进行匹配 - 其他注解
至于其他的注解其实不太常见,我这就就简单做个介绍
@OnExpressionCondition
:支持以SpEL表达式去解析@ConditionalOnJava
:要求JVM版本范围的@ConditionalOnWebApplication
:要求当前Web程序类型,是Servlet还是Reactive- ...