provides 是 java 9+ jpms 中 module-info.java 的服务声明语法,用于发布服务接口实现以供 serviceloader 运行时发现,它不执行过滤逻辑,而是支撑模块化、可插拔过滤链的契约分发与动态加载。

“provides 语法”本身不是通用编程语言的标准特性,它特指 Java 9+ 的 JPMS(Java Platform Module System) 中 module-info.java 里的声明语句,用于导出服务接口及其实现。它不直接实现过滤链逻辑,但能为模块化、可插拔的过滤链系统提供契约分发与运行时发现能力——这是动态插拔的关键基础设施。
明确 provides 的定位:服务契约的发布者,不是过滤器执行者
在 JPMS 中,provides ... with ... 声明的是:本模块实现了某个服务接口,并愿意被其他模块通过 ServiceLoader 发现和使用。它本身不创建对象、不调用 handle()、不组装链——这些仍由业务代码完成。
例如,定义统一过滤契约:
// 在 shared-api 模块中public interface DataFilter {
String getName();
Map
}
一个具体过滤器模块(如 auth-filter)需在 module-info.java 中声明:
module auth.filter {
requires shared.api;
provides shared.api.DataFilter with auth.filter.JwtAuthFilter;
}
这样,主应用只需依赖 shared.api,就能在运行时自动发现所有已安装且声明了 provides 的过滤器实现。
构建可插拔过滤链:ServiceLoader + ChainBuilder 协同工作
主应用不硬编码 new JwtAuthFilter(),而是通过标准服务加载机制收集所有可用过滤器:
- 启动时调用
ServiceLoader.load(DataFilter.class),获取全部已注册实现(按模块路径加载顺序,非声明顺序) - 将返回的
Stream<datafilter></datafilter>传给链构建器,例如:FilterChain chain = ChainBuilder.fromServices(loaders).orderUsing(@Order or metadata).build(); - 支持条件启用:检查每个
DataFilter实例的getName()或自定义元数据(如filter.enabled=true),跳过未激活项 - 避免在
load()阶段触发初始化副作用——ServiceLoader 默认延迟实例化,符合“按需插拔”原则
动态插拔的关键:模块层(Layer)与运行时重载
仅靠 provides 不足以实现热插拔;必须配合 JPMS 的模块层管理:
- 新模块(如
sensitive-word-filter@2.0)以 JAR 形式下载后,用ModuleFinder.of(path)定位 - 通过
Configuration.resolveAndBind()解析其依赖并生成新Configuration - 调用
Layer.defineModulesWithOneLoader()将其加载到新类加载器,形成隔离的模块层 - 在该层中再次调用
ServiceLoader.load(DataFilter.class),即可发现新模块提供的过滤器 - 旧链可保留或替换:将新发现的过滤器注入现有
ChainBuilder,重建链实例,无需重启 JVM
实战注意事项:避免常见陷阱
-
provides必须与uses配合:主模块若要消费服务,需在module-info.java中声明uses shared.api.DataFilter;,否则ServiceLoader不会扫描该服务类型 - 模块名必须唯一,版本变更需更新
module-info.java中的module xxx@1.2语法,否则Layer.defineModules会拒绝重复模块 - 不要在
provides类中做重量级初始化(如连接数据库)——应在ServiceLoader返回实例后的首次apply()调用中懒加载 - 若需控制顺序,不依赖模块加载顺序,而是在实现类上加注解(如
@Order(10))或在provides后追加配置属性(通过Module.getDescriptor().provides()提取)










