在jpms下用provides实现spi状态转换,关键是将状态迁移行为抽象为可插拔服务:定义state枚举和statetransitionrule接口,api模块导出契约,各策略模块通过provides声明实现,主模块用serviceloader动态加载并选择规则执行。

在 JPMS 下用 provides 实现基于 SPI 的变量状态转换逻辑,关键不是“定义状态”,而是把“状态转换行为”抽象为可插拔的服务。它要求你将状态迁移规则封装成接口,由不同模块按需提供具体策略,主模块通过 ServiceLoader 动态选取并执行。
定义统一的状态转换契约(API 模块)
先创建一个独立的 API 模块(如 app.state),只暴露接口和基础类型,不包含任何实现:
- 定义状态枚举:
enum State { INIT, VALIDATING, ACTIVE, SUSPENDED, ARCHIVED } - 定义转换接口:
public interface StateTransitionRule { boolean canTransition(State from, State to); String getReason(); } - 在
module-info.java中无限制导出:exports app.state;(不可用exports to,否则第三方无法实现)
编写多个策略实现模块(SPI Provider)
每个实现模块代表一类业务规则,例如:
-
rule.strict:仅允许线性推进(INIT → VALIDATING → ACTIVE),禁止回退 -
rule.flexible:支持任意非循环跳转,但需满足权限检查 -
rule.audit:每次转换都记录审计上下文,返回带 traceId 的原因
每个模块的 module-info.java 需声明:
module rule.strict {
requires app.state;
provides app.state.StateTransitionRule with rule.strict.StrictRule;
}
对应实现类必须有 public 无参构造器,并实现接口。
主模块中加载并使用转换规则
主模块(如 app)不硬编码任何规则,只依赖契约并声明消费点:
-
module-info.java中写:requires app.state; uses app.state.StateTransitionRule; - 运行时用
ServiceLoader.load(StateTransitionRule.class)获取全部可用规则 - 根据上下文选择策略:比如按配置名匹配、按优先级排序、或组合多个规则(如先校验再审计)
示例调用逻辑:
State current = State.INIT;
State target = State.ACTIVE;
Optional<statetransitionrule> selected = ServiceLoader
.load(StateTransitionRule.class)
.stream()
.map(Provider::get)
.filter(r -> r.canTransition(current, target))
.findFirst();
if (selected.isPresent()) {
System.out.println("Allowed: " + selected.get().getReason());
} else {
throw new IllegalStateException("Transition not permitted");
}</statetransitionrule>
注意事项与常见陷阱
不要在 API 模块里写 default 方法——这会削弱策略的正交性;不要让实现模块相互依赖——它们应只依赖 app.state;避免在 provides 后写多个类,每个 provides 行只绑定一个具体实现类;若需多实例(如不同租户用不同规则),应在实现类内部处理参数化,而非靠多个 provides 声明。










