Spring Boot 自动配置实现原理(源码分析)

news/2024/12/22 11:20:11/

前言:

我们都在为 Spring Boot 强大的自动配置功能带来的效率提升而感叹,那你是否了解自动配置原理?本篇我们就来分析一下 Spring Boot 的自动配置原理

Spring Boot 系列文章传送门

Spring Boot 启动流程源码分析(2)

Spring Boot 启动流程源码分析(2)

什么是 Spring Boot 自动配置

自动配置 Auto-Configuration,是指基于你引入的依赖 Jar 包,对 Spring Boot 应用进行自动配置自动配置的目标是减少开发者在开始一个新项目或者给现有项目添加新特性时的工作量,避免需要使用大量的配置,Spring Boot 通过在应用的启动阶段应用一些"约定优于配置"的原则来实现这一点,自动配置为 SpringBoot 框架的【开箱即用】提供了重要的基础支撑。

自动配置和自动装配的区别?

  • 自动配置: Auto-Configuration,针对的是 SpringBoot 中的配置类。
  • 自动装配: Autowire,针对是 Spring 中的依赖注入。

@SpringBootApplication 注解

我们都知道 Spring Boot 项目只需要在启动类上加上 @SpringBootApplication 注解,即可完成自动配置,那你了解 @SpringBootApplication 注解吗?

@SpringBootApplication 注解是一个组合注解,如下:

  • @SpringBootConfiguration:继承自 Configuration,支持 Java Config 方式进行配置。
  • @EnableAutoConfiguration:最最核心的注解,主要用于开启自动配置(本篇研究的重点)。
  • @ComponentScan:自动扫描组件,默认扫描该类所在包及其子包下所有带有指定注解的类,将它们自动装配到bean容器中,会被自动装配的注解包括@Controller、@Service、@Component、@Repository等。也可以指定扫描路径。

@SpringBootApplication 注解详解传送门:

@SpringBootApplication 注解到底做了什么?你真的知道吗?

@EnableAutoConfiguration 注解源码解析

@EnableAutoConfiguration 注解也是一个组合注解,引用了 @AutoConfigurationPackage 注解,以及导入了一个 AutoConfigurationImportSelector 类,我们重点分析 AutoConfigurationImportSelector类。

java">package org.springframework.boot.autoconfigure;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
}

AutoConfigurationImportSelector#selectImports 方法源码分析

AutoConfigurationImportSelector 类实现了 DeferredImportSelector 接口,DeferredImportSelector 又实现了 ImportSelector 接口,根据@Import 注解的原理,实际会调用 AutoConfigurationImportSelector#selectImports 方法实现类的加载。

java">//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports
public String[] selectImports(AnnotationMetadata annotationMetadata) {//判断是否注解元数据 自动配置是否打开if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {//从配置文件加载 AutoConfigurationMetadataAutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);//获取所有配置类 重点关注  this.getAutoConfigurationEntry 这行代码AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}
}

AutoConfigurationImportSelector#getAutoConfigurationEntry 方法源码分析

AutoConfigurationImportSelector#getAutoConfigurationEntry 方法主要是加载 META-INF/spring.factories 文件中声明的配置类,并将一些配置类过滤掉。

java">//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {//判断是否注解元数据 自动配置是否打开if (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {//获取 EnableAutoConfiguration`注解中的属性AnnotationAttributes attributes = this.getAttributes(annotationMetadata);//加载 META-INF/spring.factories 文件中声明的配置类 重点关注List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);//移出重复的配置类configurations = this.removeDuplicates(configurations);//获取注解的排除项Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);this.checkExcludedClasses(configurations, exclusions);//移出需要排除的configurations.removeAll(exclusions);//过滤掉不需要的配置类 spring-autoconfigure-metadata.propertiesconfigurations = this.filter(configurations, autoConfigurationMetadata);//发布自动配置导入事件this.fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);}
}

AutoConfigurationImportSelector#getCandidateConfigurations 方法源码分析

AutoConfigurationImportSelector#getCandidateConfigurations 方法主要是调用了 SpringFactoriesLoader#loadFactoryNames 方法,读取 META-INF/spring.factories 文件中配置的类名返回。

java">//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {//调用 Spring 提供的方法读取 META-INF/spring.factories 文件中配置的类名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;
}//org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {//接口名称String factoryTypeName = factoryType.getName();//获取接口的实现类return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}//org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {//从缓存中获取MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);if (result != null) {//缓存不为空 字节返回return result;} else {try {//从 META-INF/spring.factories 加载资源文件 META-INF/spring.factories 熟悉的名词出现了Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");//创建空 mapLinkedMultiValueMap result = new LinkedMultiValueMap();//遍历加载到的资源文件while(urls.hasMoreElements()) {//获取 urlURL url = (URL)urls.nextElement();//根据 url 创建 ResourceUrlResource resource = new UrlResource(url);//加载资源文件 资源文件是 properties 格式的 key 是接口名 value 是实现类名称 多个实现类 用英文逗号隔开Properties properties = PropertiesLoaderUtils.loadProperties(resource);//迭代遍历Iterator var6 = properties.entrySet().iterator();while(var6.hasNext()) {Entry<?, ?> entry = (Entry)var6.next();//获取 key String factoryTypeName = ((String)entry.getKey()).trim();//获取 value 数组String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());int var10 = var9.length;//遍历加入到结果集for(int var11 = 0; var11 < var10; ++var11) {String factoryImplementationName = var9[var11];result.add(factoryTypeName, factoryImplementationName.trim());}}}//加入缓存cache.put(classLoader, result);//返回return result;} catch (IOException var13) {throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);}}
}

接下来我们总结一下 AutoConfigurationImportSelector#selectImports 实现自动配置的过程:

  1. 判断自动配置是否开启,默认是开启的,可以自己配置关闭,但没必要。
  2. 获取 @EnableAutoConfiguration 注解的属性。
  3. 加载项目下所有 META-INF/spring.factories 文件中声明的配置类。
  4. 对加载的配置类进行各种去重,包括去掉指定排除的配置类,过滤掉一些不需要的配置类,最终返回需要加载的配置类。

AutoConfigurationImportSelector#selectImports 方法没有走为什么?

上面我们在分析自动配置实现原理的时分析到,@EnableAutoConfiguration 注解通过 @Import 注解导入了一个 ImportSelector 接口的实现类 AutoConfigurationImportSelector,按照 @Import 注解的功能来说,按道理来说项目启动时候会执行 AutoConfigurationImportSelector#selectImports 方法,但是我在启动调试的时候,发现并没有走 AutoConfigurationImportSelector#selectImports 方法,但是确实又走到了 AutoConfigurationImportSelector#getAutoConfigurationEntry 方法,那这是为什么呢?其根本原因在于 AutoConfigurationImportSelector 实现的这个接口 DeferredImportSelector 有点特殊,上面也说了 AutoConfigurationImportSelector 实现了 DeferredImportSelector 接口。

DeferredImportSelector 类源码分析

上面我们说项目启动的时候没有调用 AutoConfigurationImportSelector#selectImports,原因是 AutoConfigurationImportSelector 实现的接口 DeferredImportSelector 有些特殊,我们简单的看下 DeferredImportSelector 的源码,如下:

java">package org.springframework.context.annotation;import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;public interface DeferredImportSelector extends ImportSelector {//注意这个方法 getImportGroup@Nullabledefault Class<? extends DeferredImportSelector.Group> getImportGroup() {return null;}//内部 Group 接口public interface Group {//process 方法void process(AnnotationMetadata var1, DeferredImportSelector var2);//selectImports  方法  名称似乎有点熟悉Iterable<DeferredImportSelector.Group.Entry> selectImports();public static class Entry {private final AnnotationMetadata metadata;private final String importClassName;public Entry(AnnotationMetadata metadata, String importClassName) {this.metadata = metadata;this.importClassName = importClassName;}public AnnotationMetadata getMetadata() {return this.metadata;}public String getImportClassName() {return this.importClassName;}public boolean equals(@Nullable Object other) {if (this == other) {return true;} else if (other != null && this.getClass() == other.getClass()) {DeferredImportSelector.Group.Entry entry = (DeferredImportSelector.Group.Entry)other;return this.metadata.equals(entry.metadata) && this.importClassName.equals(entry.importClassName);} else {return false;}}public int hashCode() {return this.metadata.hashCode() * 31 + this.importClassName.hashCode();}public String toString() {return this.importClassName;}}}
}

从 DeferredImportSelector 的源码中我们发现了它有一个内部接口 Group,而 Group 又有一个方法叫做 selectImports,这个方法名有点熟悉,AutoConfigurationImportSelector 类中有一个同样的方法,因此我们是否可以大胆猜测项目启动的时候走到了 DeferredImportSelector.Group#selectImports 方法【虽然后面证实了这个猜测不够准确,但是我们还是可以去大胆推测的】。

进一步推测

DeferredImportSelector 源码中有一个 getImportGroup 方法,我们去 AutoConfigurationImportSelector 源码中找到了方法的实现,如下:

java">//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getImportGroup
public Class<? extends Group> getImportGroup() {return AutoConfigurationImportSelector.AutoConfigurationGroup.class;
}

根据源码发现 getImportGroup 方法返回的是 AutoConfigurationImportSelector 的内部类 AutoConfigurationGroup 的 Class,我们继续跟踪 AutoConfigurationGroup 类。

AutoConfigurationImportSelector.AutoConfigurationGroup 类源码分析

这里我们只简单贴出两个重点方法,process 方法和 selectImports 方法,我们在 process 方法中看到了 AutoConfigurationImportSelector#getAutoConfigurationEntry 方法,这个方法在项目启动时候是调用了的,因此我们可以进一步猜测,项目启动的时候是调用了 AutoConfigurationImportSelector.AutoConfigurationGroup#process 方法。

java">//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#process
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());});//getAutoConfigurationEntry 熟悉的名称出现了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);}}//org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports
public Iterable<Entry> selectImports() {if (this.autoConfigurationEntries.isEmpty()) {return Collections.emptyList();} else {Set<String> allExclusions = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());Set<String> processedConfigurations = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));processedConfigurations.removeAll(allExclusions);return (Iterable)this.sortAutoConfigurations(processedConfigurations, this.getAutoConfigurationMetadata()).stream().map((importClassName) -> {return new Entry((AnnotationMetadata)this.entries.get(importClassName), importClassName);}).collect(Collectors.toList());}
}

猜想验证

简单的启动一个 Spring Boot 项目,进行 debugger 调试。

java">@SpringBootApplication
public class MySpringBootApplication {public static void main(String[] args) {SpringApplication.run(MySpringBootApplication .class,args);}
}

AutoConfigurationImportSelector.AutoConfigurationGroup#process 方法

在这里插入图片描述

AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports 方法

在这里插入图片描述
debugger 结果验证了我们的猜想,项目启动最终走到了AutoConfigurationImportSelector 内部类 AutoConfigurationGroup 的 process 方法和 selectImports 方法,完成了自动配置

原理分析

上面我们通过 debugger 的方式验证了我们的猜想,项目启动最终走到了AutoConfigurationImportSelector 内部类 AutoConfigurationGroup 的 process 方法和 selectImports 方法,完成了自动配置,但是 Spring Boot 是从哪里对 DeferredImportSelector 接口做特殊处理,又为啥要这么设计呢?下面我们从源码层面去剖析。

我们通过 debugger 调试的方式,找到方法的调用栈,把断点打在 AutoConfigurationImportSelector#getAutoConfigurationEntry 方法上,得到如下调用栈截图。

在这里插入图片描述

方法的调用栈上可以看到非常多我们属性的方法,例如:SpringApplication#refresh 、AbstractApplicationContext#refresh、AbstractApplicationContext#invokeBeanFactoryPostProcess、ConfigurationClassParser#parse(重点关注,分析的入口)、AutoConfigurationImportSelector$AutoConfigurationGroup#process 等方法。

简单梳理一下调用栈:

  1. 启动 main 方法,掉用 SpringApplication#run 方法。
  2. SpringApplication#run 方法最终会调用 AbstractApplicationContext#refresh 进行 IOC 容器的初始化。
  3. AbstractApplicationContext#refresh 方法 会调用 AbstractApplicationContext#invokeBeanFactoryPostProcess 方法,这里会调用所有的BeanFactory 后处理器。
  4. BeanFactory 中有一个叫做 ConfigurationClassPostProcessor 的处理器,负责处理@Configuration、@Import 等注解,是我们本篇关注的重点。
  5. 接着就会调用 ConfigurationClassParser#parse 方法,本篇重点分析的方法。
  6. 再接下来就会调用到 AutoConfigurationImportSelector$AutoConfigurationGroup#process 方法,进一步调用 AutoConfigurationImportSelector#getAutoConfigurationEntry 方法,完成自动配置

ConfigurationClassParser#parse 源码分析

ConfigurationClassParser#parse 方法遍历所有的 BeanDefinitionHolder,根据 BeanDefinitionHolder 的类型不同,进行不同的解析操作,这里我们重点关注注解类型的解析,解析完成之后会执行 DeferredImportSelectorHandler#process 方法,这个方法我们最后再看。

java">//org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)
public void parse(Set<BeanDefinitionHolder> configCandidates) {//遍历所有配置项的 BeanDefinitionHolderIterator var2 = configCandidates.iterator();while(var2.hasNext()) {//从 BeanDefinitionHolder 中获取  BeanDefinitionBeanDefinitionHolder holder = (BeanDefinitionHolder)var2.next();BeanDefinition bd = holder.getBeanDefinition();try {//根据不同类型的 BeanDefinition 执行不同的解析逻辑if (bd instanceof AnnotatedBeanDefinition) {//注解的 beandefinition @Import注解就在这里解析this.parse(((AnnotatedBeanDefinition)bd).getMetadata(), holder.getBeanName());} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition)bd).hasBeanClass()) {//抽象的 beandefination 有 class 的this.parse(((AbstractBeanDefinition)bd).getBeanClass(), holder.getBeanName());} else {//其他this.parse(bd.getBeanClassName(), holder.getBeanName());}} catch (BeanDefinitionStoreException var6) {throw var6;} catch (Throwable var7) {throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", var7);}}//执行找到的DeferredImportSelector 的 process 方法this.deferredImportSelectorHandler.process();
}

ConfigurationClassParser#parse 方法源码解析

ConfigurationClassParser#parse 方法并没有什么实际操作,调用了 ConfigurationClassParser#processConfigurationClass,该方法会判断是否解析过,并且会多次解析情况进行处理,最终调用 ConfigurationClassParser#doProcessConfigurationClass 方法完成解析。

java">//org.springframework.context.annotation.ConfigurationClassParser#parse(org.springframework.core.type.AnnotationMetadata, java.lang.String)
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {this.processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}//org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {//判断是否解析过if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {//通过配置类名称获取ConfigurationClassConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass);//判断是否为nullif (existingClass != null) {//不为null  那就是再次 importif (configClass.isImported()) {if (existingClass.isImported()) {//已经存在了 就合并existingClass.mergeImportedBy(configClass);}return;}this.configurationClasses.remove(configClass);this.knownSuperclasses.values().removeIf(configClass::equals);}//处理配置类ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass, filter);do {//解析操作sourceClass = this.doProcessConfigurationClass(configClass, sourceClass, filter);} while(sourceClass != null);//存储解析的配置类  回到 parse  方法可以获取到this.configurationClasses.put(configClass, configClass);}
}

ConfigurationClassParser#doProcessConfigurationClass 方法源码解析

ConfigurationClassParser#doProcessConfigurationClass 方法中有对各种注解的解析,除了我们重点关注的 @Import 注解之外,还有 @ImportResource、@Bean 注解,该方法中也考虑到递归解析的情况。

java">//org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
@Nullable
protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass(ConfigurationClass configClass, ConfigurationClassParser.SourceClass sourceClass, Predicate<String> filter) throws IOException {//@Component 注解if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// 递归处理内部类 因为内部类也是一个配置类 配置类上有@configuration注解 继承@Component 调用 processMemberClasses 方法 递归解析配置类中的内部类this.processMemberClasses(configClass, sourceClass, filter);}//配置类上加了 @PropertySource 注解 解析加载 properties 文件 并将属性添加到 spring 上下文中Iterator var4 = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, PropertySource.class).iterator();AnnotationAttributes importResource;while(var4.hasNext()) {importResource = (AnnotationAttributes)var4.next();if (this.environment instanceof ConfigurableEnvironment) {this.processPropertySource(importResource);} else {this.logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment");}}//处理 @ComponentScan @ComponentScans 注解 并将扫描包下的所有 bean 转换成填充后的 ConfigurationClassSet<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {//componentScans 不为空  且没有解析过 迭代遍历解析Iterator var14 = componentScans.iterator();while(var14.hasNext()) {AnnotationAttributes componentScan = (AnnotationAttributes)var14.next();//解析 @ComponentScan @ComponentScans 配置的包下的类  得到 BeanDefinitionHolderSet<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());//迭代遍历Iterator var8 = scannedBeanDefinitions.iterator();while(var8.hasNext()) {BeanDefinitionHolder holder = (BeanDefinitionHolder)var8.next();BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}//判断是否是一个配置类if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {//递归解析this.parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}//处理 @Import 注解 重点关注this.processImports(configClass, sourceClass, this.getImports(sourceClass), filter, true);//处理 @ImportResource 注解 导入 Spring  配置文件importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource != null) {String[] resources = importResource.getStringArray("locations");Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");String[] var20 = resources;int var22 = resources.length;for(int var23 = 0; var23 < var22; ++var23) {String resource = var20[var23];String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}}//处理加了 @Bean  注解的方法Set<MethodMetadata> beanMethods = this.retrieveBeanMethodMetadata(sourceClass);Iterator var18 = beanMethods.iterator();while(var18.hasNext()) {MethodMetadata methodMetadata = (MethodMetadata)var18.next();//转换为 BeanMethod 对象configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}//处理接口的默认实现this.processInterfaces(configClass, sourceClass);//解析父类 如果被解析的配置类继承了某个类 那么配置类的父类也会被进行解析if (sourceClass.getMetadata().hasSuperClass()) {String superclass = sourceClass.getMetadata().getSuperClassName();if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);return sourceClass.getSuperClass();}}return null;
}

ConfigurationClassParser#processImports 方法源码解析

ConfigurationClassParser#processImports 方法在处理时分成了 3 个类型,分别是实现了 ImportSelector 接口的,实现 ImportBeanDefinitionRegistrar 接口的,其他类型的,我们重点关注的是实现 ImportSelector 接口的。

java">//org.springframework.context.annotation.ConfigurationClassParser#processImports
private void processImports(ConfigurationClass configClass, ConfigurationClassParser.SourceClass currentSourceClass, Collection<ConfigurationClassParser.SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) {//为空判断if (!importCandidates.isEmpty()) {if (checkForCircularImports && this.isChainedImportOnStack(configClass)) {//是否循环导入  是否堆栈上的链式导入  是的话  errorthis.problemReporter.error(new ConfigurationClassParser.CircularImportProblem(configClass, this.importStack));} else {this.importStack.push(configClass);try {//迭代遍历需要导入的类Iterator var6 = importCandidates.iterator();while(var6.hasNext()) {//获取要导入的类ConfigurationClassParser.SourceClass candidate = (ConfigurationClassParser.SourceClass)var6.next();Class candidateClass;//判断要导入的类 是否是 ImportSelector 的子类if (candidate.isAssignable(ImportSelector.class)) {//要导入的类是一个导入选择器  委托来确定是否进行导入candidateClass = candidate.loadClass();//反射生成一个 ImportSelect 对象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);}//判断引用选择器是否是 DeferredImportSelector 接口的实例if (selector instanceof DeferredImportSelector) {//是 将选择器添加到 deferredImportSelectorHandler 实例中 等到所有的配置类加载完成后统一处理自动化配置类this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector)selector);} else {//否  获取引入的类 然后使用递归方式将这些类中同样添加了 @Import 注解引用的类String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());Collection<ConfigurationClassParser.SourceClass> importSourceClasses = this.asSourceClasses(importClassNames, exclusionFilter);//递归处理 被 Import 进来的类可能也有 @Import 注解this.processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);}} else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {实现ImportBeanDefinitionRegistrar接口的candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar = (ImportBeanDefinitionRegistrar)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());this.processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);}}} catch (BeanDefinitionStoreException var17) {throw var17;} catch (Throwable var18) {throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", var18);} finally {this.importStack.pop();}}}
}

DeferredImportSelectorHandler#handle 方法源码解析

DeferredImportSelectorHandler#handle 方法逻辑很简单,只是注册和存储,但不会真正执行。

java">//org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#handle
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {//DeferredImportSelectorHolder 获取 holderConfigurationClassParser.DeferredImportSelectorHolder holder = new ConfigurationClassParser.DeferredImportSelectorHolder(configClass, importSelector);//为空判断if (this.deferredImportSelectors == null) {//创建一个 DeferredImportSelectorGroupingHandlerConfigurationClassParser.DeferredImportSelectorGroupingHandler handler = ConfigurationClassParser.this.new DeferredImportSelectorGroupingHandler();//注册 DeferredImportSelectorHolderhandler.register(holder);//重点方法  会调用到  AutoConfigurationGroup#processhandler.processGroupImports();} else {//加入到 deferredImportSelectorsthis.deferredImportSelectors.add(holder);}}

DeferredImportSelectorGroupingHandler#register 方法源码分析

DeferredImportSelectorGroupingHandler#register 方法主要完成 DeferredImportSelectorHolder 的注册。

java">//org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#register
public void register(ConfigurationClassParser.DeferredImportSelectorHolder deferredImport) {//获取 getImportGroup 方法返回值 getImportGroup 方法上面提到过的Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();//如果为空就说明没有重写 使用原来的 deferredImport 对象ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)this.groupings.computeIfAbsent(group != null ? group : deferredImport, (key) -> {return new ConfigurationClassParser.DeferredImportSelectorGrouping(this.createGroup(group));});//加入到 grouping 中grouping.add(deferredImport);//加入到 configurationClassesthis.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass());
}

DeferredImportSelectorGroupingHandler#processGroupImports 方法源码解析

DeferredImportSelectorGroupingHandler#processGroupImports 方法即将完成最终的调用,重点在 grouping.getImports() 这行代码,我们接着分析。

java">//org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports
public void processGroupImports() {Iterator var1 = this.groupings.values().iterator();while(var1.hasNext()) {ConfigurationClassParser.DeferredImportSelectorGrouping grouping = (ConfigurationClassParser.DeferredImportSelectorGrouping)var1.next();Predicate<String> exclusionFilter = grouping.getCandidateFilter();// getImports 方法内部会调用默认的或者我们覆盖过的 process 方法和 selectImports 方法grouping.getImports().forEach((entry) -> {ConfigurationClass configurationClass = (ConfigurationClass)this.configurationClasses.get(entry.getMetadata());try {//配置类中可能会包含 @Import 注解引入的类 通过此方法将引入的类注入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);}});}}

DeferredImportSelectorGrouping#getImports 方法源码分析

DeferredImportSelectorGrouping#getImports 方法中会调用到文章开头我们提到的 AutoConfigurationImportSelector.AutoConfigurationGroup#process 和 AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports 方法。

java">//org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGrouping#getImports
public Iterable<Entry> getImports() {//迭代遍历  deferredImports 其实就是 DeferredImportSelectorHolderIterator var1 = this.deferredImports.iterator();while(var1.hasNext()) {ConfigurationClassParser.DeferredImportSelectorHolder deferredImport = (ConfigurationClassParser.DeferredImportSelectorHolder)var1.next();//AutoConfigurationImportSelector.AutoConfigurationGroup#process  方法this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector());}//执行 AutoConfigurationImportSelector.AutoConfigurationGroup#selectImports  方法return this.group.selectImports();
}

至此,我们就知道为什么默认的 AutoConfigurationImportSelector#selectImports 方法没有执行,而是执行了 AutoConfigurationImportSelector.AutoConfigurationGroup#process 。

总结:如果没有通过大胆猜测、debugger 调试、源码跟踪,我们大概率会任务 Spring Boot 完成自动配置会走 AutoConfigurationImportSelector#selectImports 方法,这个结论明显是错误的,希望本篇的分享可以帮助到有需要的小伙伴们。
如有不正确的地方请各位指出纠正。


http://www.ppmy.cn/news/1472594.html

相关文章

mybatis varchar[] string[] 对应

MyBatis 处理数据库中的数组类型与 Java 中的集合类型对应关系并不直接。通常&#xff0c;数据库中的数组会被映射为 Java 中的 List 类型。在 MyBatis 的映射文件中&#xff0c;可以使用 resultMap 来定义这种映射关系。 以 PostgreSQL 为例&#xff0c;假设有一个表 example…

内容个性化的智能引擎:Kompas.ai如何满足用户需求

在数字化时代&#xff0c;用户对内容的消费趋向个性化和定制化。个性化内容不仅能提升用户体验&#xff0c;还能增强品牌与用户之间的互动。Kompas.ai作为一款先进的智能引擎&#xff0c;正通过其独特的技术满足用户的个性化需求。 个性化内容的重要性 个性化内容在提升用户体验…

小程序备案小程序认证双系统

​打造安全合规的线上平台 &#x1f50d; 一、引言&#xff1a;为何需要小程序备案与认证&#xff1f; 在数字化快速发展的今天&#xff0c;小程序已成为企业、个人展示自身、提供服务的重要窗口。然而&#xff0c;随着小程序数量的快速增长&#xff0c;安全、合规等问题也逐渐…

迁移学习——CycleGAN

CycleGAN 1.导入需要的包2.数据加载&#xff08;1&#xff09;to_img 函数&#xff08;2&#xff09;数据加载&#xff08;3&#xff09;图像转换 3.随机读取图像进行预处理&#xff08;1&#xff09;函数参数&#xff08;2&#xff09;数据路径&#xff08;3&#xff09;读取文…

uniapp小程序提示用户打开系统定位并授权

1.提示用户打开手机系统定位 let system uni.getSystemInfoSync(); // 获取系统信息 // 手机系统定位 if (!system.locationEnabled) {uni.showModal({title: 提示,content: 手机系统定位未打开&#xff01;,showCancel: false,success: function(res) {if (res.confirm) {co…

手把手教你玩转AD9361数字调制解调系列(三) ----纯PL逻辑实现FM信号的数字调制解调

因最近客户需求&#xff0c;用纯PL实现AD9361的数字信号调制解调&#xff0c;于是就把各种数字调制都在AD9361上都实现了一遍。 优点就是&#xff1a;既可以在zynq系列上配置9361&#xff0c;也可以在纯FPGA系列配置9361。并且理解起来比较简单&#xff01;&#xff01;&#…

xml----命名空间详解

一、XML 命名空间&#xff08;namespace&#xff09;------ xmlns 属性 我们使用xmlns 属性来指定元素的命名空间&#xff0c;格式如下&#xff1a; xmlns:namespace-prefix“namespaceURI” 即 xmlns:前缀“命名空间” xmlns 是 xml namespace的意思&#xff0c;是xml文件规范…

SpringBooot常用的内置工具类

环境&#xff1a;SpringBoot 3.2.5 1、获取进程ID 如果你想在程序中获取当前SpringBoot运行的进程号&#xff0c;那么你可以使用ApplicationPid&#xff0c;该类非常方便的获取当前进程ID。 ApplicationPid pid new ApplicationPid() ; System.out.printf("进程ID: %s%…