@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
@AliasFor(
annotation = EnableAutoConfiguration.class
)
Class<?>[] exclude() default {};
@AliasFor(
annotation = EnableAutoConfiguration.class
)
String[] excludeName() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackages"
)
String[] scanBasePackages() default {};
@AliasFor(
annotation = ComponentScan.class,
attribute = "basePackageClasses"
)
Class<?>[] scanBasePackageClasses() default {};
//是否对@Bean方法进行代理,默认true。标注了@SpringBootApplication的bean会被代理增强
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
@EnableAutoConfiguration
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
@AutoConfigurationPackage
注册了AutoConfigurationPackages.BasePackages这个bean,通过它可以获取主类包名。
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
public abstract class AutoConfigurationPackages {
public static List<String> get(BeanFactory beanFactory) {
try {
return ((AutoConfigurationPackages.BasePackages)beanFactory.getBean(BEAN, AutoConfigurationPackages.BasePackages.class)).get();
} catch (NoSuchBeanDefinitionException var2) {
throw new IllegalStateException("Unable to retrieve @EnableAutoConfiguration base packages");
}
}
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
//把包名作为构造参数传进去
constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
} else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(AutoConfigurationPackages.BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
beanDefinition.setRole(2);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
}
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
}
}
如果是DeferredImportSelector,先实例化,然后加入deferredImportSelectors集合,其中就包含了自动装配入口类AutoConfigurationImportSelector。
class ConfigurationClassParser {
private final ConfigurationClassParser.DeferredImportSelectorHandler deferredImportSelectorHandler = new ConfigurationClassParser.DeferredImportSelectorHandler();
//解析@Import
private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {
if (candidate.isAssignable(ImportSelector.class)) {
candidateClass = candidate.loadClass();
ImportSelector selector = (ImportSelector)ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
if (selector instanceof DeferredImportSelector) {
//实例化DeferredImportSelector加入deferredImportSelectors集合
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);
}
}
}
}
private class DeferredImportSelectorHandler {
@Nullable
private List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImportSelectors;
private DeferredImportSelectorHandler() {
this.deferredImportSelectors = new ArrayList();
}
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
ConfigurationClassParser.DeferredImportSelectorHolder holder = new ConfigurationClassParser.DeferredImportSelectorHolder(configClass, importSelector);
//省略部分代码
this.deferredImportSelectors.add(holder);
}
}
}
主干逻辑
class ConfigurationClassParser {
public void parse(Set<BeanDefinitionHolder> configCandidates) {
Iterator var2 = configCandidates.iterator();
while(var2.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next();
BeanDefinition bd = holder.getBeanDefinition();
//解析
this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());
}
//解析完,集中处理deferredImportSelectors
this.deferredImportSelectorHandler.process();
}
}
private class DeferredImportSelectorHandler {
public void process() {
List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
//1.创建分组处理器
ConfigurationClassParser.DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this.new DeferredImportSelectorGroupingHandler();
deferredImports.sort(ConfigurationClassParser.DEFERRED_IMPORT_COMPARATOR);
//2.注册分组
deferredImports.forEach(handler::register);
//3.分组处理器开始处理分组
handler.processGroupImports();
}
} finally {
this.deferredImportSelectors = new ArrayList();
}
}
}
1.创建分组,把DeferredImportSelector添加到分组DeferredImportSelectorGrouping中,每个分组持有Group对象 + DeferredImportSelector成员
2.把分组注册到分组处理器中,其内部是个LinkedHashMap(key=分组类型DeferredImportSelector#getImportGroup的返回值,value=分组)
private class DeferredImportSelectorGroupingHandler {
private final Map<Object, ConfigurationClassParser.DeferredImportSelectorGrouping> groupings;
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses;
private DeferredImportSelectorGroupingHandler() {
this.groupings = new LinkedHashMap();
this.configurationClasses = new HashMap();
}
public void register(ConfigurationClassParser.DeferredImportSelectorHolder deferredImport) {
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
//创建分组
ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)this.groupings.computeIfAbsent(group != null ? group : deferredImport, (key) -> {
return new ConfigurationClassParser.DeferredImportSelectorGrouping(this.createGroup(group));
});
//把DeferredImportSelector添加到分组DeferredImportSelectorGrouping
grouping.add(deferredImport);
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass());
}
}
分组处理器内部结构
1. 遍历分组,利用其保存的Group和DeferredImportSelector成员,依次调用Group#process方法(DeferredImportSelector作为参数),该方法内部调用DeferredImportSelector的相关方法获取导入类
2. 对要导入的类,继续@Import解析
private class DeferredImportSelectorGroupingHandler {
//key=DeferredImportSelector#getImportGroup返回的Group,value=DeferredImportSelectorGrouping(分组,它是分组成员 + Group对象的封装)
private final Map<Object, ConfigurationClassParser.DeferredImportSelectorGrouping> groupings;
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses;
private DeferredImportSelectorGroupingHandler() {
this.groupings = new LinkedHashMap();
this.configurationClasses = new HashMap();
}
public void processGroupImports() {
//遍历分组
Iterator var1 = this.groupings.values().iterator();
while(var1.hasNext()) {
ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)var1.next();
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
grouping.getImports().forEach((entry) -> {
ConfigurationClass configurationClass = (ConfigurationClass)this.configurationClasses.get(entry.getMetadata());
try {
ConfigurationClassParser.this.processImports(configurationClass, ConfigurationClassParser.this.asSourceClass(configurationClass, exclusionFilter), Collections.singleton(ConfigurationClassParser.this.asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false);
} catch (BeanDefinitionStoreException var5) {
throw var5;
} catch (Throwable var6) {
throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", var6);
}
});
}
}
}
private static class DeferredImportSelectorGrouping {
private final Group group;
private final List<ConfigurationClassParser.DeferredImportSelectorHolder> deferredImports = new ArrayList();
DeferredImportSelectorGrouping(Group group) {
this.group = group;
}
public Iterable<Entry> getImports() {
Iterator var1 = this.deferredImports.iterator();
while(var1.hasNext()) {
ConfigurationClassParser.DeferredImportSelectorHolder deferredImport = (ConfigurationClassParser.DeferredImportSelectorHolder)var1.next();
//调用AutoConfigurationGroup#process获取要导入的自动装配类
this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector());
}
return this.group.selectImports();
}
}
private static class AutoConfigurationGroup implements Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(this.getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();
while(var4.hasNext()) {
String importClassName = (String)var4.next();
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
}
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
//SPI加载spring-autoconfigure-metadata.properties文件,获取自动装配过滤规则元数据
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
//注解exclude指定的、spring.autoconfigure.exclude指定的
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
//看下要排除的在不在自动配置集合里,有不在的就报异常,可能要排除的并不是自动配置的类,表示无效排除
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
String name = this.getAnnotationClass().getName();
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
Assert.notNull(attributes, () -> {
return "No auto-configuration attributes found. Is " + metadata.getClassName() + " annotated with " + ClassUtils.getShortName(name) + "?";
});
return attributes;
}
protected Class<?> getAnnotationClass() {
return EnableAutoConfiguration.class;
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//即key=org.springframework.boot.autoconfigure.EnableAutoConfiguration
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] skip = new boolean[candidates.length];
boolean skipped = false;
Iterator var8 = this.getAutoConfigurationImportFilters().iterator();
while(var8.hasNext()) {
AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next();
this.invokeAwareMethods(filter);
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
for(int i = 0; i < match.length; ++i) {
if (!match[i]) {
skip[i] = true;
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
} else {
List<String> result = new ArrayList(candidates.length);
int numberFiltered;
for(numberFiltered = 0; numberFiltered < candidates.length; ++numberFiltered) {
if (!skip[numberFiltered]) {
result.add(candidates[numberFiltered]);
}
}
if (logger.isTraceEnabled()) {
numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return new ArrayList(result);
}
}
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
}
加载所有jar包中的META-INF/spring-autoconfigure-metadata.properties文件。
比如说JdbcTemplateAutoConfiguration,上面的ConditionalOnClass注解表示需要有这两个类才能加载
这个信息配置在META-INF/spring-autoconfigure-metadata.properties里,直接写全限定名方便尝试加载,避免了通过反射进行解析。
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration.ConditionalOnClass=javax.sql.DataSource,org.springframework.jdbc.core.JdbcTemplate
加载所有jar包中的META-INF/spring.factories文件。
SPI加载所有jar包中的META-INF/spring.factories文件。
它们既是AutoConfigurationImportFilter,又是Condition:
OnClassCondition,启动1个线程分担一半的工作,尝试加载过滤元数据指定的类是否存在
@Order(-2147483648)
class OnClassCondition extends FilteringSpringBootCondition {
OnClassCondition() {
}
protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
//多核
if (Runtime.getRuntime().availableProcessors() > 1) {
return this.resolveOutcomesThreaded(autoConfigurationClasses, autoConfigurationMetadata);
} else {
OnClassCondition.OutcomesResolver outcomesResolver = new OnClassCondition.StandardOutcomesResolver(autoConfigurationClasses, 0, autoConfigurationClasses.length, autoConfigurationMetadata, this.getBeanClassLoader());
return outcomesResolver.resolveOutcomes();
}
}
private ConditionOutcome[] resolveOutcomesThreaded(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
int split = autoConfigurationClasses.length / 2;
//开启1个线程分担一半的工作
OnClassCondition.OutcomesResolver firstHalfResolver = this.createOutcomesResolver(autoConfigurationClasses, 0, split, autoConfigurationMetadata);
OnClassCondition.OutcomesResolver secondHalfResolver = new OnClassCondition.StandardOutcomesResolver(autoConfigurationClasses, split, autoConfigurationClasses.length, autoConfigurationMetadata, this.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;
}
private OnClassCondition.OutcomesResolver createOutcomesResolver(String[] autoConfigurationClasses, int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
OnClassCondition.StandardOutcomesResolver outcomesResolver = new OnClassCondition.StandardOutcomesResolver(autoConfigurationClasses, start, end, autoConfigurationMetadata, this.getBeanClassLoader());
try {
return new OnClassCondition.ThreadedOutcomesResolver(outcomesResolver);
} catch (AccessControlException var7) {
return outcomesResolver;
}
}
}
private static final class ThreadedOutcomesResolver implements OnClassCondition.OutcomesResolver {
private final Thread thread;
private volatile ConditionOutcome[] outcomes;
private ThreadedOutcomesResolver(OnClassCondition.OutcomesResolver outcomesResolver) {
this.thread = new Thread(() -> {
this.outcomes = outcomesResolver.resolveOutcomes();
});
this.thread.start();
}
public ConditionOutcome[] resolveOutcomes() {
try {
this.thread.join();
} catch (InterruptedException var2) {
Thread.currentThread().interrupt();
}
return this.outcomes;
}
}
}
private final class StandardOutcomesResolver implements OnClassCondition.OutcomesResolver {
public ConditionOutcome[] resolveOutcomes() {
return this.getOutcomes(this.autoConfigurationClasses, this.start, this.end, this.autoConfigurationMetadata);
}
private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses, int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
for(int i = start; i < end; ++i) {
String autoConfigurationClass = autoConfigurationClasses[i];
if (autoConfigurationClass != null) {
String candidates = autoConfigurationMetadata.get(autoConfigurationClass, "ConditionalOnClass");
if (candidates != null) {
outcomes[i - start] = this.getOutcome(candidates);
}
}
}
return outcomes;
}
private ConditionOutcome getOutcome(String candidates) {
try {
if (!candidates.contains(",")) {
return this.getOutcome(candidates, this.beanClassLoader);
}
String[] var2 = StringUtils.commaDelimitedListToStringArray(candidates);
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
String candidate = var2[var4];
ConditionOutcome outcome = this.getOutcome(candidate, this.beanClassLoader);
if (outcome != null) {
return outcome;
}
}
} catch (Exception var7) {
}
return null;
}
}
OnBeanCondition,尝试加载过滤元数据指定的类是否存在
此处只是过滤自动装配类,bean是否存在还需要在扫描注册bean时判断,详见spring源码—bean扫描注册。
@Order(2147483647)
class OnBeanCondition extends FilteringSpringBootCondition implements ConfigurationCondition {
//获取自动装配类的过滤阶段调用
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] = this.getOutcome(onBeanTypes, ConditionalOnBean.class);
if (outcomes[i] == null) {
//类存在,继续判断
Set<String> onSingleCandidateTypes = autoConfigurationMetadata.getSet(autoConfigurationClass, "ConditionalOnSingleCandidate");
outcomes[i] = this.getOutcome(onSingleCandidateTypes, ConditionalOnSingleCandidate.class);
}
}
}
return outcomes;
}
private ConditionOutcome getOutcome(Set<String> requiredBeanTypes, Class<? extends Annotation> annotation) {
//根据类名尝试加载类
List<String> missing = this.filter(requiredBeanTypes, ClassNameFilter.MISSING, this.getBeanClassLoader());
if (!missing.isEmpty()) {
//类不存在,封装1个消息
ConditionMessage message = ConditionMessage.forCondition(annotation, new Object[0]).didNotFind("required type", "required types").items(Style.QUOTE, missing);
return ConditionOutcome.noMatch(message);
} else {
return null;
}
}
//这个在conditionEvaluator.shouldSkip调用时进入
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage matchMessage = ConditionMessage.empty();
MergedAnnotations annotations = metadata.getAnnotations();
OnBeanCondition.Spec spec;
OnBeanCondition.MatchResult matchResult;
String reason;
if (annotations.isPresent(ConditionalOnBean.class)) {
spec = new OnBeanCondition.Spec(context, metadata, annotations, ConditionalOnBean.class);
matchResult = this.getMatchingBeans(context, spec);
if (!matchResult.isAllMatched()) {
reason = this.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())) {
OnBeanCondition.Spec<ConditionalOnSingleCandidate> spec = new OnBeanCondition.SingleCandidateSpec(context, metadata, annotations);
matchResult = this.getMatchingBeans(context, spec);
if (!matchResult.isAllMatched()) {
return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll());
}
if (!this.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 = new OnBeanCondition.Spec(context, metadata, annotations, ConditionalOnMissingBean.class);
matchResult = this.getMatchingBeans(context, spec);
if (matchResult.isAnyMatched()) {
reason = this.createOnMissingBeanNoMatchReason(matchResult);
return ConditionOutcome.noMatch(spec.message().because(reason));
}
matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
}
return ConditionOutcome.match(matchMessage);
}
}