我以自动配置为SpringBoot的第一篇章,是因为从SpringMvc到SpringBoot,它实现了零配置,并出现了很多默认配置,在进行后面的源码理解时可能会有部分地方不理解,念头不通达,所以先将自动配置这部分给了解清楚,知道它的一个配置是怎么加载的,对后面的学习应该会更流畅一点。
SpringBoot的自动配置由注解@EnableAutoConfiguration
开启,所以我们从这个注解入手,看看它是怎么实现的自动配置,和条件过滤的。
外部jar包,或者第三方包他们加载的方式都是一样的,要实现自动配置,需要遵从spring的规范,一般是配置META-INFO/spring.factories
和META-INF/spring-autoconfigure-metadata.properties
两个文件,从而实现自动配置。
在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.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
dependency>
在引入时,会依赖一个
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-autoconfigureartifactId>
dependency>
可以看到,它里面配置了两个配置类,这两个就是mybatis能够自动配置的核心
然后我们在来看SpringBoot的自动配置,Spring是会引入下面的一个自动配置依赖,它里面有很多自动配置类,基本上所有的spring-boot的自动配置都在这里,仔细看的话,你会看到aop、jpa、MongoDB、kafka、servlet等等。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-autoconfigureartifactId>
dependency>
那么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 {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to 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) {
// 当配置类没有impots时不再进行
if (importCandidates.isEmpty()) {
return;
}
// 判断是否循环引入
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
// 标记正在import的配置类
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// 判断import的类是否是ImportSelector子类
// @SpringBootApplication里的import注解走的就是这里
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) {
// ImportSelector还有另一个子类接口DeferredImportSelector,
// 这里只做了一个动作:添加importHandler
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
}
else {
// 普通的imports
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// 判断import的类是否是ImportBeanDefinitionRegistrar子类
// 这个子类从命名上看就是一个beanDefinition注册器,spring中的所有bean都是先通过beanDefinition注册后,再根据beanDefinition实例化的,所有这里的注册器后面也要添加到已经载入的注册器中
// 可以查看:org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
// 它在parse(配置类)后有这么一句:this.reader.loadBeanDefinitions(configClasses);
// 这句就是将这里找出的注册器都添加的到已经加入的spring容器里的BeanDefinitionReader
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// import的类不是ImportBeanDefinitionRegistrar,ImportSelector,那么就作为配置类进行载入
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()) {
// import 的类不能是接口
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
ClassLoader classLoader = (registry instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) registry).getBeanClassLoader() : resourceLoader.getClassLoader());
// 反射实例化,这里AutoConfigurationImportSelector是无参构造
T instance = (T) createInstance(clazz, environment, resourceLoader, registry, classLoader);
// 这一步就是为这个实例化的AutoConfigurationImportSelector进行遍历初始化,
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
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);
}
}
// importSelectorHandler执行深度import
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的意义也是一样的,要保证当前循环不被破坏。
// deferredImportSelectorHandler.handle
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
// 当要添加时,发现为null,表明这个集合已经在循环处理了,这里可以直接执行
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
}
else {
this.deferredImportSelectors.add(holder);
}
}
// deferredImportSelectorHandler.process
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
// 这里执行必然不会是null,这里new List()是为了避免在循环过程中,handle方法又进行添加对象
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
所以这里初始化了两个东西:
public void register(DeferredImportSelectorHolder deferredImport) {
// 这里其实是有值的,AutoConfigurationImportSelector它重写了这个方法,并返回了AutoConfigurationGroup.class
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
// 把handle添加到grouping
// 实际执行是:this.deferredImports.add(deferredImport);
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();
// 这个getImports实际上是通过上一步添加的group.processs执行的
grouping.getImports().forEach(entry -> {
// 遍历得到的configurationEntry
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作为第三方接口实现
// grouping.getImports()
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 这里通过AutoConfigurationImportSelector.AutoConfigurationGroup#process执行
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
// 这里将导入的类返回
return this.group.selectImports();
}
// group.process
/**
* @param annotationMetadata 配置类的注解信息
* @param deferredImportSelector 配置类上@Import.value里的类
*/
@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()));
// 这里它会去获取自动配置的entry
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);
// 加载配置过滤
// 读取spring.factories中key=org.springframework.boot.autoconfigure.AutoConfigurationImportFilter的3个条件处理类,作为导入配置类的过滤处理器(import filter)
// 然后读取META-INF/spring-autoconfigure-metadata.properties下的键值(配置类的条件,格式className.condition)
// 这里这样处理,是因为在这一步并没有加载class,还知识className,通过spring-autoconfigure-metadata.properties配置,将条件写进到里面,然后进行过滤,下面还会提到,这里只是说明一下
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
这里是它获取 List
的逻辑,除了读取文件那一部分没有,从这段已经可以看出它是通过SpringFactoriesLoader.loadFactoryNames
根据EnableAutoConfiguration
全类名加载的value,这个方法的底层是直接读取META-INF/spring.factories
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// getSpringFactoriesLoaderFactoryClass() = () -> return EnableAutoConfiguration.class;
// 所以这里它是通过 EnableAutoConfiguration 全类名获取对应 META-INF/spring.factories 下的value
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;
}
// 上面一步的:SpringFactoriesLoader.loadFactoryNames方法实现
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
,然后再读取配置类条件过滤处理器进行过滤,之后构建成entry放到autoConfigurationEntries
,之后再遍历从spring.factories里得到的autoConfigurationEntries
递归走processImports
从解析import开始。
在自动配置中,还有一个比较重要的一类注解Conditional
,也是自动配置的一个功能,可以根据环境条件进行bean的注入。
比如:
@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有两个值:
/**
* @param metadata 注解元数据信息
* @param phase 执行方法的阶段,时机
*/
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<>();
// 从条件注解中获取到条件处理类,如@ConditionalOnMissingBean,拿到的就是这个注解上面的@Conditional(OnBeanCondition.class)的OnBeanCondition
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();
}
// 这里就是条件判断,看matches
// 同时这还有一个判断,判断当前配置类上的条件注解需要在什么进行校验的判断
if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
return true;
}
}
return false;
}
对于下面这个判断来说,所以配置类的条件校验都实现于ConfigurationCondition
,但对于不同的配置,解析加载的时机不同,也会有所差别。
// 这里就是条件判断,看matches
// 同时这还有一个判断,判断当前配置类上的条件注解需要在什么进行校验的判断
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) {
// 这里是获取当前配置类,在条件ConditionalOnBean下所需要的处理类
// 这里get的是spring-autoconfigure-metadata.properties读取到的key value
Set<String> onBeanTypes = autoConfigurationMetadata.getSet(autoConfigurationClass, "ConditionalOnBean");
// 查找当前需要的bean类型,返回返回结果信息
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;
}
/**
* @param requiredBeanTypes 需要查找的bean的类全名
* @param annotation 条件注解,这里因为是ConditionalOnBean代码,所以这里是ConditionalOnBean
*/
private ConditionOutcome getOutcome(Set<String> requiredBeanTypes, Class<? extends Annotation> annotation) {
// 这里是通过需要的类型,去Class.forName反射加载,能加载就将当前的类名添加到missing里
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) {
// 里面其实是通过反射`Class.forName`加载,加载失败那就是没有,返回false
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,以后有时间写一个看看。