关键在于利用java 9+的provides…with机制实现配置加载能力的可插拔:定义configloader接口作为服务契约,各适配模块(如nacos、apollo)通过module-info.java声明提供实现,主业务模块仅uses该接口并由serviceloader自动发现加载,配合外部配置驱动实现运行时解耦。

要让模块能自动识别并加载外部配置中心(比如 Nacos、Apollo)提供的变量,关键不是硬编码读取逻辑,而是利用 Java 9+ 的 provides … with 机制,把“配置加载能力”声明为一种可插拔的服务。这样,主模块不依赖具体配置中心实现,只面向接口编程,运行时由 JVM 自动发现并注入适配器。
定义统一的配置加载服务接口
在公共模块(如 com.example.config.spi)中声明服务契约:
package com.example.config.spi;
public interface ConfigLoader {
String getProperty(String key);
String getProperty(String key, String defaultValue);
void reload(); // 支持动态刷新
}
这个接口不绑定任何配置中心,只表达“我能加载配置”的能力。其他模块只需依赖它,无需知道背后是 Nacos 还是 Apollo。
在配置中心适配模块中提供实现
以 Nacos 适配模块为例(com.example.config.nacos),在 module-info.java 中声明服务实现:
module com.example.config.nacos {
requires com.example.config.spi;
requires com.alibaba.nacos.api;
provides com.example.config.spi.ConfigLoader
with com.example.config.nacos.NacosConfigLoader;
}
同时确保 NacosConfigLoader 类实现了 ConfigLoader 接口,并完成 Nacos 客户端初始化、监听变更、触发 reload() 等逻辑。JVM 启动时会扫描所有模块的 provides 声明,自动注册该实现。
主业务模块按需加载配置服务
业务模块(如 com.example.order)只需声明依赖和使用服务:
module com.example.order {
requires com.example.config.spi;
uses com.example.config.spi.ConfigLoader; // 声明我要用这个服务
}
在代码中通过标准服务加载机制获取实例:
ServiceLoader<configloader> loader = ServiceLoader.load(ConfigLoader.class);
ConfigLoader config = loader.findFirst().orElseThrow(); // 自动找到 Nacos 实现
String timeout = config.getProperty("order.timeout", "3000");</configloader>
如果后续切换成 Apollo,只需引入 com.example.config.apollo 模块(同样含 provides … with),无需修改业务代码——JVM 会自动选用新实现。
配合外部配置化实现运行时解耦
服务发现本身是静态的,但配置内容必须动态。因此需让 ConfigLoader 实现支持外部配置驱动:
- 在模块启动阶段(如监听
ApplicationReadyEvent)调用configLoader.reload() - 将配置中心地址、命名空间等参数通过
application.properties或环境变量注入,避免写死在module-info.java中 - 敏感配置(如 token)应通过配置中心加密传输,不在模块源码或 jar 包内暴露
这样,模块行为由接口定义,实现由服务提供,配置由外部注入——三层完全分离。










