小结: getSpringFactoriesInstances从 `spring.factories` 文件中加载和实例化指定类型的类
getSpringFactoriesInstances 方法工作原理
getSpringFactoriesInstances
是 Spring Boot 框架中的一个核心方法,用于从 spring.factories
文件中加载和实例化指定类型的类。这是 Spring Boot 实现自动配置和插件化扩展的关键机制。
1. 基本功能
该方法的主要作用是:
- 从 classpath 下所有 jar 包中的
META-INF/spring.factories
文件中读取配置 - 根据指定的类型查找对应的实现类
- 实例化这些实现类并返回实例列表
2. 工作流程
源码
/*** 静态工厂方法:创建一个 SpringFactoriesLoader 实例,用于加载指定资源位置(resourceLocation)的工厂配置。* @param resourceLocation 要加载的工厂配置文件的资源路径(例如 "META-INF/spring.factories" 或自定义路径),不能为空。* @param classLoader 用于加载资源的类加载器,可以为 null。* @return 一个配置好的 SpringFactoriesLoader 实例。*/
public static SpringFactoriesLoader forResourceLocation(String resourceLocation, @Nullable ClassLoader classLoader) {// 1. 输入验证:确保 resourceLocation 参数不为空、不为 null 且不只包含空白字符。// 如果验证失败,会抛出 IllegalArgumentException 异常。Assert.hasText(resourceLocation, "'resourceLocation' must not be empty");// 2. 确定加载资源所使用的类加载器 (ClassLoader)。// 如果传入的 classLoader 不为 null,则使用它;否则,使用加载 SpringFactoriesLoader 类本身的类加载器。ClassLoader resourceClassLoader = classLoader != null ? classLoader : SpringFactoriesLoader.class.getClassLoader();// 3. 获取或创建针对特定类加载器的工厂缓存映射 (factoriesCache)。// `cache` 是一个静态的、线程安全的映射 (Map<ClassLoader, Map<String, Factories>>),用于缓存不同类加载器加载的工厂配置。// `computeIfAbsent` 方法确保:如果当前类加载器 (resourceClassLoader) 对应的缓存映射不存在,则创建一个新的 `ConcurrentReferenceHashMap` 并放入 `cache` 中。// `ConcurrentReferenceHashMap` 是 Spring 提供的一个线程安全的哈希映射,其键或值可以使用弱引用或软引用,有助于防止内存泄漏。Map<String, Factories> factoriesCache = (Map)cache.computeIfAbsent(resourceClassLoader, (key) -> {return new ConcurrentReferenceHashMap();});// 4. 在针对当前类加载器的缓存映射 (factoriesCache) 中,获取或创建指定资源位置 (resourceLocation) 的工厂集合 (Factories)。// `computeIfAbsent` 方法确保:如果该资源位置对应的 `Factories` 对象不存在,则调用 `loadFactoriesResource` 方法加载资源,并用加载结果创建一个新的 `Factories` 实例,然后放入 `factoriesCache` 中。// 这个 `Factories` 对象内部封装了解析后的工厂类名列表(按工厂接口/抽象类分组)。Factories factories = (Factories)factoriesCache.computeIfAbsent(resourceLocation, (key) -> {return new Factories(loadFactoriesResource(resourceClassLoader, resourceLocation));});// 5. 创建并返回一个新的 SpringFactoriesLoader 实例。// 该实例的构造函数接收两个参数:// - 传入的原始 classLoader (可能为 null)// - 从缓存中获取的 `Factories` 对象,通过其 `byType()` 方法获取按类型(工厂接口名)组织的映射 (Map<String, List<String>>)。// 这个新创建的 loader 实例就可以用来实例化和获取在指定 resourceLocation 中配置的工厂了。return new SpringFactoriesLoader(classLoader, factories.byType());
}/*** 从指定资源位置加载工厂配置文件* <p>该方法会读取指定路径下的所有资源文件,解析其中的属性配置,* 并将相同工厂类型的实现类进行合并和去重处理。* @param classLoader 用于加载资源的类加载器* @param resourceLocation 资源文件的位置路径* @return 包含工厂类型与实现类列表映射关系的只读Map* @throws IllegalArgumentException 当无法从指定位置加载资源时抛出*/
protected static Map<String, List<String>> loadFactoriesResource(ClassLoader classLoader, String resourceLocation) {Map<String, List<String>> result = new LinkedHashMap<>();try {// 获取所有匹配resourceLocation的资源URLEnumeration<URL> urls = classLoader.getResources(resourceLocation);while (urls.hasMoreElements()) {UrlResource resource = new UrlResource(urls.nextElement());// 加载并解析资源文件中的属性配置Properties properties = PropertiesLoaderUtils.loadProperties(resource);properties.forEach((name, value) -> {String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) value);List<String> implementations = result.computeIfAbsent(((String) name).trim(),key -> new ArrayList<>(factoryImplementationNames.length));Arrays.stream(factoryImplementationNames).map(String::trim).forEach(implementations::add);});}// 对每个工厂类型对应的实现类列表进行去重并转换为不可变列表result.replaceAll(SpringFactoriesLoader::toDistinctUnmodifiableList);}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" + resourceLocation + "]", ex);}return Collections.unmodifiableMap(result);}
// 简化版的实现逻辑
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {// 1. 获取类加载器ClassLoader classLoader = getClassLoader();// 2. 从 spring.factories 文件中获取指定类型的实现类名Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));// 3. 创建实例List<T> instances = createSpringFactoriesInstances(type, names, classLoader);// 4. 排序(如果实现了 AnnotationAwareOrderComparator 接口)AnnotationAwareOrderComparator.sort(instances);return instances;
}
3. spring.factories 文件格式
spring.factories
文件采用 properties 格式,内容类似于:
org\springframework\boot\spring-boot\3.5.4\spring-boot-3.5.4.jar!\META-INF\spring.factories
# spring-boot-autoconfigure-3.5.4.jar 中的 spring.factories 示例
# Logging Systems
org.springframework.boot.logging.LoggingSystemFactory=\
org.springframework.boot.logging.java.JavaLoggingSystem$Factory,\
org.springframework.boot.logging.log4j2.Log4J2LoggingSystem$Factory,\
org.springframework.boot.logging.logback.LogbackLoggingSystem$Factory# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader# ConfigData Location Resolvers
org.springframework.boot.context.config.ConfigDataLocationResolver=\
org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\
org.springframework.boot.context.config.StandardConfigDataLocationResolver,\
org.springframework.boot.context.config.SystemEnvironmentConfigDataLocationResolver# ConfigData Loaders
org.springframework.boot.context.config.ConfigDataLoader=\
org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\
org.springframework.boot.context.config.StandardConfigDataLoader,\
org.springframework.boot.context.config.SystemEnvironmentConfigDataLoader# Application Context Factories
org.springframework.boot.ApplicationContextFactory=\
org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContextFactory,\
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContextFactory# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.io.ProtocolResolverApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor,\
org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.ReactorEnvironmentPostProcessor# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.context.config.ConfigDataNotFoundFailureAnalyzer,\
org.springframework.boot.context.properties.IncompatibleConfigurationFailureAnalyzer,\
org.springframework.boot.context.properties.NotConstructorBoundInjectionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.AotInitializerNotFoundFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.MissingParameterNamesFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.MutuallyExclusiveConfigurationPropertiesFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PatternParseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.liquibase.LiquibaseChangelogMissingFailureAnalyzer,\
org.springframework.boot.web.context.MissingWebServerFactoryBeanFailureAnalyzer,\
org.springframework.boot.web.embedded.tomcat.ConnectorStartFailureAnalyzer# Failure Analysis Reporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter# Database Initializer Detectors
org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector=\
org.springframework.boot.flyway.FlywayDatabaseInitializerDetector,\
org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializerDetector,\
org.springframework.boot.liquibase.LiquibaseDatabaseInitializerDetector,\
org.springframework.boot.orm.jpa.JpaDatabaseInitializerDetector,\
org.springframework.boot.r2dbc.init.R2dbcScriptDatabaseInitializerDetector# Depends On Database Initialization Detectors
org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector=\
org.springframework.boot.jdbc.SpringJdbcDependsOnDatabaseInitializationDetector,\
org.springframework.boot.jooq.JooqDependsOnDatabaseInitializationDetector,\
org.springframework.boot.orm.jpa.JpaDependsOnDatabaseInitializationDetector,\
org.springframework.boot.sql.init.dependency.AnnotationDependsOnDatabaseInitializationDetector# Resource Locator Protocol Resolvers
org.springframework.core.io.ProtocolResolver=\
org.springframework.boot.io.Base64ProtocolResolver# Resource File Path Resolvers
org.springframework.boot.io.ApplicationResourceLoader$FilePathResolver=\
org.springframework.boot.io.ClassPathResourceFilePathResolver,\
org.springframework.boot.web.reactive.context.FilteredReactiveWebContextResourceFilePathResolver,\
org.springframework.boot.web.servlet.context.ServletContextResourceFilePathResolver
作用
在Spring Boot中,spring.factories
文件位于META-INF
目录下,它允许开发者声明自己的组件以供Spring Boot自动发现和使用。这个文件中的每一行都遵循以下格式:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration
这里的左边部分是接口或抽象类的全限定名,右边则是实现了该接口或继承了该抽象类的具体类的全限定名。当Spring Boot启动时,它会读取所有spring.factories
文件,并将它们的内容加载到内存中,形成类似于你提供的那种映射结构。
具体作用
- ApplicationContextFactory: 这些工厂用于创建
ApplicationContext
实例,这是Spring框架的核心容器。 - SpringApplicationRunListener: 监听Spring Application的运行过程,可以用来在不同阶段执行自定义逻辑。
- ApplicationListener: 应用程序事件监听器,用于响应各种应用程序事件。
- LoggingSystemFactory: 日志系统工厂,用于创建日志系统。
- DatabaseInitializerDetector: 数据库初始化检测器,用于检测是否需要进行数据库初始化。
- PropertySourceLoader: 属性源加载器,用于加载属性源。
- FilePathResolver: 文件路径解析器,用于解析文件路径。
- FailureAnalyzer: 失败分析器,用于分析应用程序启动失败的原因。
- ProtocolResolver: 协议解析器,用于解析协议。
- SpringBootExceptionReporter: Spring Boot异常报告器,用于报告Spring Boot相关的异常。
- DependsOnDatabaseInitializationDetector: 依赖于数据库初始化的检测器,用于检测组件是否依赖于数据库初始化。
- ConfigDataLocationResolver: 配置数据位置解析器,用于解析配置数据的位置。
- ConfigDataLoader: 配置数据加载器,用于加载配置数据。
- ApplicationContextInitializer: 应用程序上下文初始化器,用于初始化应用程序上下文。
- EnvironmentPostProcessor: 环境后处理器,用于处理环境。
4. 实际应用示例
this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
- BootstrapRegistryInitializer:用于引导注册的初始化器
- ApplicationContextInitializer:应用上下文初始化器
- ApplicationListener:应用事件监听器
5. 优势和作用
- 插件化扩展:第三方库可以通过在自己的
spring.factories
文件中添加配置来扩展 Spring Boot 功能 - 自动配置:Spring Boot 自动配置功能的基础实现机制
- 解耦合:通过配置文件方式将接口和实现类解耦,避免硬编码依赖
- 灵活性:运行时动态加载和实例化所需的组件
6. 实际场景举例
当 Spring Boot 启动时,会通过 getSpringFactoriesInstances
方法加载:
- 所有的
ApplicationListener
实现,用于监听应用启动过程中的各种事件 - 所有的
ApplicationContextInitializer
实现,用于初始化应用上下文 - 所有的
BootstrapRegistryInitializer
实现,用于引导阶段的初始化
Spring Boot 3.x 和 2.x 在自动配置方面有一些重要的变化和改进,特别是在如何自动导入依赖和服务方面。以下是它们之间的一些主要区别:
Spring Boot 2.x 自动配置规则
-
spring.factories
文件:- 在 Spring Boot 2.x 中,自动配置类和其他扩展点(如
ApplicationContextInitializer
,ApplicationListener
等)通过META-INF/spring.factories
文件来声明。 - 开发者需要在该文件中手动注册他们的自动配置类。
- 在 Spring Boot 2.x 中,自动配置类和其他扩展点(如
-
条件注解:
- 使用条件注解(如
@ConditionalOnClass
,@ConditionalOnProperty
等)来控制自动配置的加载逻辑。 - 这些注解允许根据不同的条件动态地包含或排除特定的配置。
- 使用条件注解(如
Spring Boot 3.x 自动配置规则
\org\springdoc\springdoc-openapi-starter-webmvc-ui\2.5.0\springdoc-openapi-starter-webmvc-ui-2.5.0.jar!\META-INF\spring\org.springframework.boot.autoconfigure.AutoConfiguration.imports
org.springdoc.webmvc.ui.SwaggerConfig
org.springdoc.core.properties.SwaggerUiConfigProperties
org.springdoc.core.properties.SwaggerUiConfigParameters
org.springdoc.core.properties.SwaggerUiOAuthProperties
org.springdoc.core.configuration.SpringDocUIConfiguration
-
spring.factories
被取代:- 在 Spring Boot 3.x 中,传统的
spring.factories
文件机制被新的spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件所取代。 - 自动配置类现在应该在这个新的文件中列出,而不是
spring.factories
。
- 在 Spring Boot 3.x 中,传统的
-
改进的自动配置机制:
- Spring Boot 3.x 提供了更加精确的条件匹配,这可以减少不必要的自动配置,从而提高性能。
- 新的
@AutoConfiguration
注解用于替代以前的自动配置方式,并且提供了更灵活的配置优先级设置。
-
AOT(Ahead-of-Time)编译支持:
- Spring Boot 3.x 引入了对 AOT 编译的支持,这可以在编译时生成优化后的代码,以进一步减少启动时间和内存占用。
- 这也影响了自动配置的方式,因为一些配置可以在编译期就确定下来。
-
Jakarta EE 命名空间迁移:
- Spring Boot 3.x 完全迁移到 Jakarta EE 9+ 的命名空间(例如从
javax.servlet
到jakarta.servlet
),这意味着任何与 Servlet API 或其他 Jakarta EE 规范相关的自动配置也需要更新到新的包结构。
- Spring Boot 3.x 完全迁移到 Jakarta EE 9+ 的命名空间(例如从
-
移除废弃的功能:
- 随着 Spring Boot 3.x 的发布,一些过时的自动配置和功能已经被移除或者标记为废弃。开发者需要检查并更新相应的配置以适应新版本的要求。\