资讯动态

SpringFactoriesLoader 源码解析:读懂 Spring Boot 自动装配背后的 SPI 加载器

发布时间:2026/9/13 21:46:27 来源:尧图企业网站定制
SpringFactoriesLoader 源码解析读懂 Spring Boot 自动装配背后的 SPI 加载器【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/GitHub_Trending/so/source-code-hunterSpringFactoriesLoader是 Spring 框架中与 Java SPI 机制对齐的通用工厂加载器它以META-INF/spring.factories为统一约定完成按接口名批量发现实现类 → 反射实例化 → 排序的完整链路是 Spring Boot 自动装配、SpringApplication 启动监听器、配置资源加载PropertySourceLoader等一切约定优于配置能力的地基。读完本文你将掌握SpringFactoriesLoader的三个核心方法loadFactories、loadSpringFactories、instantiateFactory的实现原理、spring.factories文件的编写规范以及它如何在 Spring Boot / Spring Cloud 中被实际调用。本文以仓库内源码阅读笔记 docs/Spring/clazz/Spring-SpringFactoriesLoader.md 为主体脉络并结合仓库中 Spring Boot、Spring Cloud 相关文档与代码片段进行纵深印证。一、SpringFactoriesLoader 是什么SpringFactoriesLoader的全限定类名为org.springframework.core.io.support.SpringFactoriesLoader位于 Spring 的核心模块 spring-core 中。它的作用可以概括为一句话读取 classpath 下所有META-INF/spring.factories文件将工厂接口全限定名 → 逗号分隔的实现类名列表的映射关系解析出来并支持按接口类型加载、实例化、排序这批实现。它常被拿来与 Java 自带的 SPIService Provider Interface机制作对比Java SPI 通过META-INF/services/接口全限定名文件声明实现类用ServiceLoader加载Spring 的 SPI 通过统一的META-INF/spring.factories文件声明所有扩展点一份文件内可以同时声明多个接口与实现类由SpringFactoriesLoader统一解析。这种一个文件管所有扩展点的设计让 Spring Boot 生态中的组件如自动配置类、监听器、加载器等可以零配置、纯声明式地被宿主应用发现是EnableAutoConfiguration自动装配得以成立的底层支撑。二、类信息与测试配套在 docs/Spring/clazz/Spring-SpringFactoriesLoader.md 中明确给出了该类的基本信息全路径org.springframework.core.io.support.SpringFactoriesLoader测试类org.springframework.core.io.support.SpringFactoriesLoaderTests配套测试的存在说明该机制具有独立、可验证的行为边界测试目录下的META-INF/spring.factories文件详见本文第四节就是验证解析与加载逻辑的样例数据读者可以在自己的环境中对SpringFactoriesLoaderTests进行断点跟踪观察loadFactories与loadSpringFactories的真实执行过程。三、loadFactories加载并实例化工厂loadFactories是SpringFactoriesLoader的对外主入口职责是加载并实例化工厂——即不仅把实现类名读出来还直接通过反射创建出实例对象返回给调用方。核心源码如下摘自 docs/Spring/clazz/Spring-SpringFactoriesLoader.md#L9-L33public static T ListT loadFactories(ClassT factoryType, Nullable ClassLoader classLoader) { Assert.notNull(factoryType, factoryType must not be null); ClassLoader classLoaderToUse classLoader; if (classLoaderToUse null) { classLoaderToUse SpringFactoriesLoader.class.getClassLoader(); } // 工厂实现类名称 ListString factoryImplementationNames loadFactoryNames(factoryType, classLoaderToUse); if (logger.isTraceEnabled()) { logger.trace(Loaded [ factoryType.getName() ] names: factoryImplementationNames); } ListT result new ArrayList(factoryImplementationNames.size()); for (String factoryImplementationName : factoryImplementationNames) { // 将实例化的工厂放入结果集合 result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse)); } // 排序 AnnotationAwareOrderComparator.sort(result); return result; }3.1 执行流程拆解入参校验Assert.notNull(factoryType, ...)保证工厂接口类型不为空类加载器兜底若调用方未传入classLoader则使用SpringFactoriesLoader.class.getClassLoader()获取实现类名列表调用loadFactoryNames(factoryType, classLoaderToUse)读取META-INF/spring.factories中该工厂类型对应的全部实现类名这是本类的核心解析逻辑见第四节反射实例化遍历每个实现类名调用instantiateFactory(...)创建实例并放入结果集合排序使用AnnotationAwareOrderComparator.sort(result)对实例排序——该比较器会读取实例上的Order注解以及Ordered接口的实现数字越小优先级越高。3.2 与 loadFactoryNames 的关系loadFactories在内部复用了loadFactoryNames(factoryType, classLoader)。该静态方法的大致职责是先从缓存的META-INF/spring.factories解析结果MapString, ListString中按工厂类型取出实现类名列表若工厂类型未出现在映射中则返回空列表。换句话说loadFactoryNames只读名字不创建对象loadFactories读取名字后再逐一实例化是读取 实例化的完整版。四、loadSpringFactoriesspring.factories 的解析核心loadSpringFactories是私有方法负责真正扫描 classpath 下的META-INF/spring.factories文件并把内容解析成MultiValueMapString, String工厂接口名 → 实现类名列表。核心源码如下摘自 docs/Spring/clazz/Spring-SpringFactoriesLoader.md#L36-L79private static MapString, ListString loadSpringFactories(Nullable ClassLoader classLoader) { MultiValueMapString, String result cache.get(classLoader); if (result ! null) { return result; } try { // 找 META-INF/spring.factories EnumerationURL urls (classLoader ! null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result new LinkedMultiValueMap(); while (urls.hasMoreElements()) { // 获取 路由地址 URL url urls.nextElement(); // url 解析 UrlResource resource new UrlResource(url); // Properties 解析 Properties properties PropertiesLoaderUtils.loadProperties(resource); // 循环解析结果 for (Map.Entry?, ? entry : properties.entrySet()) { String factoryTypeName ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { // 放入list result.add(factoryTypeName, factoryImplementationName.trim()); } } } // 放入缓存 cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException(Unable to load factories from location [ FACTORIES_RESOURCE_LOCATION ], ex); } }4.1 执行流程拆解查缓存MultiValueMapString, String result cache.get(classLoader)——解析结果按ClassLoader维度缓存命中则直接返回避免重复扫描定位资源通过classLoader.getResources(FACTORIES_RESOURCE_LOCATION)classLoader 为 null 时使用ClassLoader.getSystemResources扫描 classpath 下所有META-INF/spring.factories。注意getResources返回的是EnumerationURL意味着可以同时命中多个 jar 中的同名文件这也解释了为什么一个应用中会有多个 jar 各自携带自己的spring.factories逐个解析文件while 循环内完成五步获取文件路径URL url urls.nextElement()文件路径解析为UrlResource用PropertiesLoaderUtils.loadProperties(resource)按 Java Properties 格式读取文件遍历 Properties 的每个 entrykey是工厂接口全限定名trim 去掉首尾空白value用StringUtils.commaDelimitedListToStringArray按逗号切分成实现类名数组逐个 trim 后放入结果解析完成后放入cache以 classLoader 为 key异常处理任何 IOException 都会包装为IllegalArgumentException抛出提示信息中带出资源定位FACTORIES_RESOURCE_LOCATION即META-INF/spring.factories方便排查。4.2 测试目录下的 spring.factories 样例源码阅读笔记中给出了一份测试用样例位于测试目录的META-INF/spring.factories它精确演示了该文件的格式规范org.springframework.core.io.support.DummyFactory \ org.springframework.core.io.support.MyDummyFactory2, \ org.springframework.core.io.support.MyDummyFactory1 java.lang.String\ org.springframework.core.io.support.MyDummyFactory1 org.springframework.core.io.support.DummyPackagePrivateFactory\ org.springframework.core.io.support.DummyPackagePrivateFactory这份样例说明三个格式要点key 工厂接口的全限定名例如org.springframework.core.io.support.DummyFactoryvalue 逗号分隔的实现类全限定名列表多个实现类之间用逗号,分隔行末的反斜杠\是 Properties 格式的续行符用于长列表换行书写甚至可以对java.lang.String这类非自定义接口声明实现展示其通用性同一接口可以声明多个实现解析后按文件内出现顺序进入ListString排序交由后续的AnnotationAwareOrderComparator完成。EnumerationURL urls变量存放的正是扫描到的所有META-INF/spring.factories路径集合这是整个 while 循环的数据来源。五、instantiateFactory反射实例化工厂拿到实现类名之后instantiateFactory负责把它变成一个真实的对象。核心源码如下摘自 docs/Spring/clazz/Spring-SpringFactoriesLoader.md#L105-L125SuppressWarnings(unchecked) private static T T instantiateFactory(String factoryImplementationName, ClassT factoryType, ClassLoader classLoader) { try { Class? factoryImplementationClass ClassUtils.forName(factoryImplementationName, classLoader); if (!factoryType.isAssignableFrom(factoryImplementationClass)) { throw new IllegalArgumentException( Class [ factoryImplementationName ] is not assignable to factory type [ factoryType.getName() ]); } return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance(); } catch (Throwable ex) { throw new IllegalArgumentException( Unable to instantiate factory class [ factoryImplementationName ] for factory type [ factoryType.getName() ], ex ); } }执行要点类加载ClassUtils.forName(factoryImplementationName, classLoader)按全限定名加载实现类的Class对象类型校验factoryType.isAssignableFrom(factoryImplementationClass)检查实现类是否确实实现了目标工厂接口不满足则直接抛出IllegalArgumentException从源头拦截配置错误无参构造实例化ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance()获取可访问的无参构造器accessibleConstructor会处理构造器可见性确保即使构造器不是 public 也能实例化并创建对象。这意味着通过spring.factories声明的实现类必须提供可访问的无参构造器异常包装任何反射异常都会被包装为IllegalArgumentException错误信息同时包含实现类名与工厂接口名便于定位是哪个扩展点出了问题。六、在 Spring Boot / Spring Cloud 中的实际应用SpringFactoriesLoader的价值最终体现在它对上层框架的支撑上。本仓库的 Spring Boot、Spring Cloud 文档中保留了多处直接调用证据。6.1 自动装配读取 EnableAutoConfiguration 候选配置Spring Boot 自动装配的入口是SpringBootApplication→EnableAutoConfiguration→Import(AutoConfigurationImportSelector.class)。在 docs/SpringBoot/SpringBoot-自动装配.md 中可以看到AutoConfigurationImportSelector的getCandidateConfigurations方法protected 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; }这里getSpringFactoriesLoaderFactoryClass()返回的正是EnableAutoConfiguration.class。也就是说Spring Boot 启动时会用SpringFactoriesLoader读取所有 jar 中META-INF/spring.factories里org.springframework.boot.autoconfigure.EnableAutoConfiguration这个 key 对应的全部自动配置类名然后依次去重removeDuplicates通过LinkedHashSet、剔除 excludegetExclusionscheckExcludedClasses、按条件过滤filter配合AutoConfigurationImportFilter、最终排序注册。这就是只要引入依赖 jar相关自动配置就能生效的原理所在。典型的spring.factories自动配置声明位于 spring-boot-autoconfigure 的META-INF/spring.factories形如# 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,\6.2 启动监听器SpringApplication 的 getSpringFactoriesInstances在 docs/SpringBoot/Spring-Boot-Run.md 中SpringApplication通过getSpringFactoriesInstances加载启动阶段的各种扩展点private T CollectionT getSpringFactoriesInstances(ClassT type, Class?[] parameterTypes, Object... args) { ClassLoader classLoader getClassLoader(); // Use names and ensure unique to protect against duplicates // 读取 spring.factories SetString names new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 创建SpringFactory实例 ListT instances createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); /** * 排序 {link Ordered} */ AnnotationAwareOrderComparator.sort(instances); return instances; }注意这里与loadFactories的两点差异先经LinkedHashSet去重再通过反射构造器创建实例createSpringFactoriesInstances使用getDeclaredConstructor(parameterTypes)支持带参构造器例如传入SpringApplication.class与String[].class排序同样使用AnnotationAwareOrderComparator。SpringApplicationRunListener等启动监听器正是通过此方式从spring.factories中被发现和实例化的这也是为什么在SpringApplication.run()的早期阶段IoC 容器尚未创建就能完成 SPI 装配。6.3 配置资源加载PropertySourceLoader在 docs/SpringBoot/SpringBoot-application-load.md 中环境配置的Loader构造方法使用loadFactories加载属性源解析器// 配置信息加载器初始化 this.propertySourceLoaders SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader());通过这种方式application.properties与application.yml的解析器如YamlPropertySourceLoader都是从META-INF/spring.factories中以org.springframework.boot.env.PropertySourceLoader为 key 加载出来的。文档中特别指出搜索到的YamlPropertySourceLoader与之前定位 yml 字符串时找到的类一致验证了该加载路径的正确性。6.4 Spring Cloud 中的扩展实践Spring Cloud 各组件同样重度依赖spring.factories。docs/SpringCloud/spring-cloud-commons-source-note.md 中展示了spring-cloud-commons.jar!/META-INF/spring.factories的部分内容org.springframework.boot.autoconfigure.EnableAutoConfiguration\ org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration,\ org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration,\并且该文档多次强调服务发现DiscoveryClient、负载均衡LoadBalancerAutoConfiguration等能力均通过META-INF/spring.factories中EnableAutoConfiguration下的声明自动生效BootstrapImportSelector的作用就是读取META-INF/spring.factories文件把 key 为BootstrapConfiguration的类注册进容器存在一个特殊约束EnableDiscoveryClient的实现类无法通过spring.factories中EnableDiscoveryClient.class.getName()作为 key 自动注册必须显式注册到 BeanFactory——这是该文档从源码层面得出的边界结论。这说明理解SpringFactoriesLoader是理解整个 Spring Cloud 启动与自动装配链路的前提。七、实操如何通过 spring.factories 扩展自己的工厂结合前文源码可以总结出自定义扩展点的完整步骤以扩展PropertySourceLoader为例定义工厂接口如果是扩展已有接口则跳过此步例如直接使用org.springframework.boot.env.PropertySourceLoader实现接口并提供无参构造器public class MyPropertySourceLoader implements PropertySourceLoader { public MyPropertySourceLoader() { } // 无参构造器必须可访问 // 实现 getFileExtensions() 与 load(...) 方法 }在资源目录下创建META-INF/spring.factories若项目已有该文件则追加按第四节格式声明org.springframework.boot.env.PropertySourceLoader\ com.example.MyPropertySourceLoader确保文件被打入 classpathMETA-INF/spring.factories位于src/main/resources/META-INF/下即可随 jar 发布若使用自定义打包方式需确保该文件存在于最终产物中这正是getCandidateConfigurations中Assert.notEmpty那句报错信息提醒的场景验证应用启动时通过SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader())即可看到你的实现被加载若实现类同时实现了Ordered或标注了OrderAnnotationAwareOrderComparator会按序排列。八、注意事项与常见问题实现类必须有无参构造器instantiateFactory通过ReflectionUtils.accessibleConstructor(...).newInstance()创建对象若构造器带参或不可访问将抛出IllegalArgumentException实现类必须实现目标接口factoryType.isAssignableFrom(factoryImplementationClass)的强校验会在加载期拦截类型不匹配的配置多个 jar 同名文件会合并classLoader.getResources会收集 classpath 上所有META-INF/spring.factories同名 key 的实现类会全部进入列表loadFactories场景下配合排序器决定先后解析结果按 ClassLoader 缓存cache以ClassLoader为 key 缓存解析结果同一类加载器下重复加载不会重复扫描文件这也意味着热部署/类加载器变更时需留意缓存命中行为顺序由排序器决定AnnotationAwareOrderComparator依据Order/Ordered排序未声明顺序的实现保持文件内先后次序可对任意类型声明工厂从测试样例可见 key 甚至可以是java.lang.String机制本身不限制接口类型只要实现类满足类型兼容与可实例化即可。九、总结SpringFactoriesLoader用不到百行代码完成了约定文件META-INF/spring.factories→ 类名解析 → 反射实例化 → 排序的完整 SPI 加载链路loadFactories是面向调用方的实例化入口loadSpringFactories是底层文件解析与缓存实现instantiateFactory完成最后的类型校验与对象创建。Spring Boot 的自动装配、启动监听器、属性源加载器以及 Spring Cloud 的各类自动配置全部建立在这一机制之上。想进一步深入可以从本仓库的 SpringFactoriesLoader 源码阅读笔记 起步结合 Spring Boot 自动装配、Spring Boot 启动流程、Spring Boot 配置加载 以及 Spring Cloud Commons 源码笔记 逐层追踪即可完整打通从框架底层 SPI 到应用层自动装配的整条调用链。【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/GitHub_Trending/so/source-code-hunter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价