当前位置: 首页 > news >正文

小结: 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));
  1. BootstrapRegistryInitializer:用于引导注册的初始化器
  2. ApplicationContextInitializer:应用上下文初始化器
  3. ApplicationListener:应用事件监听器

5. 优势和作用

  1. 插件化扩展:第三方库可以通过在自己的 spring.factories 文件中添加配置来扩展 Spring Boot 功能
  2. 自动配置:Spring Boot 自动配置功能的基础实现机制
  3. 解耦合:通过配置文件方式将接口和实现类解耦,避免硬编码依赖
  4. 灵活性:运行时动态加载和实例化所需的组件

6. 实际场景举例

当 Spring Boot 启动时,会通过 getSpringFactoriesInstances 方法加载:

  • 所有的 ApplicationListener 实现,用于监听应用启动过程中的各种事件
  • 所有的 ApplicationContextInitializer 实现,用于初始化应用上下文
  • 所有的 BootstrapRegistryInitializer 实现,用于引导阶段的初始化

Spring Boot 3.x 和 2.x 在自动配置方面有一些重要的变化和改进,特别是在如何自动导入依赖和服务方面。以下是它们之间的一些主要区别:

Spring Boot 2.x 自动配置规则

  1. spring.factories 文件:

    • 在 Spring Boot 2.x 中,自动配置类和其他扩展点(如 ApplicationContextInitializer, ApplicationListener 等)通过 META-INF/spring.factories 文件来声明。
    • 开发者需要在该文件中手动注册他们的自动配置类。
  2. 条件注解:

    • 使用条件注解(如 @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
  1. spring.factories 被取代:

    • 在 Spring Boot 3.x 中,传统的 spring.factories 文件机制被新的 spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件所取代。
    • 自动配置类现在应该在这个新的文件中列出,而不是 spring.factories
  2. 改进的自动配置机制:

    • Spring Boot 3.x 提供了更加精确的条件匹配,这可以减少不必要的自动配置,从而提高性能。
    • 新的 @AutoConfiguration 注解用于替代以前的自动配置方式,并且提供了更灵活的配置优先级设置。
  3. AOT(Ahead-of-Time)编译支持:

    • Spring Boot 3.x 引入了对 AOT 编译的支持,这可以在编译时生成优化后的代码,以进一步减少启动时间和内存占用。
    • 这也影响了自动配置的方式,因为一些配置可以在编译期就确定下来。
  4. Jakarta EE 命名空间迁移:

    • Spring Boot 3.x 完全迁移到 Jakarta EE 9+ 的命名空间(例如从 javax.servletjakarta.servlet),这意味着任何与 Servlet API 或其他 Jakarta EE 规范相关的自动配置也需要更新到新的包结构。
  5. 移除废弃的功能:

    • 随着 Spring Boot 3.x 的发布,一些过时的自动配置和功能已经被移除或者标记为废弃。开发者需要检查并更新相应的配置以适应新版本的要求。\
http://www.lryc.cn/news/617206.html

相关文章:

  • 一维码+二维码+字符识别
  • 关于开发面对颠覆性需求变更的思考
  • SpringBoot 实现 Excel 导入导出功能的三种实现方式
  • MySQL语句,体系结构等基础知识解析
  • 量子计算:叩响金融定价革命的大门——期权定价的范式转移
  • 【PyTorch学习笔记 - 01】 Tensors(张量)
  • MLAG双活网络妙招:BGP + 静态VRRP实现智能负载均衡
  • MATLAB实现遗传算法求解路网路由问题
  • 【FAQ】Win11创建资源不足绕开微软账号登录
  • 【数据结构入门】树
  • 2025世界机器人大会|具身智能机器人十大发展趋势
  • 人脸识别系统技术文档
  • C9800 ISSU升级
  • Netty使用CA证书实现tls双认证
  • Linux ethernet驱动移植之常见问题
  • html转成markdown(1.0.0)
  • Mybatis学习之缓存(九)
  • 文件编辑html
  • 通用 maven 私服 settings.xml 多源配置文件(多个仓库优先级配置)
  • Django配置sqllite之外的数据库
  • 爬虫与数据分析结合案例学习总结
  • Apache Ignite 核心组件:GridClosureProcessor解析
  • pom.xml父子模块配置
  • 【Maven】01 - 入门篇
  • Maven 的 module 管理
  • 基于Spring Data Elasticsearch的分布式全文检索与集群性能优化实践指南
  • Maven 报错:Blocked mirror for repositories【完美解决】
  • 直接编辑pdf文件教程
  • SpringBoot 自动配置核心机制(面试高频考点)
  • wpf问题记录