Java中用枚举实现业务状态机流转校验的核心是将合法状态及允许转移路径内聚于枚举,通过集合字段定义入边/出边、封装带校验的状态变更服务、支持谓词函数扩展多条件判断,并可结合Spring声明式验证。

Java 中用枚举实现业务状态机的流转校验,核心是把「合法状态」和「允许的转移路径」都定义在枚举内部,借助枚举的类型安全和单例特性,让状态变更逻辑集中、可读、不可绕过。
定义状态枚举并内聚转移规则
不要只把枚举当常量容器。每个枚举实例应明确自己能从哪些状态“来”,能转移到哪些状态“去”。常用方式是在枚举中定义集合字段(如 Set
public enum OrderStatus {
CREATED(Set.of()),
PAID(Set.of(CREATED)),
SHIPPED(Set.of(PAID)),
DELIVERED(Set.of(SHIPPED)),
CANCELLED(Set.of(CREATED, PAID));
private final Set<OrderStatus> allowedPrevStates;
OrderStatus(Set<OrderStatus> allowedPrevStates) {
this.allowedPrevStates = Collections.unmodifiableSet(allowedPrevStates);
}
public boolean canTransferFrom(OrderStatus from) {
return allowedPrevStates.contains(from);
}
}
这样,PAID.canTransferFrom(CREATED) 返回 true,而 PAID.canTransferFrom(SHIPPED) 返回 false,校验逻辑天然绑定在状态本身上。
封装状态变更服务,强制走校验流程
避免直接调用 order.setStatus(newStatus) 这类裸操作。提供一个带校验的变更方法:
立即学习“Java免费学习笔记(深入)”;
- 接收当前状态、目标状态,先校验是否允许转移;
- 校验通过后更新状态,并可触发事件(如发消息、记日志);
- 抛出特定异常(如 IllegalStatusTransitionException),便于上层统一处理。
public class OrderService {
public void transitionStatus(Order order, OrderStatus target) {
OrderStatus current = order.getStatus();
if (!target.canTransferFrom(current)) {
throw new IllegalStatusTransitionException(
String.format("Cannot transition from %s to %s", current, target)
);
}
order.setStatus(target);
publishStatusChangedEvent(order, current, target);
}
}
支持多条件转移(可选进阶)
真实业务中,状态转移常依赖额外条件(如“已支付且库存充足才能发货”)。可在枚举中定义谓词函数,把校验逻辑外置但结构化:
- 枚举字段改为 BiPredicate
类型的校验器; - 每个状态在初始化时传入自己的校验逻辑;
- 调用 canTransferFrom(current, order, context) 时动态执行判断。
这种方式保持枚举主干简洁,又不牺牲灵活性,适合中大型状态机。
配合 Spring 等框架做自动校验(实用技巧)
如果项目用 Spring,可结合 @Valid 和自定义约束注解(如 @ValidStatusTransition),在 Controller 层或 DTO 入参时自动拦截非法转移请求。底层验证器仍调用枚举的 canTransferFrom 方法,做到声明式 + 面向对象的双重保障。


















