关键在于将格式能力作为可声明、可注入的服务管理,Java的ServiceLoader机制支持模块化注册MessageFormatter实现类,运行时按模板特征和locale自动路由,新增格式只需添加实现类和注册文件,无需修改调用方代码。
要实现针对不同消息格式变量的自动适配模块,关键不是堆砌逻辑分支,而是把“格式能力”本身作为可声明、可注入的服务来管理。java 的 provides 语法(配合 serviceloader)正好为此提供原生支持——它让格式处理器成为可插拔的模块化组件,而非硬编码在业务代码里。
定义统一的消息格式服务接口
先创建一个抽象能力契约,比如 MessageFormatter 接口:
- 声明方法
String format(String template, Object... args),屏蔽底层差异 - 增加
boolean supports(Locale locale, String formatHint)方法,用于运行时判断是否适配当前语言或模板特征(如含{0,date}或{1,number,currency}) - 接口保持无状态、无副作用,便于多实例并行调用
为每种格式体系编写独立实现并注册
每个实现类对应一种格式引擎,例如:
-
JavaMessageFormatAdapter:封装java.text.MessageFormat,处理{0,date}等语法 -
ICUMessageFormatAdapter:基于com.ibm.icu.text.MessageFormat,支持复数、选择等高级语义 -
SimplePlaceholderAdapter:轻量级字符串替换,仅处理{key}形式,适合前端传入的简单模板
在各自 JAR 的 META-INF/services/com.example.MessageFormatter 文件中写入对应全限定类名,完成 provides 声明。
运行时按需加载与自动路由
使用 ServiceLoader.load(MessageFormatter.class) 获取所有可用实现,再根据上下文智能匹配:
- 优先检查模板字符串是否含
{0,date}类型占位符 → 启用JavaMessageFormatAdapter - 若模板含
{num, plural, ...}→ 选用ICUMessageFormatAdapter - 若 locale 是
zh_CN且模板含货币符号 → 触发支持currency格式的实现 - 可加缓存层:对同一模板结构哈希后绑定首选 formatter,避免重复解析
模块化带来的实际好处
这种设计让新增格式支持变得极简:
- 加一个新实现类 + 注册文件,无需改任何已有调用方代码
- 测试可完全隔离:每个 adapter 单独验证格式行为,不依赖全局配置
- 部署灵活:生产环境可只打包需要的 formatter(如去掉 ICU 以减小包体积),
ServiceLoader自动跳过缺失实现










