4-Spring SPI机制解读

Spring SPI

Spring为什么要自己实现SPI

Java SPI的不足

虽然Java SPI机制很强大,但在实际应用中存在一些限制:

1. 无法按需加载

java 复制代码
// Java SPI: 必须遍历所有实现才能获取特定实现
ServiceLoader<MyService> loader = ServiceLoader.load(MyService.class);
for (MyService service : loader) {
    if (service instanceof SpecificImpl) {
        // 找到了!但是其他所有实现也被实例化了
        break;
    }
}

2. 配置文件分散

复制代码
每个接口一个配置文件:
META-INF/services/
    ├── com.example.service.UserService
    ├── com.example.service.OrderService
    ├── com.example.service.PaymentService
    └── com.example.service.LogService

难以统一管理和查看

3. 无法批量获取类名

java 复制代码
// Java SPI: 只能获取实例,无法只获取类名
ServiceLoader<Driver> loader = ServiceLoader.load(Driver.class);
// 没有直接的API获取所有类名列表

4. 不支持分组/分类

java 复制代码
// 无法区分不同用途的实现
// 例如: 测试环境用的实现 vs 生产环境用的实现

Spring SPI的改进

Spring设计了自己的SPI机制,主要改进:

特性 Java SPI Spring SPI
配置文件 每个接口一个文件 统一在spring.factories
文件格式 纯文本(每行一个类名) Properties格式(key=value)
加载方式 只能获取实例 可以只获取类名,按需实例化
批量操作 ✅ 支持批量获取
条件过滤 ✅ 支持条件注解过滤
缓存策略 简单缓存 多级缓存

核心优势示意:

复制代码
Java SPI流程:
加载配置 → 遍历实例化 → 全部创建 → 选择需要的
(性能差,资源浪费)

Spring SPI流程:
加载配置 → 获取类名列表 → 条件过滤 → 按需实例化
(性能好,资源节约)

SpringFactoriesLoader核心类

类的基本信息

位置: org.springframework.core.io.support.SpringFactoriesLoader

作用: 加载并实例化META-INF/spring.factories文件中配置的工厂类

类签名:

java 复制代码
public final class SpringFactoriesLoader {
    // 工具类,私有构造器
    private SpringFactoriesLoader() {
    }
}

核心常量和字段

java 复制代码
public final class SpringFactoriesLoader {

    /**
     * 📌 配置文件位置
     */
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

    /**
     * 📌 缓存: ClassLoader -> (接口名 -> 实现类名列表)
     * 使用ConcurrentReferenceHashMap实现弱引用,避免内存泄漏
     */
    private static final Map<ClassLoader, MultiValueMap<String, String>> cache =
        new ConcurrentReferenceHashMap<>();
}

缓存结构说明:

复制代码
cache (ConcurrentReferenceHashMap)
└── ClassLoader1 (AppClassLoader)
    └── MultiValueMap
        ├── "org.springframework.boot.autoconfigure.EnableAutoConfiguration"
        │   ├── "com.example.AutoConfig1"
        │   ├── "com.example.AutoConfig2"
        │   └── "com.example.AutoConfig3"
        │
        ├── "org.springframework.context.ApplicationListener"
        │   ├── "com.example.Listener1"
        │   └── "com.example.Listener2"
        │
        └── "org.springframework.boot.env.PropertySourceLoader"
            ├── "com.example.PropertiesLoader"
            └── "com.example.YamlLoader"

SpringFactoriesLoader源码完整解析

核心方法1: loadFactoryNames()

作用: 加载指定接口的所有实现类名(不实例化)

源码分析:

java 复制代码
/**
 * 加载指定工厂接口的所有实现类名
 *
 * @param factoryType 工厂接口Class
 * @param classLoader 类加载器(null则使用默认)
 * @return 实现类名列表
 */
public static List<String> loadFactoryNames(Class<?> factoryType,
                                           @Nullable ClassLoader classLoader) {
    // 📌 步骤1: 获取接口全限定名作为key
    String factoryTypeName = factoryType.getName();

    // 📌 步骤2: 加载所有spring.factories,返回Map
    // 📌 步骤3: 从Map中获取指定接口的实现类列表
    return loadSpringFactories(classLoader)
        .getOrDefault(factoryTypeName, Collections.emptyList());
}

调用示例:

java 复制代码
// 获取所有自动配置类的类名
List<String> configNames = SpringFactoriesLoader.loadFactoryNames(
    EnableAutoConfiguration.class,
    getClass().getClassLoader()
);

// 输出示例:
// [
//   "org.springframework.boot.autoconfigure.aop.AopAutoConfiguration",
//   "org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration",
//   ...
// ]

核心方法2: loadFactories()

作用: 加载并实例化指定接口的所有实现类

源码分析:

java 复制代码
/**
 * 加载并实例化指定工厂接口的所有实现类
 *
 * @param factoryType 工厂接口Class
 * @param classLoader 类加载器
 * @return 实例列表
 */
public static <T> List<T> loadFactories(Class<T> factoryType,
                                       @Nullable ClassLoader classLoader) {
    Assert.notNull(factoryType, "'factoryType' must not be null");

    // 📌 步骤1: 获取类加载器
    ClassLoader classLoaderToUse = classLoader;
    if (classLoaderToUse == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }

    // 📌 步骤2: 加载所有实现类名
    List<String> factoryImplementationNames =
        loadFactoryNames(factoryType, classLoaderToUse);

    if (logger.isTraceEnabled()) {
        logger.trace("Loaded [" + factoryType.getName() + "] names: " +
                    factoryImplementationNames);
    }

    // 📌 步骤3: 实例化
    List<T> result = new ArrayList<>(factoryImplementationNames.size());
    for (String factoryImplementationName : factoryImplementationNames) {
        // ⚠️ 调用 instantiateFactory 实例化每个类
        result.add(instantiateFactory(factoryImplementationName,
                                     factoryType,
                                     classLoaderToUse));
    }

    // 📌 步骤4: 排序(基于@Order注解或Ordered接口)
    AnnotationAwareOrderComparator.sort(result);

    return result;
}

实例化方法:

java 复制代码
/**
 * 实例化单个工厂类
 */
@SuppressWarnings("unchecked")
private static <T> T instantiateFactory(String factoryImplementationName,
                                       Class<T> factoryType,
                                       ClassLoader classLoader) {
    try {
        // 📌 步骤1: 加载类
        Class<?> factoryImplementationClass =
            ClassUtils.forName(factoryImplementationName, classLoader);

        // 📌 步骤2: 验证类型
        if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
            throw new IllegalArgumentException(
                "Class [" + factoryImplementationName +
                "] is not assignable to factory type [" + factoryType.getName() + "]");
        }

        // 📌 步骤3: 实例化(调用无参构造器)
        return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass)
            .newInstance();

    } catch (Throwable ex) {
        throw new IllegalArgumentException(
            "Unable to instantiate factory class [" +
            factoryImplementationName + "] for factory type [" +
            factoryType.getName() + "]", ex);
    }
}

核心方法3: loadSpringFactories() - 最重要!

这是整个Spring SPI机制的核心方法!

完整源码解析:

java 复制代码
/**
 * 加载所有spring.factories文件
 *
 * @param classLoader 类加载器
 * @return Map<接口名, List<实现类名>>
 */
private static Map<String, List<String>> loadSpringFactories(
        @Nullable ClassLoader classLoader) {

    // ⚠️ 关键点1: 缓存检查
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        return result;  // 缓存命中,直接返回
    }

    try {
        // ⚠️ 关键点2: 加载所有spring.factories文件
        Enumeration<URL> urls = (classLoader != null ?
                classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));

        // 使用LinkedMultiValueMap保持顺序
        result = new LinkedMultiValueMap<>();

        // ⚠️ 关键点3: 遍历所有配置文件
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();

            // ⚠️ 关键点4: 将URL包装为Resource
            UrlResource resource = new UrlResource(url);

            // ⚠️ 关键点5: 加载Properties
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);

            // ⚠️ 关键点6: 解析每个key-value
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                // key: 接口全限定名
                String factoryTypeName = ((String) entry.getKey()).trim();

                // value: 逗号分隔的实现类名列表
                for (String factoryImplementationName :
                     StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                    // 添加到结果Map中
                    result.add(factoryTypeName, factoryImplementationName.trim());
                }
            }
        }

        // ⚠️ 关键点7: 放入缓存
        cache.put(classLoader, result);
        return result;

    } catch (IOException ex) {
        throw new IllegalArgumentException(
            "Unable to load factories from location [" +
            FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

逐步分解:

步骤1: 缓存检查

java 复制代码
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
    return result;  // 命中缓存
}

为什么用MultiValueMap?

  • 一个key(接口)对应多个value(实现类)
  • 自动处理重复添加

步骤2-3: 扫描所有jar包的spring.factories

java 复制代码
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");

while (urls.hasMoreElements()) {
    URL url = urls.nextElement();
    // 例如: jar:file:/path/spring-boot-autoconfigure-2.7.0.jar!/META-INF/spring.factories
}

可能扫描到的文件:

复制代码
jar:file:/xxx/spring-boot-2.7.0.jar!/META-INF/spring.factories
jar:file:/xxx/spring-boot-autoconfigure-2.7.0.jar!/META-INF/spring.factories
jar:file:/xxx/spring-beans-5.3.20.jar!/META-INF/spring.factories
jar:file:/xxx/spring-context-5.3.20.jar!/META-INF/spring.factories
jar:file:/xxx/mybatis-spring-boot-starter-2.2.0.jar!/META-INF/spring.factories
...

步骤4-5: 加载Properties

java 复制代码
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);

// properties内容示例:
// org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
// com.example.Config1,\
// com.example.Config2

步骤6: 解析key-value并存入MultiValueMap

java 复制代码
for (Map.Entry<?, ?> entry : properties.entrySet()) {
    String factoryTypeName = entry.getKey();  // 接口名

    // 分割逗号分隔的实现类
    String[] factoryNames = StringUtils.commaDelimitedListToStringArray(
        entry.getValue());

    for (String factoryName : factoryNames) {
        result.add(factoryTypeName, factoryName.trim());
    }
}

结果示例:

java 复制代码
result = {
    "org.springframework.boot.autoconfigure.EnableAutoConfiguration": [
        "org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration",
        "org.springframework.boot.autoconfigure.aop.AopAutoConfiguration",
        "org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration",
        // ... 130+ 个
    ],
    "org.springframework.context.ApplicationListener": [
        "org.springframework.boot.ClearCachesApplicationListener",
        "org.springframework.boot.builder.ParentContextCloserApplicationListener",
        // ...
    ],
    "org.springframework.boot.env.PropertySourceLoader": [
        "org.springframework.boot.env.PropertiesPropertySourceLoader",
        "org.springframework.boot.env.YamlPropertySourceLoader"
    ]
}

spring.factories配置文件详解

文件位置和格式

位置: META-INF/spring.factories

格式: 标准的Properties格式

示例 - spring-boot-autoconfigure包中的spring.factories:

properties 复制代码
# 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,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration

# 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.config.DelegatingApplicationListener

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor

# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer

# Property Source Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

格式规则

1. Key-Value结构

properties 复制代码
# key: 接口或注解的全限定名
# value: 实现类的全限定名,多个用逗号分隔
接口全限定名=实现类1,实现类2,实现类3

2. 续行符

properties 复制代码
# 使用反斜杠 \ 续行
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.Config1,\
com.example.Config2,\
com.example.Config3

3. 注释

properties 复制代码
# 井号开头的行是注释
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.Config1

4. 空白处理

properties 复制代码
# 首尾空白会被自动trim
org.springframework.context.ApplicationListener=\
  com.example.Listener1  ,  \
  com.example.Listener2

# 等价于:
org.springframework.context.ApplicationListener=\
com.example.Listener1,\
com.example.Listener2

完整执行流程图

复制代码
┌─────────────────────────────────────────────────────────┐
│  SpringFactoriesLoader.loadFactoryNames(                │
│      EnableAutoConfiguration.class,                     │
│      classLoader)                                       │
└───────────────────┬─────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  1. 获取接口全限定名                                       │
│     factoryTypeName = "org.springframework.boot.       │
│         autoconfigure.EnableAutoConfiguration"          │
└───────────────────┬─────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  2. 调用 loadSpringFactories(classLoader)               │
└───────────────────┬─────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  3. 检查缓存 cache.get(classLoader)                      │
│     ├─ 命中 → 直接返回                                    │
│     └─ 未命中 → 继续加载                                  │
└───────────────────┬─────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  4. 扫描所有jar包                                         │
│     classLoader.getResources(                           │
│         "META-INF/spring.factories")                    │
│     返回所有spring.factories文件的URL                     │
└───────────────────┬─────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  5. 遍历每个spring.factories文件                         │
│     while (urls.hasMoreElements())                      │
└───────────────────┬─────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  6. 加载Properties                                       │
│     PropertiesLoaderUtils.loadProperties(resource)      │
│     读取key=value内容                                     │
└───────────────────┬─────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  7. 解析每个entry                                         │
│     for (entry : properties.entrySet())                 │
│         key = 接口名                                      │
│         values = 逗号分隔的实现类列表                      │
│         存入MultiValueMap                                │
└───────────────────┬─────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  8. 合并所有文件的结果                                     │
│     result = {                                          │
│       "接口1": [实现1, 实现2, ...],                       │
│       "接口2": [实现3, 实现4, ...],                       │
│       ...                                               │
│     }                                                   │
└───────────────────┬─────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  9. 放入缓存                                              │
│     cache.put(classLoader, result)                      │
└───────────────────┬─────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────────────────────┐
│  10. 返回指定接口的实现类列表                              │
│      result.get(factoryTypeName)                        │
└─────────────────────────────────────────────────────────┘

上下游流程关系图

上游调用者(谁在用它)

下面我指举例了一些比较常见的,还有其他上游路径没有展示~

SpringBoot启动流程核心入口

流程如下:

SpringApplication构造器

setInitializers()

getSpringFactoriesInstances(ApplicationContextInitializer.class)

SpringFactoriesLoader.loadFactoryNames()

代码路径: SpringApplication.java

java 复制代码
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // ...
    
    // 上游调用1: 加载ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
    
    // 上游调用2: 加载ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(
        ApplicationListener.class));
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    // 直接调用SpringFactoriesLoader
    return SpringFactoriesLoader.loadFactories(type, classLoader);
}
自动配置核心流程

流程如下:

@EnableAutoConfiguration

AutoConfigurationImportSelector

getCandidateConfigurations()

SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class)

代码路径: AutoConfigurationImportSelector.java

java 复制代码
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
                                                  AnnotationAttributes attributes) {
    // 核心调用: 加载所有自动配置类
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
        getSpringFactoriesLoaderFactoryClass(), // EnableAutoConfiguration.class
        getBeanClassLoader());
    
    Assert.notEmpty(configurations, 
        "No auto configuration classes found in META-INF/spring.factories");
    return configurations;
}

下游被加载内容(它加载了什么)

EnableAutoConfiguration - 自动配置类(最重要!)

spring.factories配置示例:

text 复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
# ... 130+ 个自动配置类

这些自动配置类会:

  • 根据条件注解决定是否生效
  • 自动创建Bean
  • 配置默认属性
ApplicationContextInitializer - 容器初始化器
text 复制代码
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer

作用时机: 在容器刷新(refresh)之前执行

ApplicationListener - 应用监听器
text 复制代码
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener

作用: 监听Spring Boot启动过程中的各种事件

还有其他下游,不一一显示了,遇见应该也能认出来的.

完整上下游调用链路图

text 复制代码
┌────────────────────────────────────────────────────────────────┐
│                     Spring Boot 启动入口                          │
│                  SpringApplication.run()                        │
└───────────────────────────┬────────────────────────────────────┘
                            ↓
┌────────────────────────────────────────────────────────────────┐
│                   上游调用者层                                     │
└────────────────────────────────────────────────────────────────┘
                            
    ┌───────────────────────┼───────────────────────┐
    ↓                       ↓                       ↓
┌─────────────┐    ┌──────────────────┐    ┌─────────────────┐
│SpringApplication│    │AutoConfiguration │    │FailureAnalyzers │
│  构造器        │    │ ImportSelector   │    │                 │
└──────┬──────┘    └────────┬─────────┘    └────────┬────────┘
       │                    │                        │
       ↓                    ↓                        ↓
   加载初始化器            加载自动配置类          加载失败分析器
   和监听器

                            ↓
┌────────────────────────────────────────────────────────────────┐
│                     核心SPI加载层                                 │
│               SpringFactoriesLoader                             │
│                                                                  │
│  核心方法:                                                        │
│  • loadFactoryNames()  - 获取类名                                │
│  • loadFactories()     - 获取实例                                │
│  • loadSpringFactories() - 加载配置                              │
└───────────────────────┬────────────────────────────────────────┘
                        ↓
            扫描所有jar包的spring.factories
                        ↓
┌────────────────────────────────────────────────────────────────┐
│                   spring.factories 配置文件                       │
└────────────────────────────────────────────────────────────────┘
    
    ├─ spring-boot.jar!/META-INF/spring.factories
    ├─ spring-boot-autoconfigure.jar!/META-INF/spring.factories
    ├─ mybatis-spring-boot-starter.jar!/META-INF/spring.factories
    └─ 自定义jar!/META-INF/spring.factories

                        ↓
┌────────────────────────────────────────────────────────────────┐
│                   下游被加载内容层                                 │
└────────────────────────────────────────────────────────────────┘

┌──────────────────────┐  ┌──────────────────────┐  ┌─────────────────┐
│ 自动配置类             │  │ 容器初始化器           │  │ 事件监听器       │
│ EnableAutoConfiguration│  │ApplicationContext    │  │Application      │
│                       │  │Initializer           │  │Listener         │
├──────────────────────┤  ├──────────────────────┤  ├─────────────────┤
│• RedisAutoConfig     │  │• ConfigWarnings      │  │• Logging        │
│• WebMvcAutoConfig    │  │• ContextId           │  │• FileEncoding   │
│• DataSourceAutoConfig│  │• Delegating          │  │• ClearCaches    │
│• JpaAutoConfig       │  │                      │  │                 │
│  (130+ 个)           │  │                      │  │                 │
└──────────────────────┘  └──────────────────────┘  └─────────────────┘

┌──────────────────────┐  ┌──────────────────────┐  ┌─────────────────┐
│ 环境后置处理器         │  │ 配置文件加载器         │  │ 失败分析器       │
│EnvironmentPost       │  │PropertySource        │  │Failure          │
│Processor             │  │Loader                │  │Analyzer         │
├──────────────────────┤  ├──────────────────────┤  ├─────────────────┤
│• CloudFoundryVcap    │  │• PropertiesLoader    │  │• BeanCreation   │
│• JsonEnvironment     │  │• YamlLoader          │  │• BeanNotFound   │
│• SystemEnvironment   │  │                      │  │• CircularRef    │
└──────────────────────┘  └──────────────────────┘  └─────────────────┘

                        ↓
┌────────────────────────────────────────────────────────────────┐
│                   最终结果层                                      │
└────────────────────────────────────────────────────────────────┘

    • 容器初始化完成
    • 自动配置生效
    • Bean创建完成
    • 应用启动成功

时序调用关系

text 复制代码
时间线 (Spring Boot启动过程)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

T1: new SpringApplication()
    │
    ├─→ SpringFactoriesLoader.loadFactories(ApplicationContextInitializer.class)
    │   └─→ 加载: ConfigurationWarnings, ContextId, Delegating...
    │
    └─→ SpringFactoriesLoader.loadFactories(ApplicationListener.class)
        └─→ 加载: LoggingListener, FileEncodingListener...

T2: prepareEnvironment()
    │
    └─→ EnvironmentPostProcessorApplicationListener
        └─→ SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class)
            └─→ 加载: CloudFoundryVcap, JsonEnvironment...

T3: prepareContext()
    │
    └─→ 执行所有ApplicationContextInitializer

T4: refreshContext()
    │
    └─→ @EnableAutoConfiguration触发
        └─→ AutoConfigurationImportSelector.getCandidateConfigurations()
            └─→ SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class)
                └─→ 加载: RedisAutoConfiguration, WebMvcAutoConfiguration...
                    │
                    ├─→ 条件判断(@ConditionalOnClass, @ConditionalOnBean...)
                    ├─→ 创建Bean
                    └─→ 配置属性

T5: 启动失败(如果有)
    │
    └─→ FailureAnalyzers
        └─→ SpringFactoriesLoader.loadFactories(FailureAnalyzer.class)
            └─→ 分析失败原因并输出友好错误信息

Spring SPI vs Java SPI对比总结

核心区别表

对比项 Java SPI Spring SPI
配置文件 META-INF/services/接口名 META-INF/spring.factories
文件数量 每个接口一个文件 统一一个文件
文件格式 纯文本(每行一个类名) Properties(key=value)
加载类 ServiceLoader SpringFactoriesLoader
获取方式 只能获取实例 可获取类名或实例
缓存机制 简单HashMap ConcurrentReferenceHashMap(弱引用)
实例化时机 遍历时立即实例化 可延迟实例化
排序支持 ✅ 支持@Order排序
条件过滤 ✅ 配合@Conditional使用

代码对比

场景: 获取所有自动配置类
Java SPI方式:

java 复制代码
// 1. 必须实例化才能获取
ServiceLoader<AutoConfiguration> loader =
    ServiceLoader.load(AutoConfiguration.class);

List<String> classNames = new ArrayList<>();
for (AutoConfiguration config : loader) {
    // ❌ 问题: 为了获取类名,所有实现都被实例化了
    classNames.add(config.getClass().getName());
}

// 2. 无法直接获取类名列表
// 3. 无法按需实例化

Spring SPI方式:

java 复制代码
// 1. 只获取类名,不实例化
List<String> classNames = SpringFactoriesLoader.loadFactoryNames(
    EnableAutoConfiguration.class,
    getClass().getClassLoader()
);

// 2. 可以进行条件过滤
List<String> filtered = classNames.stream()
    .filter(name -> name.contains("Redis"))
    .collect(Collectors.toList());

// 3. 按需实例化
for (String className : filtered) {
    Class<?> clazz = Class.forName(className);
    Object instance = clazz.newInstance();
}

// 或者直接获取实例(会自动排序)
List<AutoConfiguration> instances = SpringFactoriesLoader.loadFactories(
    AutoConfiguration.class,
    getClass().getClassLoader()
);

相关推荐
Code小翊3 小时前
C语言bsearch的使用
java·c语言·前端
yong99903 小时前
C#驱动斑马打印机实现包装自动打印
java·数据库·c#
好记忆不如烂笔头abc3 小时前
linux系统记录登录用户的所有操作
java·linux·服务器
用户68545375977693 小时前
🎯 Class文件结构大揭秘:打开Java的"身份证" 🪪
后端
sp423 小时前
一套清晰、简洁的 Java AES/DES/RSA 加密解密 API
java·后端
用户68545375977693 小时前
💥 栈溢出 VS 内存溢出:别再傻傻分不清楚!
后端
王嘉祥3 小时前
Pangolin:基于零信任理念的反向代理
后端·架构
Yimin3 小时前
2. 这才是你要看的 网络I/O模型
后端
野犬寒鸦3 小时前
从零起步学习MySQL || 第五章:select语句的执行过程是怎么样的?(结合源码深度解析)
java·服务器·数据库·后端·mysql·adb