Spring.factories深度解析:自动装配原理、Starter实战与迁移指南

发布时间:2026/10/11 6:11:00
Spring.factories深度解析:自动装配原理、Starter实战与迁移指南 去年帮同事排查一个线上启动异常现象很怪服务能正常起来但一些业务Bean全是null控制台也没有任何报错。我把项目里里外外翻了一遍最后在构建产物里看到META-INF/spring.factories里的自动配置类被一次依赖合并弄丢了这才意识到这个平时不起眼的文件才是Spring Boot自动装配的心脏。很多人天天用SpringBootApplication却从来没打开过Spring.factories直到自己写Starter或者做框架级集成才知道它其实是让第三方jar包里的配置类能被自动加载的关键。这篇文章我打算只聚焦这个文件本身把它的加载原理、常见用法、手动写Starter的完整步骤以及在新老版本之间怎么迁移一次讲透。适合想真正理解Spring Boot自动配置的人也适合在Spring Cloud、Spring AI这类项目里做扩展集成的开发者参考。1. Spring.factories到底是什么它解决的问题是什么1.1 一个配置文件的“身份信息”spring.factories本质上就是一个标准的Properties文件放在jar包里的META-INF/spring.factories路径下。文件内容很简单每一行都是一个键值对key是某个扩展节点的接口全限定名value是这个接口下的多个实现类多个类之间用英文逗号分隔。遇到一行放不下的时候还可以用反斜杠\换行写法上和其他Properties文件完全一致。举个例子一个典型的自动配置声明长这样org.springframework.boot.autoconfigure.EnableAutoConfiguration\ com.example.gray.GrayAutoConfiguration,\ com.example.log.LogAutoConfigurationSpring框架在启动阶段会通过SpringFactoriesLoader去扫描所有依赖jar包里的这个文件把对应的类名解析出来再实例化成对象塞回容器。你可以把它理解成一份“扩展点清单”框架只负责做发布订阅具体谁实现了某个能力全部在spring.factories里登记。这个设计很像Java自带的META-INF/servicesSPI机制但比JDK原生的更灵活因为同一个文件里可以声明多种不同类型的扩展点而且不要求实现类必须有公开无参构造器实例化动作统一交给Spring管理。1.2 没有自动装配的年代扩展点为什么难管理我在刚接触Spring的时候还是XML盛行的年代。当时要在项目里引入一个第三方组件通常要手动在配置文件里注册一堆bean比如数据源、事务管理器、视图解析器。后来注解驱动普及了ComponentScan可以扫描当前包以及子包里的Component但问题也随之而来我们写业务代码能控制包结构依赖jar包里的类却不受扫描规则约束。任何一个第三方框架想往Spring容器里塞东西都得强迫用户在自己的配置类里Import或Bean手动注册这对使用方来说是很大的心智负担。Spring.factories解决的就是这个“自动发现”问题。框架层在启动时统一读取所有jar包里这个文件谁声明了自动配置谁就会被加载使用方不需要知道内部实现细节只要引入Starter依赖即可。更关键的是这种机制不仅服务Spring BootApplicationContextInitializer、ApplicationListener、EnvironmentPostProcessor这些底层扩展点统统可以通过它注册这也是为什么你在很多框架jar包里都能看到它的原因。2. SpringFactoriesLoader加载机制拆解2.1 核心加载流程与源码路径SpringFactoriesLoader这个类在Spring框架的spring-core模块里对应包路径是org.springframework.core.io.support。它不是Spring Boot独有的早在Spring Framework 3.2就已经存在。核心方法有两个public static ListString loadFactoryNames(Class? factoryType, Nullable ClassLoader classLoader) public static T ListT loadFactories(ClassT factoryType, Nullable ClassLoader classLoader)第一个方法只返回类名列表第二个方法会直接实例化每一个实现类并返回对象列表。Spring Boot启动时AutoConfigurationImportSelector调用的就是loadFactoryNames(EnableAutoConfiguration.class, classLoader)。整个加载流程可以拆成四步确定用来扫描资源的ClassLoader。优先使用调用方传入的ClassLoader如果为空就依次尝试线程上下文类加载器和默认类加载器。通过classLoader.getResources(FACTORIES_RESOURCE_LOCATION)找到所有jar包下的META-INF/spring.factories资源路径这是一个EnumerationURL。逐个加载这些URL用Properties.load(InputStream)解析成键值对然后按key合并到一个MultiValueMap中。根据目标接口类型取出对应的实现类全限定名列表再去实例化或者直接返回。实际源码里还会处理URL重复、ClassLoader缓存、去重等问题但总体逻辑就是我上面说的这一套。理解了这个流程很多“为什么我的配置不生效”的问题其实都能直接看出答案。2.2 类加载、合并顺序与缓存细节有几个细节在实际排查时非常关键。第一多个jar包里可能都存在META-INF/spring.factories且声明了同一个key。Spring不会因为发现了一个就停止扫描而是会把所有jar包中相同key的value全部合并到一个列表里。这就意味着如果你的业务代码里也维护了一份spring.factories和某个框架包里的重名两边都会生效。这个特性会带来好处也会带来重复注册的麻烦后面实战部分我会专门讲。第二解析顺序依赖getResources返回的Enumeration而这个顺序通常由类加载器的实现决定不建议业务方对实现类的加载顺序做任何假设。如果你真的需要控制自动配置类之间的优先级正确做法是用AutoConfigureBefore、AutoConfigureAfter注解而不是调整文件里value的先后顺序。第三SpringFactoriesLoader内部对解析结果有缓存。不同版本的Spring实现略有差异但思路一致以ClassLoader为key缓存一份MultiValueMapString, String避免每次启动都重复扫描所有jar包。这同时也意味着如果在运行时动态往ClassLoader里塞新的jar包已经缓存过的结果不会重新刷新。正常Spring Boot应用很少遇到这种情况但做插件化开发、热部署工具时要格外留意。2.3 与手写Spring、三级缓存的关系很多读者被“手写Spring”专题里的三级缓存折腾得不轻很容易把Spring.factories和三级缓存弄混。简单说两者解决的问题完全不在一个阶段。三级缓存解决的是循环依赖。当Spring在创建Bean A的过程中发现A依赖了B而B又依赖A为了不直接报错容器会通过“提前暴露ObjectFactory”的方式把还没完全初始化完成的A的半成品对象放入三级缓存让B先拿到引用等B创建完成后A再继续初始化。这属于单例Bean生命周期管理时的手段。Spring.factories则是服务加载机制解决的是“框架去哪里发现扩展实现类”。它发生在Bean定义加载阶段启动早期就要把所有候选配置类先读出来。手写IoC容器时你会用包扫描来寻找Component但这种方法没办法扫描到jar包里的扩展类所以必须靠SPI机制补上这个口子。如果你正在手写Spring考虑完“扫描哪些包”之后下一步大概率也会想到自己实现一个类似SpringFactoriesLoader的加载器。3. Spring Boot自动配置里的Spring.factories3.1 EnableAutoConfiguration的读取路径Spring Boot的核心注解SpringBootApplication组合了EnableAutoConfiguration而EnableAutoConfiguration的导入逻辑是通过AutoConfigurationImportSelector实现的。这个Selector在Spring Boot 2.6及更早版本里核心动作就是调用SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, classLoader)读取所有META-INF/spring.factories中org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的类名列表。拿到候选的自动配置类之后AutoConfigurationImportSelector会做四件后续工作去重排除掉重复声明。根据注解上的exclude、excludeName过滤。按AutoConfigureBefore、AutoConfigureAfter排序。交给容器依次处理期间各个配置类上的ConditionalOnClass、ConditionalOnProperty等条件注解会真正决定这个类是否奏效。这里有一个新手常犯的误解自动配置类被读取到不代表它一定生效。它只是候选后面还有条件注解的层层筛选。所以当你发现某个自动配置没起作用时排查顺序应该是先确认文件里有没有声明再确认条件评估结果而不是一上来就怀疑框架没读到文件。3.2 你可能会用到的Spring.factories扩展点除了EnableAutoConfigurationspring.factories还能注册其他很多扩展点。我把常见key整理成一张表排查依赖时可以直接对着看。Spring.factories中的key用途说明org.springframework.boot.autoconfigure.EnableAutoConfiguration注册自动配置类以前是Starter的标配org.springframework.context.ApplicationContextInitializer在Spring容器刷新之前做定制操作比如设置一些默认环境属性org.springframework.context.ApplicationListener注册某个应用事件监听器不限具体的监听事件org.springframework.boot.env.EnvironmentPostProcessor在Environment准备阶段修改配置属性常用于加载自定义配置中心org.springframework.boot.SpringApplicationRunListener监听SpringApplication启动过程中的多个阶段比如starting、started、failedorg.springframework.boot.diagnostics.FailureAnalyzer将启动异常转换成更直观的提示信息org.springframework.boot.web.servlet.ServletContextInitializer嵌入式容器环境下注册ServletContext初始化逻辑org.springframework.boot.context.config.ConfigDataLocationResolver自定义配置文件位置的解析器需要说明的是Spring Boot 2.7之前上面这些key都可以写在META-INF/spring.factories里。2.7版本开始官方把EnableAutoConfiguration这个key迁移到了新的文件META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports其他扩展点仍然保留在spring.factories。3.0之后EnableAutoConfiguration老key彻底失效再在新版本里用老文件声明自动配置类只会被静默忽略。3.3 与Spring Cloud、Spring AI的关联Spring Cloud和Spring AI生态里也大量依赖这类自动配置机制。比如Spring Cloud Context的引导初始化、BootstrapConfiguration等早期加载逻辑就经常通过spring.factories注册对应的ApplicationListener和ApplicationContextInitializer。Spring AI Alibaba以及各类基于Spring Boot的AI框架同样会提供自己的AutoConfiguration.imports或spring.factories文件让开发者引入依赖后就能直接使用ChatClient、ChatModel等Bean。所以不管你是用Spring Cloud做微服务整合还是用Spring AI做模型调用只要想扩展框架能力或者想理解框架为什么能“加依赖就自动好用”最终都要回到这个机制上。搞懂Spring.factories与其新替代文件等于掌握了Spring Boot各种自动集成的底层钥匙。4. 手写一个Starter把Spring.factories用起来4.1 场景设计做一个灰度路由Starter理论讲再多不如自己动手写一个Starter。我这边选个贴近实际业务的场景灰度发布。假设我们希望给调用方提供一个GrayRouterBean它能判断当前用户ID是否命中灰度名单。使用这个Starter的人只需要引入依赖再配置几行灰度名单就能在自己的代码里注入GrayRouter完全不用关心内部实现。这个例子虽然简单但能完整覆盖一个自动配置类需要的四个部分配置属性类、自动配置类、条件装配、注册文件。我把它写成一个Maven模块结构如下gray-spring-boot-starter ├── pom.xml └── src └── main └── resources └── META-INF └── spring.factoriesStarter本身通常只做依赖引入和自动配置所以代码名下项目里我会放src/main/java来写核心逻辑同时通过spring.factories声明自动配置入口。4.2 定义配置属性和自动配置类先定义GrayProperties它绑定配置前缀gray同时维护一个灰度用户列表package com.example.gray; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.ArrayList; import java.util.List; ConfigurationProperties(prefix gray) public class GrayProperties { private boolean enabled true; private ListString users new ArrayList(); public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled enabled; } public ListString getUsers() { return users; } public void setUsers(ListString users) { this.users users; } }再定义一个灰度路由接口和一个简单的实现类public interface GrayRouter { boolean hit(String userId); } public class UserListGrayRouter implements GrayRouter { private final ListString users; public UserListGrayRouter(ListString users) { this.users users; } Override public boolean hit(String userId) { return users ! null users.contains(userId); } }然后写自动配置类这里我使用AutoConfiguration注解。Spring Boot 2.7之后这个注解专门用于自动配置类配合新的AutoConfiguration.imports文件使用如果坚持用老式spring.factories声明也可以写Configuration但既然新版本已推荐AutoConfiguration建议直接用它。package com.example.gray; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; AutoConfiguration EnableConfigurationProperties(GrayProperties.class) ConditionalOnProperty(name gray.enabled, havingValue true, matchIfMissing true) public class GrayAutoConfiguration { Bean ConditionalOnMissingBean(GrayRouter.class) public GrayRouter grayRouter(GrayProperties properties) { return new UserListGrayRouter(properties.getUsers()); } }这里我加了一个ConditionalOnMissingBean(GrayRouter.class)意思是如果使用方已经自己定义了一个GrayRouter我的自动配置就不再重复创建Bean避免冲突。4.3 编辑Spring.factories和AutoConfiguration.imports如果你要兼容Spring Boot 2.6及更早版本就得在META-INF/spring.factories里写org.springframework.boot.autoconfigure.EnableAutoConfiguration\ com.example.gray.GrayAutoConfiguration如果项目已经切换到Spring Boot 2.7官方更推荐在新文件里写路径是META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports内容直接写自动配置类的完整类名一行一个com.example.gray.GrayAutoConfiguration这里有一个非常关键的建议同一个自动配置类不要同时出现在spring.factories和AutoConfiguration.imports里。Spring Boot 2.7为了兼容新旧两套机制是同时支持的如果同一份类重复注册轻则日志告警重则导致Bean重复定义。我建议在新项目里全部使用AutoConfiguration.imports老项目升级时再逐步迁移。4.4 条件装配与配置属性绑定的细节自动配置能不能生效条件注解是关键。上面例子里的ConditionalOnProperty、ConditionalOnMissingBean是最常用的两个但这只是冰山一角。还有一些我实际用过并觉得重要的条件ConditionalOnClass(name com.example.SomeClass)当依赖中出现了某个类才生效通常放在类上用来做功能开关。ConditionalOnWebApplication(type Type.SERVLET)只在Web应用环境生效可以用来区分MVC和响应式应用。ConditionalOnExpression基于SpEL表达式做更复杂的判断。配置属性绑定方面新版配置属性类推荐使用构造器绑定比ConfigurationProperties setter更安全。比如可以改成ConfigurationProperties(prefix gray) public final class GraySettings { private final boolean enabled; private final ListString users; public GraySettings(boolean enabled, ListString users) { this.enabled enabled; this.users users; } // getters... }启用构造器绑定时需要在EnableConfigurationProperties或ConfigurationPropertiesScan时指定ConfigurationPropertiesScan。不过这对刚入门的人来说不是必须的我通常建议先跑通setter绑定版本理解了原理再进一步优化。5. 实操中容易踩的坑与排查实录5.1 自动配置没生效的常见原因我总结了一个速查表基本涵盖了这几年遇到过的绝大多数问题。现象可能原因解决办法自动配置类没有生效没有报错文件名拼写错误比如写成spring.factories少个s检查资源路径确认是META-INF/spring.factories自动配置类没有生效也没有逻辑执行spring.factories中的类名写错、包名写错检查类全限定名是否与实际一致用IDE搜索确认自动配置类没有生效启动时debug也不显示Spring Boot 3.x里仍使用老机制声明自动配置迁移到META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports自动配置类有加载但Bean没创建条件注解不满足比如缺少某个类、属性值不匹配查看条件评估报告定位具体是哪一条Condition为false出现多个相同类型的Bean自己的配置类和自动配置类都创建了同一个Bean在自动配置Bean上添加ConditionalOnMissingBeanClassNotFoundException: xxxAutoConfigurationStarter的依赖范围不对或使用方没有传递依赖检查Maven的scope确保运行期包含相关依赖ApplicationListener没有执行key写错或错误放到了AutoConfiguration.imports里确认只有自动配置类放新文件监听器仍然放spring.factories这个表里的每一条我都踩过或者见同事踩过也是个很实用的排查路径。5.2 用条件评估报告和Actuator来定位Spring Boot启动时加一个--debug参数控制台会输出一份完整的自动配置报告里面分成“Positive matches”“Negative matches”“Exclusions”几个部分。Negative matches里会把你这个自动配置类为什么没有被加载说得很清楚。比如缺少某个Class或者某个属性不是预期值这些都能在报告最后的“Condition evaluation”段落里看到。还有一个方法引入spring-boot-starter-actuator然后访问/actuator/conditions端点在线查看同样条件的评估结果。这在排查已经部署到服务器上的应用时非常有用不需要重新启动就能看到当前所有自动配置类的匹配情况。实际定位问题时我习惯先用一个临时日志来判断流程在自动配置类的静态代码块或构造器里加一行System.out.println或log.info打包后重新启动。如果日志都没打出来问题基本集中在资源文件加载或类名拼写上如果日志打出来了但Bean没加载再去看条件评估报告。简单粗暴但非常有效。5.3 版本升级时Spring.factories的迁移经验从Spring Boot 2.6升级到3.x最让框架作者头疼的就是spring.factories的迁移。我自己的做法是三步走先全局搜索所有META-INF/spring.factories把里面的EnableAutoConfiguration条目全部抽出来。新建META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports把抽出来的自动配置类一个个填进去。检查spring.factories中是否还有其他扩展点比如ApplicationListener、ApplicationContextInitializer、EnvironmentPostProcessor。这些扩展点不迁移继续留在老文件里即可。还有一个容易忽略的问题Spring Boot 3.x里自动配置类的写法也变了。如果配置类上同时使用了Configuration和Conditional建议换成AutoConfiguration并且尽量避免在自动配置类里做包扫描。自动配置类的加载顺序也要统一用AutoConfigureBefore和AutoConfigureAfter控制不要再依赖Properties文件里value的先后顺序。我自己在实际开发中的体会是越往上层的框架越感觉不到spring.factories的存在但一旦你开始写自己的Starter、中间件或者公司内部公共库它就变成了绕不开的核心设施。新项目我建议全部使用新的AutoConfiguration.imports可老项目里大量的存量代码仍然依赖spring.factories所以这套机制不仅要会还要能讲清楚。最后分享一个小技巧排查自动配置失败时先别急着看条件评估先用IDE的“External Libraries”搜索所有jar包里的META-INF/spring.factories有些第三方依赖之间会互相覆盖同路径文件这种肉眼不容易发现的问题往往才是启动异常的真凶。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询