Hutool Hutool
(opens new window)
🏡首页
📖指南
🎢最佳实践
💖支持
💡javaDoc (opens new window)
⏳更新记录 (opens new window)
  • 🍎gitee (opens new window)
  • 🍏github (opens new window)
(opens new window)
🏡首页
📖指南
🎢最佳实践
💖支持
💡javaDoc (opens new window)
⏳更新记录 (opens new window)
  • 🍎gitee (opens new window)
  • 🍏github (opens new window)
  • 快速入门

  • 核心(hutool-core)

    • AI(Hutool-ai)

    • 注解(annotation)

    • 数组(array)

    • JavaBean(bean)

    • 缓存(cache)

    • 类加载器(classloader)

    • 编码解码(codec)

    • 集合(collection)

    • 比较器(comparator)

    • 压缩(compress)

    • 类型转换(convert)

    • 数据(data)

    • 日期时间(date)

    • 异常(exception)

    • 函数(func)

    • IO流(io)

    • 语言特性(lang)

    • Map(map)

    • 数字数学(math)

    • 网络(net)

    • 对象池(pool)

    • 反射(reflect)

    • 正则(regex)

    • 服务提供(spi)

      • 服务提供模块
      • SpiUtil
      • 服务提供(ServiceLoader)
        • 1. 架构设计
          • 1.1 核心接口
          • 1.2 工具类
          • 1.3 架构图
        • 2. 设计哲学
          • 2.1 灵活性
          • 2.2 高性能
          • 2.3 容错性
          • 2.4 易用性
        • 3. 核心实现
          • 3.1 列表形式服务加载器(ListServiceLoader)
          • 配置文件格式
          • 使用示例
          • 3.2 键值对形式服务加载器(MapServiceLoader)
          • 配置文件格式
          • 使用示例
          • 3.3 工具方法
          • 加载第一个可用服务
          • 加载服务列表
        • 4. 与 JDK ServiceLoader 对比
          • 4.1 增强特性对比
          • 4.2 性能对比
          • 4.3 使用场景对比
        • 5. 最佳实践
          • 5.1 服务接口设计
          • 5.2 配置文件管理
          • 5.3 服务加载策略
          • 5.4 示例代码
          • 定义服务接口
          • 实现服务接口
          • 配置服务实现
          • 加载和使用服务
        • 6. 注意事项
        • 7. 总结
    • 聚合操作(stream)

    • 字符串文本(text)

    • 并发和线程(thread)

    • 树结构(tree)

    • 工具集合(util)

    • XML操作(xml)

  • 定时任务(hutool-cron)

  • 加密(hutool-crypto)

  • 数据库(hutool-db)

  • 扩展(hutool-extra)

  • HTTP(hutool-http)

  • 日志(hutool-log)

  • 配置文件(hutool-setting)

  • JSON(hutool-json)

  • Office办公(hutool-poi)

  • 套接字(hutool-socket)

  • GUI(hutool-swing)

  • 指南
  • 核心(hutool-core)
  • 服务提供(spi)
Hutool
2026-02-09
目录

服务提供(ServiceLoader)

SPI(Service Provider Interface)是一种服务发现机制,它允许在运行时动态加载服务实现。Hutool 提供了一套增强的 SPI 实现,相比 JDK 原生的 ServiceLoader,提供了更加灵活、强大的服务加载能力。

# 1. 架构设计

Hutool 的 SPI 实现基于以下核心组件:

# 1.1 核心接口

  • ServiceLoader<S> - 服务加载器接口,定义了服务加载的基本方法
  • AbsServiceLoader<S> - 抽象服务加载器,提供基础属性和构造方法
  • ListServiceLoader<S> - 列表形式的服务加载器,类似 JDK ServiceLoader
  • MapServiceLoader<S> - 键值对形式的服务加载器,使用 Properties 格式

# 1.2 工具类

  • SpiUtil - SPI 相关工具方法,提供加载第一个可用服务等功能
  • JdkServiceLoaderUtil - 对 JDK ServiceLoader 的封装工具类
  • SpiException - SPI 相关异常类

# 1.3 架构图

flowchart TD
    subgraph 接口层
        ServiceLoader["ServiceLoader<S>\n服务加载器接口"]
    end

    subgraph 抽象层
        AbsServiceLoader["AbsServiceLoader<S>\n抽象服务加载器"]
    end

    subgraph 实现层
        ListServiceLoader["ListServiceLoader<S>\n列表形式服务加载器"]
        MapServiceLoader["MapServiceLoader<S>\n键值对形式服务加载器"]
    end

    subgraph 工具层
        SpiUtil["SpiUtil\nSPI工具类"]
        JdkServiceLoaderUtil["JdkServiceLoaderUtil\nJDK ServiceLoader封装"]
    end

    ServiceLoader --> AbsServiceLoader
    AbsServiceLoader --> ListServiceLoader
    AbsServiceLoader --> MapServiceLoader
    SpiUtil --> ServiceLoader
    JdkServiceLoaderUtil --> JDK["JDK ServiceLoader"]

# 2. 设计哲学

Hutool 的 SPI 实现体现了以下设计哲学:

# 2.1 灵活性

  • 多种加载方式:提供列表形式和键值对形式两种服务加载器
  • 可配置路径:支持自定义服务文件存储路径,默认路径分别为 META-INF/services/(列表形式)和 META-INF/hutool/(键值对形式)
  • 自定义类加载器:允许指定自定义的类加载器,适应不同的类加载场景
  • 编码支持:支持自定义编码,解决不同环境下的编码问题

# 2.2 高性能

  • 缓存机制:使用 SimpleCache 缓存服务实例,避免重复创建
  • 按需加载:支持按需加载指定服务,不需要一次性加载所有服务
  • 资源优先级:按照资源加载优先级,先加载的资源优先使用,后加载的同名资源丢弃

# 2.3 容错性

  • 错误处理:提供 loadFirstAvailable 方法,忽略错误的服务实现,加载第一个可用的服务
  • 异常封装:将底层异常封装为 SpiException,提供更加友好的错误信息

# 2.4 易用性

  • 简洁 API:提供静态工厂方法 of,简化服务加载器的创建
  • 链式调用:支持流畅的链式调用风格
  • 丰富的工具方法:提供多种工具方法,满足不同的服务加载需求

# 3. 核心实现

# 3.1 列表形式服务加载器(ListServiceLoader)

列表形式的服务加载器类似于 JDK 的 ServiceLoader,使用纯文本文件存储服务实现类名。

# 配置文件格式

服务文件默认位于 META-INF/services/ 目录下,文件名为服务接口的全限定类名。内容格式如下:

# 我是注释
hutool.service.Service1
hutool.service.Service2

# 使用示例

// 创建服务加载器
ListServiceLoader<MyService> loader = ListServiceLoader.of(MyService.class);

// 获取所有服务名称
List<String> serviceNames = loader.getServiceNames();

// 根据索引获取服务
MyService service = loader.getService(0);

// 遍历所有服务
for (MyService s : loader) {
    // 使用服务
}

# 3.2 键值对形式服务加载器(MapServiceLoader)

键值对形式的服务加载器使用 Properties 格式存储服务实现,支持通过名称获取服务。

# 配置文件格式

服务文件默认位于 META-INF/hutool/ 目录下,文件名为服务接口的全限定类名。内容格式如下:

# 我是注释
service1 = hutool.service.Service1
service2 = hutool.service.Service2

# 使用示例

// 创建服务加载器
MapServiceLoader<MyService> loader = MapServiceLoader.of(MyService.class);

// 根据名称获取服务
MyService service = loader.getService("service1");

// 遍历所有服务
for (MyService s : loader) {
    // 使用服务
}

# 3.3 工具方法

# 加载第一个可用服务

// 加载第一个可用服务
MyService service = SpiUtil.loadFirstAvailable(MyService.class);

# 加载服务列表

// 加载服务列表
ServiceLoader<MyService> loader = SpiUtil.loadList(MyService.class);

# 4. 与 JDK ServiceLoader 对比

Hutool 的 SPI 实现相比 JDK 原生的 ServiceLoader,提供了以下增强功能:

# 4.1 增强特性对比

特性 JDK ServiceLoader Hutool SPI
存储位置 固定为 META-INF/services/ 可自定义,默认为 META-INF/services/ 或 META-INF/hutool/
配置格式 仅支持类名列表 支持类名列表和键值对两种格式
编码支持 使用默认平台编码 可自定义编码
类加载器 使用系统类加载器 可自定义类加载器
加载机制 一次性加载所有服务 支持按需加载指定服务
缓存机制 无缓存,每次获取都创建新实例 提供缓存,重复获取返回相同实例
容错机制 遇到错误直接抛出异常 支持忽略错误实现,加载第一个可用服务
扩展性 扩展点有限 提供丰富的扩展点和工具方法

# 4.2 性能对比

场景 JDK ServiceLoader Hutool SPI
首次加载 较慢(加载所有服务) 较快(可按需加载)
重复获取 较慢(每次创建新实例) 较快(使用缓存)
内存占用 较高(存储所有服务实例) 较低(按需创建和缓存)

# 4.3 使用场景对比

场景 推荐使用 原因
简单服务加载 JDK ServiceLoader 原生支持,配置简单
复杂服务加载 Hutool SPI 功能丰富,灵活性高
需要按名称获取服务 Hutool SPI 支持键值对格式,可通过名称获取
需要自定义加载行为 Hutool SPI 提供多种自定义选项
对性能有较高要求 Hutool SPI 提供缓存机制,性能更好
需要容错能力 Hutool SPI 支持忽略错误实现

# 5. 最佳实践

# 5.1 服务接口设计

  1. 接口定义:创建清晰、职责单一的服务接口
  2. 实现类:提供多个可选的实现类,满足不同场景需求
  3. 文档说明:为服务接口和实现类提供详细的文档说明

# 5.2 配置文件管理

  1. 命名规范:配置文件名称必须与服务接口的全限定类名相同
  2. 格式规范:根据使用的加载器类型,选择合适的配置格式
  3. 版本管理:在配置文件中添加版本信息,便于维护
  4. 注释说明:添加必要的注释,说明配置项的用途

# 5.3 服务加载策略

  1. 按需加载:对于大型应用,建议使用按需加载策略,避免一次性加载所有服务
  2. 缓存使用:充分利用 Hutool SPI 的缓存机制,提高性能
  3. 容错处理:使用 loadFirstAvailable 方法,提高系统的稳定性
  4. 优先级管理:合理组织服务实现的顺序,确保优先级高的服务先被加载

# 5.4 示例代码

# 定义服务接口

public interface MessageService {
    String send(String message);
}

# 实现服务接口

public class EmailMessageService implements MessageService {
    @Override
    public String send(String message) {
        return "Email sent: " + message;
    }
}

public class SmsMessageService implements MessageService {
    @Override
    public String send(String message) {
        return "SMS sent: " + message;
    }
}

# 配置服务实现

使用 ListServiceLoader:

在 META-INF/services/ 目录下创建文件 com.example.MessageService,内容如下:

# 邮件消息服务
com.example.EmailMessageService
# SMS消息服务
com.example.SmsMessageService

使用 MapServiceLoader:

在 META-INF/hutool/ 目录下创建文件 com.example.MessageService,内容如下:

# 邮件消息服务
email = com.example.EmailMessageService
# SMS消息服务
sms = com.example.SmsMessageService

# 加载和使用服务

使用 ListServiceLoader:

// 创建服务加载器
ListServiceLoader<MessageService> loader = ListServiceLoader.of(MessageService.class);

// 获取第一个服务
MessageService service1 = loader.getService(0);
Console.log(service1.send("Hello World"));

// 获取第二个服务
MessageService service2 = loader.getService(1);
Console.log(service2.send("Hello World"));

// 遍历所有服务
for (MessageService service : loader) {
    Console.log(service.send("Hello World"));
}

使用 MapServiceLoader:

// 创建服务加载器
MapServiceLoader<MessageService> loader = MapServiceLoader.of(MessageService.class);

// 根据名称获取服务
MessageService emailService = loader.getService("email");
Console.log(emailService.send("Hello World"));

// 根据名称获取服务
MessageService smsService = loader.getService("sms");
Console.log(smsService.send("Hello World"));

// 遍历所有服务
for (MessageService service : loader) {
    Console.log(service.send("Hello World"));
}

使用工具方法:

// 加载第一个可用服务
MessageService service = SpiUtil.loadFirstAvailable(MessageService.class);
Console.log(service.send("Hello World"));

# 6. 注意事项

  1. 配置文件位置:确保配置文件放在正确的目录下,否则服务加载器无法找到
  2. 类名正确性:确保配置文件中的类名是完整的、正确的全限定类名
  3. 依赖管理:确保服务实现类的依赖都已正确添加,避免运行时出现 ClassNotFoundException
  4. 版本兼容性:注意不同版本的 Hutool SPI 实现可能存在差异,确保使用兼容的版本
  5. 性能考虑:对于大量服务的场景,建议使用按需加载策略,避免一次性加载所有服务
  6. 安全性:注意服务实现的安全性,避免加载恶意的服务实现

# 7. 总结

Hutool 的 SPI 实现是对 JDK 原生 ServiceLoader 的增强和扩展,提供了更加灵活、强大、高性能的服务加载能力。它体现了 Hutool 的设计哲学:简单、灵活、高效、易用。

通过 Hutool 的 SPI 实现,开发者可以更加方便地实现服务的动态加载和替换,提高系统的可扩展性和可维护性。无论是简单的服务加载场景,还是复杂的服务管理需求,Hutool SPI 都能提供合适的解决方案。

SpiUtil
聚合操作模块

← SpiUtil 聚合操作模块→

Theme by Vdoing | Copyright © 2025-2026 Hutool | Apache-2.0
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式