Provider 扩展模型
受众:开发者 更新:2026-05-31 摘要:可替换能力的统一扩展模型。开发短信、存储、支付、翻译、AI 等 Provider 前先读本页。
项目可替换能力统一采用以下设计链路:
Facade -> Manager -> ConfigResolver -> Provider SPI这是完整形态;其中 Facade 和 Config Resolver 可选,真正必选的只有 Manager 和 Provider SPI(详见分层职责)。
适用场景:短信服务商、文件存储、支付渠道、翻译服务、AI 模型、内容安全、文档解析、搜索、通知通道等需要“同一业务能力,多种实现方式”的模块。
为什么统一
早期代码中同时出现过 Driver、Provider、Gateway、Disk、Store、Channel 等命名。它们有些来自外部生态,有些来自业务语义,长期会让开发者不知道扩展点在哪里。
后续项目自有可替换能力统一用 Provider:
| 旧口径 | 新口径 |
|---|---|
XxxDriver | XxxProvider |
XxxDriverNames | XxxProviderNames |
com.springopen.xxx.driver | com.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 只做薄转发,不写业务逻辑。
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 类型给业务模块。
标准文件结构
新增一个可替换能力时,优先按下面结构组织:
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。
因此新增代码应写:
SmsProviderConfig config = smsConfigResolver.provider(providerName, supplier);
smsProviderClient.send(providerName, config, message);不要再写:
SmsProviderConfig settings = smsConfigResolver.provider(providerName, supplier);
smsProviderClient.send(providerName, settings, message);约定:
provider/放可替换实现。support/放内部辅助类。exception/放模块异常。- 内置 provider 名称必须常量化。
- i18n key 必须放
XxxMessageKeys或共享MessageKeys。
配置约定
项目自有可替换能力优先使用:
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):
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 |
命名规则
| 类型 | 命名 | 必选 |
|---|---|---|
| SPI | XxxProvider | 是 |
| 实现类 | <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(配置快照)。
后续若再迁移或新增能力,按以下要点执行:
- 按能力模块拆成可验证小提交,避免一次性跨全仓难排查。
- 类型重命名会波及下游业务模块:先
rg全仓库引用确定完整文件集(含 modules / application / 测试 / 配置),一并提交,避免 starter 改了而消费端漏改导致编译不一致。 - 改完用
rg "Driver|driver|\\.driver|DriverNames|Settings"复核,剩余项应只限:JDBC / Spring Boot 标准字段、第三方库 API(如 jsoupoutputSettings)、历史数据库列、通用参数常量。
允许保留的 driver / settings 只限上述外部标准、第三方 API、历史数据库列或非 Provider 业务语义。