多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Spring Boot 自动装配源码解析:从 @SpringBootApplication 到 AutoConfigurationImportSelector 的完整链路

Spring Boot 自动装配源码解析:从 @SpringBootApplication 到 AutoConfigurationImportSelector 的完整链路 Spring Boot 自动装配源码解析从 SpringBootApplication 到 AutoConfigurationImportSelector 的完整链路【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/GitHub_Trending/so/source-code-hunterSpring Boot 最核心的魔法就是自动装配Auto Configuration只要在类路径上引入某个 starter 依赖对应的 Bean 就会自动就绪开发者无需手动编写大量Configuration。本文基于 source-code-hunter 仓库的源码阅读笔记以org.springframework.boot.autoconfigure包为核心从SpringBootApplication注解出发逐层拆解EnableAutoConfiguration、AutoConfigurationImportSelector以及spring.factories、自动配置元数据、条件过滤、排除机制等关键环节并结合 Redis 自动配置实例帮助你完整掌握 Spring Boot 自动装配的底层实现原理具备阅读与调试 Spring Boot 启动流程源码的能力。一、一切从 SpringBootApplication 开始绝大多数 Spring Boot 应用的启动类上都标注了SpringBootApplication它其实是一个组合注解源码定义如下org.springframework.boot.autoconfigure.SpringBootApplicationTarget(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Inherited 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 {}; AliasFor(annotation Configuration.class) boolean proxyBeanMethods() default true; }从定义可以看出它由三大部分叠加而成SpringBootConfiguration本质上是Configuration表明启动类本身是一个配置类EnableAutoConfiguration自动装配的总开关是本文要剖析的核心ComponentScan(excludeFilters ...)默认开启组件扫描同时通过两个自定义过滤器排除特殊组件——TypeExcludeFilter排除通过类型排除的组件与AutoConfigurationExcludeFilter排除自动配置类防止自动配置类被当作普通组件重复扫描注册。注解内声明的多个属性都通过AliasFor与内部注解的属性互相映射属性映射目标作用excludeEnableAutoConfiguration.exclude按类排除自动配置excludeNameEnableAutoConfiguration.excludeName按类名排除自动配置scanBasePackagesComponentScan.basePackages指定扫描包scanBasePackageClassesComponentScan.basePackageClasses按类指定扫描包proxyBeanMethodsConfiguration.proxyBeanMethods是否代理Bean方法默认为true二、EnableAutoConfiguration自动装配的开关EnableAutoConfiguration本身的代码非常简洁关键在于它通过Import引入了一个核心类Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Inherited AutoConfigurationPackage Import(AutoConfigurationImportSelector.class) public interface EnableAutoConfiguration { }这里的Import是 Spring 框架层面的机制它支持导入Configuration类、ImportSelector、ImportBeanDefinitionRegistrar或普通组件可参考仓库笔记 Spring-Import。AutoConfigurationImportSelector正是DeferredImportSelector的典型实现负责在解析配置类时动态决定要导入哪些自动配置类。AutoConfigurationPackage则用于记录自动配置包信息供后续扫描使用。三、AutoConfigurationImportSelector自动装配的总调度器AutoConfigurationImportSelector是自动装配的核心执行者先看它的类图从类图可以清晰地看到它同时实现了多组接口职责划分明确ImportSelector/DeferredImportSelector负责导入类选择。DeferredImportSelector是延迟导入的变体其处理时机晚于普通ImportSelector保证自动配置能在用户自定义配置处理之后介入EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware、ResourceLoaderAware在回调阶段注入环境、Bean 工厂、类加载器与资源加载器Ordered参与排序控制处理顺序。在 Spring 处理DeferredImportSelector时会调用其内部DeferredImportSelector.Group#process将自动装配流程串起来。下面的process是整条链路的入口之一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( // 加载配置元数据 getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); for (String importClassName : autoConfigurationEntry.getConfigurations()) { this.entries.putIfAbsent(importClassName, annotationMetadata); } }可以看到process首先断言传入的deferredImportSelector必须是AutoConfigurationImportSelector随后调用getAutoConfigurationEntry获取自动配置结果并把结果分别记录到autoConfigurationEntries与entries两个集合中。而getAutoConfigurationEntry又依赖两个前置动作加载自动配置元数据与读取候选配置清单。下面逐一展开。四、加载自动配置元数据AutoConfigurationMetadataLoadergetAutoConfigurationMetadata()负责加载自动配置的元数据信息private AutoConfigurationMetadata getAutoConfigurationMetadata() { if (this.autoConfigurationMetadata null) { // 加载配置信息 this.autoConfigurationMetadata AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); } return this.autoConfigurationMetadata; }元数据加载的底层实现位于org.springframework.boot.autoconfigure.AutoConfigurationMetadataLoader#loadMetadata(java.lang.ClassLoader)static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) { try { // 获取资源路径 EnumerationURL urls (classLoader ! null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path); Properties properties new Properties(); while (urls.hasMoreElements()) { properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement()))); } return loadMetadata(properties); } catch (IOException ex) { throw new IllegalArgumentException(Unable to load ConditionalOnClass location [ path ], ex); } }关键点如下它通过类加载器扫描固定路径下的资源文件这个路径就是常量protected static final String PATH META-INF/spring-autoconfigure-metadata.properties把扫描到的所有properties合并后封装成AutoConfigurationMetadata注意这个文件位于 target 编译后的目录中由注解处理器在编译期生成不是源码目录下的手写文件。Spring Boot 官方曾在 Issue #11282 中讨论过这一行为阅读源码时如果找不到该文件请先确认项目已编译。调试视角下可以看到类加载器如Launcher$AppClassLoader等正沿着类路径逐一查找该资源文件该元数据文件记录的是每个自动配置类所声明的条件注解与顺序信息如ConditionalOnClass依赖的类、AutoConfigureAfter/AutoConfigureBefore排序关系等这些信息在后面的filter过滤阶段会被用来做免加载类级别的预判避免在未满足条件时仍加载整个自动配置类。下图是调试时看到的元数据结果共 486 条五、读取候选配置清单spring.factories 与 SpringFactoriesLoader自动装配的候选名单来自spring.factories文件。在 Spring Boot 源码仓库中其路径为spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories该文件内以org.springframework.boot.autoconfigure.EnableAutoConfiguration为键列出了全部自动配置类# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\同样的机制也适用于第三方 starter任何自定义 starter 只要在自己的META-INF/spring.factories中按同样格式声明EnableAutoConfiguration键其自动配置类就会被 Spring Boot 识别并纳入候选。读取这些配置的方法就是getCandidateConfigurationsprotected ListString getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // 读取 org.springframework.boot.autoconfigure.EnableAutoConfiguration 相关配置 ListString 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会遍历类路径上所有 jar 的META-INF/spring.factories收集所有以EnableAutoConfiguration为键的配置类名。这与 Spring Boot 启动流程中加载SpringApplicationRunListener、SpringBootExceptionReporter等组件使用的是同一套 SPI 机制可参见仓库笔记 Spring-Boot-Run 中getSpringFactoriesInstances的读取逻辑。六、以 Redis 为例解剖一个真实的自动配置类在元数据与候选清单中都能找到 Redis 的身影例如下面这些自动配置类都被纳入了管理仔细看org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration类的声明Configuration(proxyBeanMethods false) ConditionalOnClass(RedisOperations.class) EnableConfigurationProperties(RedisProperties.class) Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })这四行注解是理解自动配置类如何工作的最佳范例Configuration(proxyBeanMethods false)声明这是一个配置类且关闭Bean方法代理以提升启动性能ConditionalOnClass(RedisOperations.class)仅当类路径上存在RedisOperations即引入 redis 相关依赖时才生效。这是Conditional条件家族的典型应用条件家族还包括ConditionalOnBean、ConditionalOnMissingBean、ConditionalOnProperty、ConditionalOnWebApplication等十余种详见仓库笔记 SpringBoot-ConditionalOnBeanEnableConfigurationProperties(RedisProperties.class)把RedisProperties注册为配置属性 BeanImport(...)按需引入 Lettuce / Jedis 两种客户端的连接配置。6.1 EnableConfigurationProperties 与配置绑定EnableConfigurationProperties的含义是自动把一个 POJO 映射到 Spring Boot 配置文件默认是 application.properties/application.yml的属性集上。它的底层实现由EnableConfigurationPropertiesRegistrar一个ImportBeanDefinitionRegistrar完成会注册ConfigurationPropertiesBindingPostProcessor等基础设施 Bean并在 Bean 初始化前把配置文件中匹配前缀的属性绑定到 POJO 上详见仓库笔记 SpringBoot-ConfigurationProperties。Redis 对应的配置属性类是org.springframework.boot.autoconfigure.data.redis.RedisProperties部分属性如下ConfigurationProperties(prefix spring.redis) public class RedisProperties { /** * Database index used by the connection factory. */ private int database 0; /** * Connection URL. Overrides host, port, and password. User is ignored. Example: * redis://user:passwordexample.com:6379 */ private String url; /** * Redis server host. */ private String host localhost; /** * Login password of the redis server. */ private String password; /** * Redis server port. */ private int port 6379; /** * Whether to enable SSL support. */ private boolean ssl; /** * Connection timeout. */ private Duration timeout; /** * Client name to be set on connections with CLIENT SETNAME. */ private String clientName; }通过ConfigurationProperties(prefix spring.redis)spring.redis.host、spring.redis.port等配置项就会被自动绑定到上述字段。同样的套路可以推广到其他模块例如org.springframework.boot.autoconfigure.jdbc.JdbcProperties对应spring.datasource前缀读者可以自行展开阅读。七、主流程核心getAutoConfigurationEntry回到自动装配的总入口getAutoConfigurationEntry串联了从读取候选到过滤排除再到触发事件的完整流程protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // 获取注解属性值 AnnotationAttributes attributes getAttributes(annotationMetadata); // 获取候选配置信息 ListString configurations getCandidateConfigurations(annotationMetadata, attributes); // 删除重复配置 configurations removeDuplicates(configurations); // 获取 exclude 属性 SetString exclusions getExclusions(annotationMetadata, attributes); // 校验 exclude 类 checkExcludedClasses(configurations, exclusions); // 配置中删除 exclude 的属性值 configurations.removeAll(exclusions); // 过滤 configurations filter(configurations, autoConfigurationMetadata); // 触发自动配置事件 fireAutoConfigurationImportEvents(configurations, exclusions); // 返回 return new AutoConfigurationEntry(configurations, exclusions); }整个流程共七个步骤下面逐一分析。7.1 getAttributes读取注解属性protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) { // name org.springframework.boot.autoconfigure.EnableAutoConfiguration , 这是一个固定的值 String name getAnnotationClass().getName(); // 获取注解的属性 AnnotationAttributes attributes AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true)); Assert.notNull(attributes, () - No auto-configuration attributes found. Is metadata.getClassName() annotated with ClassUtils.getShortName(name) ?); return attributes; }这里读取的注解名固定为org.springframework.boot.autoconfigure.EnableAutoConfiguration返回的AnnotationAttributes中携带exclude、excludeName等属性值也就是启动类上SpringBootApplication(exclude ...)传入的值。7.2 getCandidateConfigurations 与 removeDuplicates候选配置来自spring.factories见第五节。由于多个 jar 可能重复声明同一配置类还需要去重protected final T ListT removeDuplicates(ListT list) { return new ArrayList(new LinkedHashSet(list)); }removeDuplicates借助LinkedHashSet去重同时保持原有顺序。7.3 getExclusions收集所有需要排除的类排除来源有三处注解上的exclude属性、excludeName属性以及配置文件中的spring.autoconfigure.excludeprotected SetString getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) { SetString excluded new LinkedHashSet(); // 获取属性 exclude 值转换成list excluded.addAll(asList(attributes, exclude)); // 获取属性 excludeName 值转换成list excluded.addAll(Arrays.asList(attributes.getStringArray(excludeName))); // 获取 SpringBoot 本身的忽略配置属性 excluded.addAll(getExcludeAutoConfigurationsProperty()); return excluded; }getExcludeAutoConfigurationsProperty负责从 Environment 中读取spring.autoconfigure.exclude配置private ListString getExcludeAutoConfigurationsProperty() { if (getEnvironment() instanceof ConfigurableEnvironment) { Binder binder Binder.get(getEnvironment()); // 取出 spring.autoconfigure.exclude 转换成list return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList) .orElse(Collections.emptyList()); } String[] excludes getEnvironment().getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class); return (excludes ! null) ? Arrays.asList(excludes) : Collections.emptyList(); }也就是说除了在启动类上写SpringBootApplication(exclude ...)你还可以通过配置文件排除自动配置spring.autoconfigure.excludeorg.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration调试视角下getExclusions的执行过程如下此时excluded集合为空表示未排除任何类原文档中作者还实际验证了通过excludeName排除自研测试自动配置类的效果SpringBootApplication(excludeName { org.sourcehot.service.HelloServiceAutoConfiguration })7.4 checkExcludedClasses校验排除项private void checkExcludedClasses(ListString configurations, SetString exclusions) { ListString invalidExcludes new ArrayList(exclusions.size()); for (String exclusion : exclusions) { // if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) !configurations.contains(exclusion)) { invalidExcludes.add(exclusion); } } if (!invalidExcludes.isEmpty()) { // 处理忽略的类 handleInvalidExcludes(invalidExcludes); } }逻辑要点如果某个exclusion指定的类确实存在于类路径中却不在候选配置列表里则判定为非法排除项交由handleInvalidExcludes处理默认抛出IllegalStateException提示。随后configurations.removeAll(exclusions)把合法的排除项从候选中移除。7.5 filter基于条件注解的过滤private ListString filter(ListString configurations, AutoConfigurationMetadata autoConfigurationMetadata) { long startTime System.nanoTime(); String[] candidates StringUtils.toStringArray(configurations); boolean[] skip new boolean[candidates.length]; boolean skipped false; // 获取 AutoConfigurationImportFilter 相关配置 for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) { // 执行 aware 相关接口 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; } ListString result new ArrayList(candidates.length); for (int i 0; i candidates.length; i) { if (!skip[i]) { result.add(candidates[i]); } } if (logger.isTraceEnabled()) { int numberFiltered configurations.size() - result.size(); logger.trace(Filtered numberFiltered auto configuration class in TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) ms); } return new ArrayList(result); }filter的做法是通过getAutoConfigurationImportFilters()从spring.factories中加载所有AutoConfigurationImportFilter实现逐个对候选配置类执行match只要有一个过滤器判定不匹配就把该候选标记为skip最后保留所有未被跳过的类。开启 trace 日志时可以看到过滤耗时统计。AutoConfigurationImportFilter的三个内置实现同样注册在spring.factories中org.springframework.boot.autoconfigure.AutoConfigurationImportFilter\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition即OnBeanConditionBean 条件、OnClassCondition类路径条件、OnWebApplicationConditionWeb 应用条件三个过滤器。它们都继承自SpringBootCondition其matches方法是典型的模板方法先由子类实现getMatchOutcome得出匹配结论再统一记录日志与评估报告详见仓库笔记 SpringBoot-ConditionalOnBeanOverride public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 类名或者方法名标记 String classOrMethodName getClassOrMethodName(metadata); try { // 比较类,子类实现 ConditionOutcome outcome getMatchOutcome(context, metadata); // 日志输出 logOutcome(classOrMethodName, outcome); // 报告记录 recordEvaluation(context, classOrMethodName, outcome); // 返回匹配结果 return outcome.isMatch(); } catch (NoClassDefFoundError ex) { throw new IllegalStateException(Could not evaluate condition on classOrMethodName due to ex.getMessage() not found. Make sure your own configuration does not rely on that class. This can also happen if you are ComponentScanning a springframework package (e.g. if you put a ComponentScan in the default package by mistake), ex); } catch (RuntimeException ex) { throw new IllegalStateException(Error processing condition on getName(metadata), ex); } }ConditionOutcome封装了是否匹配以及匹配信息public class ConditionOutcome { /** * 是否匹配 */ private final boolean match; /** * 条件信息 */ private final ConditionMessage message; }有了这套条件机制RedisAutoConfiguration的ConditionalOnClass(RedisOperations.class)才能在不引入 Redis 依赖时被OnClassCondition过滤器提前剔除而无需真正加载并执行该配置类。OnBeanCondition的match则由getOutcomes负责比对自动配置类与元数据最终落到beanFactory.getBeanNamesForType等 Spring 容器查询方法上。7.6 fireAutoConfigurationImportEvents触发自动配置事件private void fireAutoConfigurationImportEvents(ListString configurations, SetString exclusions) { // 获取自动配置的监听器列表 ListAutoConfigurationImportListener listeners getAutoConfigurationImportListeners(); if (!listeners.isEmpty()) { // 创建 自动配置事件 AutoConfigurationImportEvent event new AutoConfigurationImportEvent(this, configurations, exclusions); for (AutoConfigurationImportListener listener : listeners) { // 执行 Aware 相关接口 invokeAwareMethods(listener); // 监听器执行自动配置事件 listener.onAutoConfigurationImportEvent(event); } } }该方法从spring.factories中加载所有AutoConfigurationImportListener实现构造AutoConfigurationImportEvent事件对象携带最终候选配置与排除列表并逐个回调监听器。AutoConfigurationImportListener目前唯一的实现类是ConditionEvaluationReportAutoConfigurationImportListener它的作用是记录本次自动配置的候选与排除情况供条件评估报告Condition Evaluation Report使用Override public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) { if (this.beanFactory ! null) { ConditionEvaluationReport report ConditionEvaluationReport.get(this.beanFactory); // 记录需要加载的配置 report.recordEvaluationCandidates(event.getCandidateConfigurations()); // 记录不需要加载的配置 report.recordExclusions(event.getExclusions()); } }这也是我们调试某个自动配置为什么没生效的重要抓手开启debugtrue后启动日志中的Positive matches / Negative matches / Exclusions三个区块正是来源于此报告。八、process 与 selectImports收尾与排序8.1 process收集每一条自动配置结果AutoConfigurationGroup#process在前面已出现过这里给出完整实现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( // 加载配置元数据 getAutoConfigurationMetadata(), annotationMetadata); this.autoConfigurationEntries.add(autoConfigurationEntry); // 循环需要自动注入的类 for (String importClassName : autoConfigurationEntry.getConfigurations()) { // 继续放入k,v this.entries.putIfAbsent(importClassName, annotationMetadata); } }注意这里出现的AnnotationMetadata annotationMetadata存储了两类信息mergedAnnotations类相关的注解信息与annotationTypes启动类上的注解类型列表它是后续构造Entry自动配置类与来源注解的映射的依据。8.2 selectImports汇总、剔除并排序Override public IterableEntry selectImports() { if (this.autoConfigurationEntries.isEmpty()) { return Collections.emptyList(); } // 获取忽略的类 SetString allExclusions this.autoConfigurationEntries.stream() .map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet()); // 获取需要注入的类 SetString processedConfigurations this.autoConfigurationEntries.stream() .map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream) .collect(Collectors.toCollection(LinkedHashSet::new)); // 把不需要自动注入的类从需要注入的类中移除 processedConfigurations.removeAll(allExclusions); // 排序 return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream() .map((importClassName) - new Entry(this.entries.get(importClassName), importClassName)) .collect(Collectors.toList()); }selectImports是DeferredImportSelector.Group接口定义的最终回调它做三件事汇总所有AutoConfigurationEntry的排除列表与候选列表从候选中再次剔除排除项调用sortAutoConfigurations按照AutoConfigureOrder、AutoConfigureAfter/AutoConfigureBefore等元数据信息排序最终把每个配置类包装成Entry记录其来源注解元数据返回给 Spring 容器。后续的 Bean 注册、实例化与依赖注入就交由 Spring 容器接管不再属于自动装配选择器的职责范围。九、总结一条完整的自动装配链路把全文串起来Spring Boot 自动装配的完整执行链路如下启动类标注SpringBootApplication其组合注解中包含EnableAutoConfigurationEnableAutoConfiguration通过Import引入AutoConfigurationImportSelector一个DeferredImportSelectorSpring 容器处理延迟导入时回调AutoConfigurationGroup#process内部先调用getAutoConfigurationMetadata()通过AutoConfigurationMetadataLoader加载编译期生成的META-INF/spring-autoconfigure-metadata.properties元数据getAutoConfigurationEntry依次执行读取注解属性getAttributes→ 从spring.factories加载候选配置getCandidateConfigurations基于SpringFactoriesLoaderSPI 机制→ 去重removeDuplicates→ 收集排除项getExclusions含exclude、excludeName、spring.autoconfigure.exclude三路来源→ 校验并剔除排除项checkExcludedClassesremoveAll→ 条件过滤filter由OnBeanCondition、OnClassCondition、OnWebApplicationCondition三个AutoConfigurationImportFilter依据元数据预判→ 触发导入事件fireAutoConfigurationImportEvents由ConditionEvaluationReportAutoConfigurationImportListener记录候选与排除最后selectImports汇总去重、二次剔除并排序将自动配置类列表返回给 Spring 容器完成注册。对开发者而言掌握这条链路意味着三件实事可以信手拈来排查自动配置不生效借助debugtrue启动日志中的 Positive/Negative matches 与 Exclusions 区块定位是条件不满足还是被显式排除精确控制自动配置通过SpringBootApplication(exclude ...)、excludeName或spring.autoconfigure.exclude排除不需要的自动配置类编写自己的 starter在META-INF/spring.factories中以EnableAutoConfiguration为键声明自动配置类并在配置类上组合使用ConditionalOnClass、ConditionalOnMissingBean、EnableConfigurationProperties等注解即可复刻 Spring Boot 官方的自动装配能力。相关扩展阅读仓库内SpringBoot-ConditionalOnBean条件注解与过滤器实现、SpringBoot-ConfigurationProperties配置绑定底层实现、Spring-Boot-Run启动流程总览、Spring-ImportImport机制基础。【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/GitHub_Trending/so/source-code-hunter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表