跳到内容

Provider 扩展模型 ​

受众:开发者 更新:2026-05-31 摘要:可替换能力的统一扩展模型。开发短信、存储、支付、翻译、AI 等 Provider 前先读本页。

项目可替换能力统一采用以下设计链路:

text
Facade -> Manager -> ConfigResolver -> Provider SPI

这是完整形态;其中 Facade 和 Config Resolver 可选,真正必选的只有 Manager 和 Provider SPI(详见分层职责)。

适用场景:短信服务商、文件存储、支付渠道、翻译服务、AI 模型、内容安全、文档解析、搜索、通知通道等需要“同一业务能力,多种实现方式”的模块。

为什么统一 ​

早期代码中同时出现过 Driver、Provider、Gateway、Disk、Store、Channel 等命名。它们有些来自外部生态,有些来自业务语义,长期会让开发者不知道扩展点在哪里。

后续项目自有可替换能力统一用 Provider:

旧口径新口径
XxxDriverXxxProvider
XxxDriverNamesXxxProviderNames
com.springopen.xxx.drivercom.springopen.xxx.provider
项目自有 driver: 配置provider: 或 default-provider / providers

外部标准字段不改。例如 spring.datasource.driver-class-name 是 JDBC / Spring Boot 标准配置,不属于本项目 Provider 迁移范围。

分层职责 ​

可替换能力的完整形状如下,但只有 Manager 和 Provider SPI 是必选,其余按能力需要取用。很多能力只有 Manager + Provider SPI,不需要独立 Config Resolver / ProviderConfig。

层级职责是否必选
Manager能力主入口:校验、模板 / 参数处理、provider 选择与 fallback 编排、审计、异常转换必选
Provider SPI(XxxProvider)可替换实现契约,隐藏第三方 SDK 和厂商差异必选
Facade(Xxx)静态短入口,给业务代码提供简洁调用方式可选(高频直发能力才提供)
Config Resolver(XxxConfigResolver)把 properties / 运行时配置 / 数据库配置解析为 provider 执行配置可选(需要配置解析或后台覆盖时)
Provider Client(XxxProviderClient)适配某个第三方生态(sms4j、IJPay 等),被多个 Provider 复用可选(基于成熟生态时)

provider 选择和 fallback 不在 Config Resolver,而是由 Manager 按「消息显式指定的 provider → default-provider → fallback-providers」依次决定。Config Resolver 只负责把配置解析成 XxxProviderConfig。个别能力如需按 region / scene 在代码侧选 provider,可在该模块内自行加路由 SPI,不作为通用标准(见各模块文档,如 sms.md)。

Facade ​

Facade 只做薄转发,不写业务逻辑。

java
Sms.send(phone, content);
Storage.put(path, bytes);
Payment.create(order);

Facade 适合高频调用能力。如果能力只在少数 Spring Bean 内部使用,可以只暴露 Manager。

Manager ​

Manager 是运行时 API 和业务编排中心。

Manager 负责:

  • 校验 command / context。
  • 调用 Config Resolver 获取 provider 执行配置,并自行完成 provider 选择。
  • 处理 fallback、审计、指标、异常转换。
  • 返回项目自有 Result / Response / VO,不返回第三方 SDK 类型。

Manager 不负责:

  • 直接拼厂商 SDK 参数。
  • 到处手写配置读取。
  • 把厂商异常直接抛给业务模块。

Config Resolver(可选) ​

需要把配置统一解析、或支持后台配置覆盖时才引入。它不决定用哪个 provider(那是 Manager 的职责;个别模块可在 Manager 内部接入模块级路由 SPI),只负责产出 provider 执行配置。

Config Resolver 负责:

  • 读取 typed properties、运行时配置、数据库配置或上下文覆盖参数。
  • 合并默认值、租户 / 场景 / locale / region 等信息。
  • 产出稳定的 XxxProviderConfig(一个能力可能有多个,如 XxxProviderConfig + 厂商专属 XxxSmtpProviderConfig)。

Provider 不直接读取 ConfigManager、数据库配置、环境变量或散落的 application.yml,这些都交给 Config Resolver。简单能力没有 Config Resolver 时,Provider 可直接读 typed properties。

Provider Client(可选) ​

当多个 Provider 复用同一第三方生态(如 sms4j、IJPay)时,抽出 XxxProviderClient 把生态调用集中在一处,Provider 实现只委托给它。业务和 Provider 都不直接 import 第三方 SDK。

Provider SPI ​

Provider 是真正可替换的执行契约。

Provider 负责:

  • 调用第三方 SDK 或本地实现。
  • 把厂商响应转换为项目自有结果。
  • 隐藏 SDK 类型、认证细节和错误码差异。

Provider 不负责:

  • 决定自己是否启用。
  • 读取数据库配置。
  • 处理业务权限。
  • 暴露厂商 SDK 类型给业务模块。

标准文件结构 ​

新增一个可替换能力时,优先按下面结构组织:

text
com.springopen.<capability>/
├── XxxManager.java                  # 能力主入口(必选)
├── XxxProvider.java                 # Provider SPI(必选)
├── XxxProviderNames.java            # 内置 provider 名称常量
├── XxxProperties.java               # @ConfigurationProperties(绑定配置,必选)
├── XxxAutoConfiguration.java        # 自动配置(必选)
├── Xxx.java                         # Facade(可选)
├── XxxConfigResolver.java           # 配置解析(可选,需要后台覆盖 / 复杂解析时)
├── XxxRuntimeConfigSource.java      # 运行时配置来源(可选)
├── provider/                        # 可替换实现
│   ├── LogXxxProvider.java
│   ├── MemoryXxxProvider.java
│   └── TencentXxxProvider.java
└── support/                         # 内部辅助:XxxProviderConfig、XxxProviderClient 等

XxxProviderConfig 是 provider 执行配置(resolver 产出的快照),不强制只有一个:一个能力可能有多个,也可能带厂商限定词(如 XxxSmtpProviderConfig、XxxWebhookProviderConfig),少数能力没有独立 config(Provider 直接读 properties)。它和 XxxProperties(@ConfigurationProperties 绑定类)是两回事,不要混用。

变量命名 ​

Provider 相关变量统一按“解析程度”命名:

  • XxxProperties properties:Spring Boot 绑定配置,通常来自 application.yml。
  • XxxProviderConfig config:XxxConfigResolver 产出的、可直接交给 Provider / ProviderClient 执行的配置快照。
  • XxxConfigResolver configResolver:配置解析器依赖,不使用 settingsResolver。
  • 厂商或子配置与局部 config 冲突时,用更具体的名字,例如 alipayConfig、wechatConfig、smtpConfig、s3Config。
  • settings 只保留给外部标准、第三方 SDK 原生命名、历史数据库列或非 Provider 业务语义;项目自有 Provider 执行配置不再叫 settings。

因此新增代码应写:

java
SmsProviderConfig config = smsConfigResolver.provider(providerName, supplier);
smsProviderClient.send(providerName, config, message);

不要再写:

java
SmsProviderConfig settings = smsConfigResolver.provider(providerName, supplier);
smsProviderClient.send(providerName, settings, message);

约定:

  • provider/ 放可替换实现。
  • support/ 放内部辅助类。
  • exception/ 放模块异常。
  • 内置 provider 名称必须常量化。
  • i18n key 必须放 XxxMessageKeys 或共享 MessageKeys。

配置约定 ​

项目自有可替换能力优先使用:

yaml
spring:
  open:
    sms:
      default-provider: log
      fallback-providers:
        - memory
      providers:
        log:
          enabled: true
        tencent:
          enabled: false
          access-key-id: ${SPRING_OPEN_SMS_TENCENT_SECRET_ID:}
          access-key-secret: ${SPRING_OPEN_SMS_TENCENT_SECRET_KEY:}
          sdk-app-id: ${SPRING_OPEN_SMS_TENCENT_SDK_APP_ID:}

规则:

  • default-provider 表示默认实现。
  • providers.<name> 表示各 provider 独立配置。
  • fallback-providers 表示主 provider 失败后的降级顺序。
  • 第三方密钥不写死默认值。
  • 业务展示名、描述、Logo、可用环境等由业务元数据或接口返回,不把 provider 名直接展示给用户。

调用示例 ​

以短信为例(真实 API):

text
Sms.send(message)                      // Facade 薄转发;也可 Sms.send(phone, content)
  -> SmsManager.send(message)          // 校验 + 模板渲染 + 选 provider + fallback + 审计
       选 provider:message.provider → default-provider → fallback-providers
  -> SmsProvider.send(message)         // 命中的 provider 实现执行(log / memory / 腾讯云…)
       厂商 provider 内部:Config Resolver 解析 SmsProviderConfig → SmsProviderClient 调 sms4j

示例含义:

步骤说明
Sms.send(message)业务侧短入口,薄转发到 Manager
SmsManager.send(message)校验、渲染、选 provider、按 fallback 链重试、审计、异常转换
SmsProvider.send(message)命中的实现执行;厂商实现内部用 Config Resolver 解析配置、SmsProviderClient 调底层 SDK

命名规则 ​

类型命名必选
SPIXxxProvider是
实现类<Vendor>XxxProvider 或 <Scene>XxxProvider(如 TencentSmsProvider)是
常量类XxxProviderNames是
绑定配置XxxProperties(@ConfigurationProperties)是
包名com.springopen.<capability>.provider(实现放这里)是
配置集合providers是
配置解析器XxxConfigResolver可选
生态适配XxxProviderClient可选
执行配置XxxProviderConfig(可多个 / 带厂商限定词,如 XxxSmtpProviderConfig)可选

不再新增项目自有 XxxDriver、driver 包或 XxxDriverNames。外部标准字段(如 spring.datasource.driver-class-name)和第三方库 API 不在此约束内。

与业务模块的边界 ​

业务模块只依赖 Facade / Manager / 项目自有 DTO。

业务模块不允许:

  • import 第三方 SDK。
  • import 某个具体 Provider 实现类。
  • 读取 Provider 密钥配置。
  • 把 Provider 名称作为用户可见支付方式、短信服务商文案或菜单文案。

如果业务需要展示可选项,应由后端返回业务元数据,例如:

  • 支付方式名称、Logo、描述、支持环境。
  • 存储空间名称和可见用途。
  • AI 模型名称、价格、上下文长度。
  • 短信通道运营名称和适用国家。

迁移规则 ​

存量 starter(sms / mail / storage / payment / scheduler / web3 / notification / cache / rate-limit 等)的 Driver → Provider 与 Settings → Config 命名迁移已完成,无旧术语残留,也未做兼容包装。新代码直接按本页规范实现,不再新增 XxxDriver / driver 包 / XxxDriverNames / XxxSettings(配置快照)。

后续若再迁移或新增能力,按以下要点执行:

  1. 按能力模块拆成可验证小提交,避免一次性跨全仓难排查。
  2. 类型重命名会波及下游业务模块:先 rg 全仓库引用确定完整文件集(含 modules / application / 测试 / 配置),一并提交,避免 starter 改了而消费端漏改导致编译不一致。
  3. 改完用 rg "Driver|driver|\\.driver|DriverNames|Settings" 复核,剩余项应只限:JDBC / Spring Boot 标准字段、第三方库 API(如 jsoup outputSettings)、非 Provider 业务语义或通用参数常量。

允许保留的 driver / settings 只限上述外部标准、第三方 API 或非 Provider 业务语义;项目自有持久化字段不得继续使用旧 Provider 术语。

Released under the Apache License 2.0.