前言
我以自动配置为SpringBoot的第一篇章,是因为从SpringMvc到SpringBoot,它实现了零配置,并出现了很多默认配置,在进行后面的源码理解时可能会有部分地方不理解,念头不通达,所以先将自动配置这部分给了解清楚,知道它的一个配置是怎么加载的,对后面的学习应该会更流畅一点。
SpringBoot的自动配置由注解@EnableAutoConfiguration 开启,所以我们从这个注解入手,看看它是怎么实现的自动配置,和条件过滤的。
原理
在Spring章节spring源码篇(六)配置类解析过程,学习了Spring通过配置类,注入bean的方式,如下。
Spring判断为配置类,有几个注解:@Configuration、@Component、@ComponentScan、@Import、@ImportResource 还有@Bean
所有,Spring常规的注入Bean的方式有(Spring是从beanDefinition去判断的,所有前提要是一个beanDefinition):
-
@Component/@Configuration -
@Component/@Configuration + @Bean -
@Component/@Configuration + @CompnentScan -
@Component/@Configuration + @Import -
@Component/@Configuration + @ImportResource
那么引入其他外部类的方式也大概可以猜到,可以使用@ComponentScan 扫描的方式将外部包扫描进来,而这种方式是应用主动的去扫描,这种主动的方式对于组件整合这种框架就不灵活了,比如你开了一个接口给第三方,然后你在调用接口的时候调用不到,你还要在你的配置上将这个接口添加进来,是不是感觉很违和,就像,我开发一个idea插件,我根据idea的规范做好了,发布了,我还用通知idea官方,让他们将我的插件开通下使用权限,是不是离谱了,没人理你。
所以我们应该关注的应该时被动的去接受组件,然后扫描,就像组装车子一样,有了spring提供框架,主控芯片,发动机,其他就由我们去组装了。
Tomcat中,Tomcat初始化应用程序是通过META-INF/services/javax.servlet.ServletContainerInitializer 读取到应用实现类,而spring也是一样的,它是通过读取META-INFO/spring.factories 这个文件来加载配置的。
比如mybatis的依赖包
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
在引入时,会依赖一个
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>
可以看到,它里面配置了两个配置类,这两个就是mybatis能够自动配置的核心
然后我们在来看SpringBoot的自动配置,Spring是会引入下面的一个自动配置依赖,它里面有很多自动配置类,基本上所有的spring-boot的自动配置都在这里,仔细看的话,你会看到aop、jpa、MongoDB、kafka、servlet等等。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
@EnableAutoConfiguration
解析Import
那么SpringBoot是怎么加载的呢?
在启动SpringBoot应用时,都会加上注解@SpringBootApplication ,这个注解就是关键
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class);
}
}
SpringApplication.run(App.class); 这个是启动web容器,spring容器,然后就是Spring的那一套,
我们查看@SpringBootApplication 的定义,它里面有一个@EnableAutoConfiguration ,这个就是自动配置加载的注解
@EnableAutoConfiguration 注解里有这样一个注解@Import({AutoConfigurationImportSelector.class})
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
Class<?>[] value();
}
通过注释我们可以找到,它的value可以设置四种类型class:
- 标注了
@Configuration 的配置类 - 实现了
ImportSelector 接口的类 - 实现了
ImportBeanDefinitionRegistrar 的类 - 普通的
component 类
当Spring启动时会创建ioc容器,并将我们的配置类(@SpringBootApplication 标注的启动类)作为配置类加入,它相当于一个Configuration ,也是最初的一个配置类,通过这个配置类扫描到其他配置类或bean;在解析这个配置类时,不管有没有imports 都会进行的解析
解析配置类的位置:org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
解析importor的位置:org.springframework.context.annotation.ConfigurationClassParser#processImports
这一步的作用是将imports里的ImportSelector类找出来,还有将Import注解里的beanDefinition注册器也找出来,只是找出来,并没有做什么处理。
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = 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) {
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
finally {
this.importStack.pop();
}
}
}
看一下:ParserStrategyUtils.instantiateClass(...) ,它的实例化不是简单的实例化,还对实例化后的bean做了初始化设置。
org.springframework.context.annotation.ParserStrategyUtils#instantiateClass
static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo, Environment environment,
ResourceLoader resourceLoader, BeanDefinitionRegistry registry) {
Assert.notNull(clazz, "Class must not be null");
Assert.isAssignable(assignableTo, clazz);
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
ClassLoader classLoader = (registry instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader());
T instance = (T) createInstance(clazz, environment, resourceLoader, registry, classLoader);
ParserStrategyUtils.invokeAwareMethods(instance, environment, resourceLoader, registry, classLoader);
return instance;
}
从这一步看出,@Imports 引入的类,Spring给予它对最大程度的支持:BeanClassLoaderAware、BeanFactoryAware、EnvironmentAware、ResourceLoaderAware
private static void invokeAwareMethods(Object parserStrategyBean, Environment environment,
ResourceLoader resourceLoader, BeanDefinitionRegistry registry, @Nullable ClassLoader classLoader) {
if (parserStrategyBean instanceof Aware) {
if (parserStrategyBean instanceof BeanClassLoaderAware && classLoader != null) {
((BeanClassLoaderAware) parserStrategyBean).setBeanClassLoader(classLoader);
}
if (parserStrategyBean instanceof BeanFactoryAware && registry instanceof BeanFactory) {
((BeanFactoryAware) parserStrategyBean).setBeanFactory((BeanFactory) registry);
}
if (parserStrategyBean instanceof EnvironmentAware) {
((EnvironmentAware) parserStrategyBean).setEnvironment(environment);
}
if (parserStrategyBean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) parserStrategyBean).setResourceLoader(resourceLoader);
}
}
}
在上面找到了需要import的类后,执行ImportSelectorHandle
位置:org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
**总结:**上面的步骤是解析import 注解,然后将注解了的类进行解析,包含递归的解析。
import分三种情况:
- 实现了
ImportSelector 接口,那么这一类的接口属于是需要执行接口方法进行导入的,所以在解析时都会添加到deferredImportSelectorHandler - 实现了
ImportBeanDefinitionRegistrar 接口的,这个接口属于是beanDefinition的注册接口,spring中的创建bean都有依据beanDefinition来的,所以应该是以统一的步骤为基础,并且在这个解析配置类的过程中,还没有到创建bean的步骤,还在准备阶段,所以后面是要和其他的beanDefinition一起实例化的,所以这里都是放到了configurationClasses 里,后面解析完配置类后再从取出来进行合并 - 其他情况,普通的配置类,如
Configuration component 等,它同样是作为配置类放到configurationClasses 里
然后下一步(this.deferredImportSelectorHandler.process(); ),对通过@Import 注解拿到的ImportSelector 类进行执行,可以说是深度import,这个是我们对它的一个方法功能描述,可能不太准确。
执行导入类处理器
这里是执行deferredImportSelectorHandler.process 方法,但这里要提一下deferredImportSelectorHandler.handle 方法。
位置:org.springframework.context.annotation.ConfigurationClassParser#processImports
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
它有一个null的判断,但是类初始化时就new了,不存在null的情况,
我要说的是整个null的情况是在selector扫描完了,他会执行process的方法(this.deferredImportSelectorHandler.process(); ),在process方法中,它循环deferredImportSelectors 然后执行,并且deferredImportSelectors 是非线程安全的,再者解析配置类也是一个循环,所以这个process它不能保证执行过程中会出现add的情况,所以,process会先置空,这样,在handle时,就知道当前的handle集合已经在处理了,不能在往里添加了,那么就直接执行吧;
而process里重新new了一个list的意义也是一样的,要保证当前循环不被破坏。
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
}
else {
this.deferredImportSelectors.add(holder);
}
}
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
deferredImports.forEach(handler::register);
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
}
那么在接着看process方法
位置:org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process
process中deferredImports.forEach(handler::register); 添加group,AutoConfigurationImportSelector 是方法直接返回的AutoConfigurationGroup.class
所以这里初始化了两个东西:
- group -》 AutoConfigurationGroup.class 自动配置group,group针对两种场景:
- 当前jar包 -》DefaultDeferredImportSelectorGroup.class 本篇不分析
- 外部jar包 -》 AutoConfigurationGroup.class (本篇分析的重点)
- deferredImports -》 DeferredImportSelector,@Import.value里DeferredImportSelector的子类,它需要
public void register(DeferredImportSelectorHolder deferredImport) {
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
grouping.add(deferredImport);
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
grouping.getImports() 实际执行的方法:
这里group指的是AutoConfigurationImportSelector.AutoConfigurationGroup,是固定的,而deferredImport它是从注解里获取的,并非是固定的,可以把group.process看做一个执行器,deferredImport作为第三方接口实现
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
这里是把从spring.fatories文件读取到的类(List)构建成AutoConfigurationEntry ,可以看做一个配置类对象,后面还有进行解析
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);
}
这里是它获取 List<String> configurations 的逻辑,除了读取文件那一部分没有,从这段已经可以看出它是通过SpringFactoriesLoader.loadFactoryNames 根据EnableAutoConfiguration 全类名加载的value,这个方法的底层是直接读取META-INF/spring.factories
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
所以SpringBoot的自动配置是读取这个键的值
**总结:**这一步是执行DeferredImportSelector 的类,在@Import 中有引入其子类的,这里都会执行,@EnableAutoConfiguration 注解里的是AutoConfigurationImportSelector ,所以会执行getAutoConfigurationEntry 方法,从而读取
META-INF/spring.factories 下的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration 的键值,这时加载到的是List<String> ,然后再读取配置类条件过滤处理器进行过滤,之后构建成entry放到autoConfigurationEntries ,之后再遍历从spring.factories里得到的autoConfigurationEntries 递归走processImports 从解析import开始。
SpringBootCondition
在自动配置中,还有一个比较重要的一类注解Conditional ;
比如:
@ConditionalOnBean
@ConditionalOnMissingBean
@ConditionalOnClass
而在SpringBoot中,SpringBootCondition 是所有条件处理类基础,其他都是通过实现它的getMatchOutcome 接口完成判定就行。
比如@ConditionalOnBean ,它上面还标注了@Conditional(OnBeanCondition.class) ,那么它的处理类就是OnBeanCondition
这里我还要提一个点,因为上面我没有详细说明;
上面@Import 注解会读取META-INF/spring.factories 下的配置类,一共会读取两个key的键值:
EnableAutoConfiguration -> 这个是import的类
AutoConfigurationImportFilter -> 这个是Condition条件处理类的实现类,用来处理import的类
而这时导入的都是String类型的,还没有读取class,因为存在像这样的ConditionalOnClass 注解,所以加载class前会有一次过滤,过滤条件就配置就保存在META-INF/spring-autoconfigure-metadata.properties ;它的key=class类全名.Condition注解名称,表示配置类标注了哪些条件注解,可以翻看spring和mybatis的包,都会找到的。
判定
org.springframework.context.annotation.ConditionEvaluator#shouldSkip(org.springframework.core.type.AnnotatedTypeMetadata, org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase)
该类是解析配置类之前都会去调用的一个验证条件验证类,判断该类是否被加载,都是调用shouldSkip
位置:org.springframework.context.annotation.ConditionEvaluator#shouldSkip(org.springframework.core.type.AnnotatedTypeMetadata, org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase)
ConfigurationPhase有两个值:
- PARSE_CONFIGURATION -》 表示解析配置该时机正处于解析配置阶段,shouldSkip判断失败,不会加载class
- REGISTER_BEAN -》 表示该时机正处于注册bean阶段,shouldSkipt判断失败,不会注册bean
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
if (phase == null) {
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<>();
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, this.context.getClassLoader());
conditions.add(condition);
}
}
AnnotationAwareOrderComparator.sort(conditions);
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;
}
对于下面这个判断来说,所以配置类的条件校验都实现于ConfigurationCondition ,但对于不同的配置,解析加载的时机不同,也会有所差别。
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
以OnBeanCondition 为例,它是需要REGISTER_BEAN 阶段才进行判断,因为,onBeanConditional这个条件针对bean对象,那么他需要准备好class,当扫描到一个配置类,它在missingBean,或onBean时,才能直接取出。
OnBeanCondition 实际的匹配方法:
@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;
}
这里ClassNameFilter.MISSING 是一个方法
MISSING {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return !isPresent(className, classLoader);
}
};
然后返回org.springframework.boot.autoconfigure.condition.FilteringSpringBootCondition#match
@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;
}
主方法是:org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata)
它有很多子类,其实现都有不同,但匹配的逻辑差不多。
总结
@Import 导入支持
- 标注了
@Configuration 的配置类 - 实现了
ImportSelector 接口的类 - 实现了
ImportBeanDefinitionRegistrar 的类 - 普通的
component 类
SpringBoot自动配置中使用@Import(AutoConfigurationImportSelector.class) ,它通过AutoConfigurationImportSelector读取META-INF/spring.factories 下的key为org.springframework.boot.autoconfigure.EnableAutoConfiguration 的键值,加载到配置类列表,然后再读取key为org.springframework.boot.autoconfigure.AutoConfigurationImportFilter 的条件过滤处理器,和META-INF/spring-autoconfigure-metadata.properties 文件的配置类条件配置,进行过滤,得到配置类的entry,然后在遍历得到的entry,再走一遍import的解析过程(递归)。
所以我们要实现我们自定义的spring-boot-starter,就要写一个META-INF/spring.factories ,就像mybatis这样
这一过程是在解析配置类的时候进行,再解析完配置类后,会把解析的配置类注册到ConfigurationClassBeanDefinitionReader 中,待后面进行实例化。
配置类条件处理器,我们也可以自己定义,继承SpringBootCondition 就行,再自定义一个注解,这里偷懒了,没有写demo,以后有时间写一个看看。
|