如何理解springboot2.0.6中META-INF/spring.factories通过系统加载类获取对应的class的全限定名称

发布时间:2021-09-28 09:49:55 作者:柒染
来源:亿速云 阅读:248
# 如何理解Spring Boot 2.0.6中META-INF/spring.factories通过系统加载类获取对应的class全限定名称

## 引言

在Spring Boot框架中,`META-INF/spring.factories`文件扮演着至关重要的角色。这个看似简单的配置文件,实际上是Spring Boot自动配置机制的核心组成部分之一。特别是在Spring Boot 2.0.6版本中,该文件的加载机制和实现方式有着独特的设计理念。本文将深入剖析`spring.factories`文件如何通过系统类加载器获取对应的class全限定名称,揭示这一过程背后的技术细节。

## 一、spring.factories文件概述

### 1.1 文件位置与格式

`META-INF/spring.factories`文件通常位于项目的`resources`目录下,最终会被打包到JAR文件的`META-INF`目录中。其基本格式如下:

```properties
# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration,\
com.example.AnotherAutoConfiguration

# Auto Configure Exclusions
org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter=\
com.example.ExcludeFilter

1.2 文件作用

该文件本质上是一个Java属性文件,用于声明各种Spring工厂扩展点。主要作用包括:

  1. 实现自动配置类的注册
  2. 定义各种Spring工厂接口的实现
  3. 提供扩展Spring Boot功能的标准化方式

二、Spring Boot 2.0.6中的加载机制

2.1 加载流程概览

Spring Boot 2.0.6中spring.factories的加载过程可以分为以下几个关键步骤:

  1. 资源定位:通过类加载器查找所有META-INF/spring.factories文件
  2. 资源加载:读取找到的所有文件内容
  3. 属性解析:将文件内容解析为Properties对象
  4. 类名提取:获取特定键对应的类名列表
  5. 类加载:通过反射实例化这些类

2.2 核心实现类

在Spring Boot 2.0.6中,这一过程主要由SpringFactoriesLoader类实现,该类位于org.springframework.core.io.support包中。

三、深入源码分析

3.1 加载入口

SpringFactoriesLoader的核心方法是:

public static List<String> loadFactoryNames(Class<?> factoryType, 
                                          ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoader)
        .getOrDefault(factoryTypeName, Collections.emptyList());
}

3.2 资源加载过程

loadSpringFactories方法的实现:

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    // 缓存检查
    Map<String, List<String>> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }
    
    result = new HashMap<>();
    try {
        // 关键步骤:获取所有资源
        Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                String[] factoryImplementationNames = 
                    StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                for (String factoryImplementationName : factoryImplementationNames) {
                    result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                        .add(factoryImplementationName.trim());
                }
            }
        }
        // 转换为不可修改的Map
        result.replaceAll((factoryType, implementations) -> 
            Collections.unmodifiableList(implementations));
        cache.put(classLoader, Collections.unmodifiableMap(result));
    } catch (IOException ex) {
        throw new IllegalArgumentException(...);
    }
    return result;
}

3.3 关键点解析

  1. 类加载器的作用

    • 使用传入的ClassLoader来查找资源
    • 支持多模块/多JAR文件的资源合并
  2. 资源定位

    • ClassLoader.getResources()方法会搜索类路径下的所有匹配资源
    • 这意味着来自不同JAR的spring.factories文件会被合并
  3. 属性解析

    • 使用Spring的PropertiesLoaderUtils进行属性加载
    • 支持属性值的多行定义(通过反斜杠连接)

四、类加载机制详解

4.1 类加载器层次结构

Spring Boot 2.0.6在加载spring.factories时使用的类加载器遵循以下顺序:

  1. 线程上下文类加载器(Thread.currentThread().getContextClassLoader())
  2. 当前类的类加载器(SpringFactoriesLoader.class.getClassLoader())
  3. 系统类加载器(ClassLoader.getSystemClassLoader())

4.2 类加载的实际过程

当获取到全限定类名后,实际的类加载是通过Class.forName()完成的:

private static <T> T instantiateFactory(String instanceClassName, 
                                      Class<T> factoryType, 
                                      ClassLoader classLoader) {
    try {
        Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
        if (!factoryType.isAssignableFrom(instanceClass)) {
            throw new IllegalArgumentException(...);
        }
        return (T) ReflectionUtils.accessibleConstructor(instanceClass).newInstance();
    } catch (Throwable ex) {
        throw new IllegalArgumentException(...);
    }
}

五、Spring Boot 2.0.6的特定实现

5.1 与早期版本的区别

在Spring Boot 2.0.6中,对spring.factories的加载机制做了以下优化:

  1. 缓存机制:增加了ConcurrentReferenceHashMap缓存,提高性能
  2. 并行加载:支持并行加载多个spring.factories文件
  3. 错误处理:改进了错误处理机制,提供更详细的错误信息

5.2 自动配置的特殊处理

对于自动配置类(EnableAutoConfiguration),Spring Boot 2.0.6增加了额外的过滤逻辑:

List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
configurations = sort(configurations, autoConfigurationMetadata);
configurations = filter(configurations, autoConfigurationMetadata);

六、实际应用案例分析

6.1 自定义Starter中的使用

假设我们要创建一个自定义Starter,典型的spring.factories配置如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.mystarter.MyAutoConfiguration,\
com.example.mystarter.AdditionalAutoConfiguration

org.springframework.context.ApplicationListener=\
com.example.mystarter.MyApplicationEventListener

6.2 加载过程追踪

  1. Spring Boot应用启动时,SpringApplication初始化
  2. 调用SpringFactoriesLoader.loadFactoryNames()加载所有自动配置类
  3. 合并所有JAR中的spring.factories定义
  4. 根据条件注解过滤有效的自动配置类
  5. 实例化并通过@Bean方法创建相关bean

七、常见问题与解决方案

7.1 加载失败问题排查

问题现象:配置了spring.factories但自动配置类未生效

排查步骤

  1. 确认文件路径正确:META-INF/spring.factories
  2. 检查文件编码:必须是UTF-8
  3. 验证JAR包中是否包含该文件
  4. 检查类路径是否正确

7.2 类加载冲突

当出现ClassNotFoundExceptionNoClassDefFoundError时:

  1. 确认依赖版本一致
  2. 检查类加载器层次
  3. 使用-verbose:class参数查看类加载过程

八、性能优化建议

8.1 减少自动配置类数量

  1. 只声明必要的自动配置类
  2. 使用@Conditional注解精确控制加载条件

8.2 合理组织spring.factories

  1. 避免单个文件过大
  2. 按功能模块拆分定义

九、总结与展望

Spring Boot 2.0.6中META-INF/spring.factories的加载机制体现了以下设计理念:

  1. 约定优于配置:通过标准化文件位置和格式简化配置
  2. 可扩展性:支持多模块贡献配置
  3. 灵活性:通过类加载器机制适应各种部署环境

随着Spring Boot的发展,后续版本对这一机制进行了进一步优化,如:

但理解2.0.6版本的实现原理仍然是掌握Spring Boot自动配置机制的重要基础。

附录:相关源码片段

// SpringFactoriesLoader的部分关键实现
public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
    private static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();
    
    // ... 其他方法实现
}

参考文献

  1. Spring Boot 2.0.6官方文档
  2. SpringFactoriesLoader源码注释
  3. 《Spring Boot实战》相关章节
  4. Java类加载机制相关技术文章

”`

注:本文实际字数为约3000字,要达到5950字需要进一步扩展每个章节的详细内容,添加更多实现细节、示例代码和案例分析。由于篇幅限制,这里提供了主体框架和核心内容,您可以根据需要扩展以下部分:

  1. 增加更多源码分析细节
  2. 添加完整的实际项目示例
  3. 深入探讨类加载器机制
  4. 补充性能测试数据
  5. 增加与其他版本比较的详细表格
  6. 扩展故障排查章节的案例数量
推荐阅读:
  1. SpringBoot创建WebService服务 + C++调用WebService具体实现
  2. SpringBoot 教程之属性加载详解

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

springboot spring.factories

上一篇:如何实现python颜值打分小系统

下一篇:微信mac版本指的是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》