Condition
条件满足返回true,条件不满足返回false
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
几个子接口、类
Condition有几个子接口、类,对其功能做了扩展
ConfigurationCondition
public interface ConfigurationCondition extends Condition {
ConfigurationPhase getConfigurationPhase();
enum ConfigurationPhase {
PARSE_CONFIGURATION,
REGISTER_BEAN
}
}
ConfigurationCondition接口加了一个描述该Condition阶段的方法,只有调用Condition的阶段跟该Condition的阶段相匹配,matches方法才会被调用。ConfigurationPhase定义了两个阶段:
PARSE_CONFIGURATION:配置类( @Configuration)解析阶段
REGISTER_BEAN:普通bean的注册阶段(包括配置类的注册)
SpringBootConfiguration
public abstract class SpringBootCondition implements Condition {
private final Log logger = LogFactory.getLog(getClass());
@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
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);
}
}
}
SpringBootCondition是一个抽象类,使用模板模式实现了接口方法,做了三件事:
1、getMatchOutcome生成ConditionOutcome,是个抽象方法(唯一的一个需要子类实现的方法)
2、落日志,等级trace
3、调用ConditionEvaluationReport报告结果
ConditionOutcome记录了匹配结果和message
public class ConditionOutcome {
private final boolean match;
private final ConditionMessage message;
}
FilteringSpringBootCondition
类定义
abstract class FilteringSpringBootCondition extends SpringBootCondition
implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware
FilteringSpringBootCondition扩展了SpringBootCondition,还实现了AutoConfigurationImportFilter接口。扩展SpringBootCondition接口并未实现其抽象方法,与SpringBootCondition完全一样;实现AutoConfigurationImportFilter接口部分需要看一下
AutoConfigurationImportFilter
@FunctionalInterface
public interface AutoConfigurationImportFilter {
boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata);
}
AutoConfigurationImportFilter接口是干嘛的?SpringBoot会把AutoConfiguration配置类写在spring.factories文件里,会写的非常全,实际上工程里大部分都用不到,需要提前过滤掉一大部分。传进来的autoConfigurationClasses是需要过滤的类,返回boolean[]数组,即每个类条件是否match,为true则条件match,该AutoConfiguration类应保留;为false则条件不match,该AutoConfiguration类应过滤掉
FilteringSpringBootCondition实现
abstract class FilteringSpringBootCondition extends SpringBootCondition
implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {
private BeanFactory beanFactory;
private ClassLoader beanClassLoader;
@Override
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++) {
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
}
}
}
return match;
}
}
FilteringSpringBootCondition实现了AutoConfigurationImportFilter接口的match方法,实际上也是采用模板模式,做了4件事情
1、调用抽象方法getOutcomes,获取每个自动配置类的ConditionOutcome数组
2、for循环里拼装返回结果:outcome是null或者outcome isMatch,这个类配置都match
3、记录日志
4、报告结果
这里再介绍这个类里面的一个重要方法filter,后面多处都在使用
filter方法会传进来一组类名classNames和classNameFilter,作用是返回满足classNameFilter条件的类
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;
}
ClassNameFilter是一个枚举,有PRESENT、MISSING两个
ClassNameFilter里isPresent方法就是调用Class.forName判断这个类是否存在,再看两个枚举实现的matches方法可以得知:枚举PRESENT当类存在时返回true,枚举MISSING当类不存在时返回true
protected enum ClassNameFilter {
PRESENT {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return isPresent(className, classLoader);
}
},
MISSING {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return !isPresent(className, classLoader);
}
};
abstract boolean matches(String className, ClassLoader 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 Class.forName(className, false, classLoader);
}
return Class.forName(className);
}
所以filter(Collection classNames, ClassNameFilter classNameFilter, ClassLoader classLoader)这个方法的用处就是:ClassNameFilter传PRESENT时,返回classNames中存在的类;ClassNameFilter传MISSING时,返回classNames中不存在的类
在哪里用
配置类解析&bean注册
1、ConfigurationClassParser#processConfigurationClass
ConfigurationClassParser在解析配置类时
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
...
}
2、ConfigurationClassParser#doProcessConfigurationClass处理配置类的ComponentScan注解时
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
...
// Process any @ComponentScan annotations
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
...
}
3、ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod注册@Bean时
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
...
if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
configClass.skippedBeanMethods.add(methodName);
return;
}
...
}
4、AnnotatedBeanDefinitionReader#doRegisterBean注册bean时
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
@Nullable BeanDefinitionCustomizer[] customizers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
}
这些使用场景都时借助ConditionEvaluator来调用Condition来获取match结果,主要是shouldSkip方法,返回true表示条件不满足,需要跳过该配置类或者不注册该bean
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
// 1
if (phase == null) {
if (metadata instanceof AnnotationMetadata &&
ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
}
return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
}
// 2
List<Condition> conditions = new ArrayList<>();
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
}
}
// 3
AnnotationAwareOrderComparator.sort(conditions);
// 4
for (Condition condition : conditions) {
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
}
return false;
}
实现细节
1、如果phase为null,则推断当前的phase。如果该类是一个配置类,phase就是PARSE_CONFIGURATION,否则REGISTER_BEAN
怎么判断是不是一个配置类?其实就是看是否标了特定注解、是否有@bean方法
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// Do not consider an interface or an annotation...
if (metadata.isInterface()) {
return false;
}
// Any of the typical annotations found?
// @Component、@ComponentScan、@Import、@ImportResource
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
// Finally, let's look for @Bean methods...
return hasBeanMethods(metadata);
}
2、获取Condition类并实例化
获取Condition类就是从@Conditional及其派生注解里获取到;实例化通过反射调用构造方法实现。一个配置类上或注册bean时,可能会指定多个条件,所有是一个List
3、对Condition进行排序
调用AnnotationAwareOrderComparator对Condition进行排序,AnnotationAwareOrderComparator能够将PriorityOrdered接口、Ordered接口、@Order指定的顺序都考虑进来
4、调用这一组条件
for循环调用这一组条件,调用条件前会判断phase是否匹配:如果condition未指定phase(直接实现Conditon接口而未实现ConfigurationConditon接口)或者condition的phase和当前phase相同,则会调用Condition接口。
并且只要有一个条件不满足,就会返回true,表明需要跳过该配置类或者不注册该bean;所有条件都满足,才会返回false
AutoConfiguration过滤自动配置类
AutoConfiguration的spring.factories里会写上很多自动配置类,实际工程中只会用到极少数,因此都读到的配置类会先过滤掉。主要逻辑在AutoConfigurationImportSelector#getAutoConfigurationEntry方法,getCandidateConfigurations(annotationMetadata, attributes)通过spring.factories spi获取到所有的AutoConfiguration类名称。下面看是如何过滤的
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
1、getConfigurationClassFilter()获取Filter
private ConfigurationClassFilter getConfigurationClassFilter() {
if (this.configurationClassFilter == null) {
List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
for (AutoConfigurationImportFilter filter : filters) {
invokeAwareMethods(filter);
}
this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
}
return this.configurationClassFilter;
}
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
这里就是通过SpringFactoriesLoader获取AutoConfigurationImportFilter实例,然后封装成ConfigurationClassFilter对象
spring.factories里配置了什么?其实就是配置了3个Condition,OnBeanCondition、OnClassCondition、OnWebApplicationCondition。也可以自行扩展
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
ConfigurationClassFilter是什么?其实就是把配置的AutoConfigurationImportFilter存了一下
private static class ConfigurationClassFilter {
private final AutoConfigurationMetadata autoConfigurationMetadata;
private final List<AutoConfigurationImportFilter> filters;
}
2、AutoConfigurationImportSelector.ConfigurationClassFilter#filter过滤掉不满足Condition过滤掉
List<String> filter(List<String> configurations) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean skipped = false;
for (AutoConfigurationImportFilter filter : this.filters) {
boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (String candidate : candidates) {
if (candidate != null) {
result.add(candidate);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return result;
}
主要是第一个for循环,调用所有的filter,把不满足condition的配置类都过滤掉
用法&例子
@Conditional
就是一个注解,可以指定一组Condition,还需要其他地方获取这些Condition才能生效,例如前面的ConditionEvaluator和AutoConfigurationImportSelector
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
@Conditional也是一个元注解,能够用在其他注解上,利用spring的注解机制也能够获取到Condition,例如@ConditionalOnBean、@ConditionalOnClass,也能够容易的扩展自己的注解
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
...
}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
...
}
OnBeanCondition
OnBeanCondition是一个Condition实现,类定义如下
class OnBeanCondition extends FilteringSpringBootCondition implements ConfigurationCondition {
}
结合前面介绍的FilteringSpringBootCondition抽象类和ConfigurationCondition接口,OnBeanCondition是可以有两种用法的:
1、作为ConfigurationCondition的实现,在ConditionEvaluator类中使用,主要是下面几个注解
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnSingleCandidate {}
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnMissingBean {}
2、是AutoConfigurationImportFilter的实现,在AutoConfigurationImportSelector类中过滤掉一部分配置类
分别来看
作为ConfigurationCondition
@Order(Ordered.LOWEST_PRECEDENCE)
class OnBeanCondition extends FilteringSpringBootCondition implements ConfigurationCondition {
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
@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);
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);
}
}
1、标注了@Order注解,会在ConditionEvaluator类中和其他条件一起排序
2、实现了getConfigurationPhase方法,该方法返回的是REGISTER_BEAN,意味着bean注册时该条件才会起作用。如果在配置类上使用该注解,解析配置类时该条件会被忽略
3、实现了getMatchOutcome方法,这个方法里处理@ConditionalOnBean、@ConditionalOnSingleCandidate、@ConditionalOnMissingBean三个注解
作为AutoConfigurationImportFilter
class OnBeanCondition extends FilteringSpringBootCondition implements ConfigurationCondition {
@Override
protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
for (int i = 0; i < outcomes.length; i++) {
String autoConfigurationClass = autoConfigurationClasses[i];
if (autoConfigurationClass != null) {
Set<String> onBeanTypes = autoConfigurationMetadata.getSet(autoConfigurationClass, "ConditionalOnBean");
outcomes[i] = getOutcome(onBeanTypes, ConditionalOnBean.class);
if (outcomes[i] == null) {
Set<String> onSingleCandidateTypes = autoConfigurationMetadata.getSet(autoConfigurationClass,
"ConditionalOnSingleCandidate");
outcomes[i] = getOutcome(onSingleCandidateTypes, ConditionalOnSingleCandidate.class);
}
}
}
return outcomes;
}
private ConditionOutcome getOutcome(Set<String> requiredBeanTypes, Class<? extends Annotation> annotation) {
List<String> missing = filter(requiredBeanTypes, ClassNameFilter.MISSING, getBeanClassLoader());
if (!missing.isEmpty()) {
ConditionMessage message = ConditionMessage.forCondition(annotation)
.didNotFind("required type", "required types").items(Style.QUOTE, missing);
return ConditionOutcome.noMatch(message);
}
return null;
}
}
作为AutoConfigurationImportFilter时,仅处理@ConditionalOnBean和@ConditionalOnSingleCandidate。getOutcomes通过for循环调用getOutcome处理每一个autoConfigurationClass,getOutcome调用的filter方法看requiredBeanTypes里面哪些bean beanFacotry不存在,如果有不存在的,返回noMatch;如果没有不存在的,返回null表示当前match,还需要继续看其他Condition
OnClassCondition
@Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends FilteringSpringBootCondition {
}
OnClassCondition实现了FilteringSpringBootCondition,和OnBeanCondition类似,也会有两个作用。但OnClassCondition没有实现ConfigurationCondition,所以在PARSE_CONFIGURATION、REGISTER_BEAN阶段都会起作用
看一下作为Condition的实现
OnClassCondition会处理@ConditionalOnClass和@ConditionalOnMissingClass两个注解,首先都会通过getCandidates获取注解上标注的类名,然后处理ConditionalOnClass时,通过filter获取标注了的但不存在的类,如果不为空则条件不满足;处理ConditionalOnMissingClass时,通过filter获取标注了的但存在的类,如果不为空则条件不满足
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) {
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);
}
OnExpressionCondition
@Order(Ordered.LOWEST_PRECEDENCE - 20)
class OnExpressionCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
String expression = (String) metadata.getAnnotationAttributes(ConditionalOnExpression.class.getName())
.get("value");
expression = wrapIfNecessary(expression);
ConditionMessage.Builder messageBuilder = ConditionMessage.forCondition(ConditionalOnExpression.class,
"(" + expression + ")");
expression = context.getEnvironment().resolvePlaceholders(expression);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beanFactory != null) {
boolean result = evaluateExpression(beanFactory, expression);
return new ConditionOutcome(result, messageBuilder.resultedIn(result));
}
return ConditionOutcome.noMatch(messageBuilder.because("no BeanFactory available."));
}
private Boolean evaluateExpression(ConfigurableListableBeanFactory beanFactory, String expression) {
BeanExpressionResolver resolver = beanFactory.getBeanExpressionResolver();
if (resolver == null) {
resolver = new StandardBeanExpressionResolver();
}
BeanExpressionContext expressionContext = new BeanExpressionContext(beanFactory, null);
Object result = resolver.evaluate(expression, expressionContext);
return (result != null && (boolean) result);
}
private String wrapIfNecessary(String expression) {
if (!expression.startsWith("#{")) {
return "#{" + expression + "}";
}
return expression;
}
}
OnExpressionCondition只继承了SpringBootCondition,所以只有一种用法。实现上首先通过context.getEnvironment().resolvePlaceholders(expression)替换了表达式里的${}变量,然后通过BeanExpressionResolver(默认是StandardBeanExpressionResolver,即SPEL表达式)去解析表达式的值,即true/false
OnPropertyCondition
value和name是一个,prefix+name指定属性名,havingValue指定属性的值,matchIfMissing为true表示属性不存在时条件成立
具体的havingValue的行为
Property Value | havingValue="" | havingValue=“true” | havingValue=“false” | havingValue=“foo” |
---|---|---|---|---|
“true” | yes | yes | no | no |
“false” | no | no | yes | no |
“foo” | yes | no | no | yes |
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
String[] value() default {};
String prefix() default "";
String[] name() default {};
String havingValue() default "";
boolean matchIfMissing() default false;
}
OnPropertyCondition继承了SpringBootCondition,然后实现getMatchOutcome方法