Java实现状态机案例

wen java案例 6

📚 目录导读

  1. 【什么是状态机?为什么业务系统离不开它?】
  2. 【Java实现状态机的四种主流方案对比(含代码案例)】
    • 方案A:枚举 + 状态流转表(最轻量)
    • 方案B:状态模式(GOF经典,适合复杂行为)
    • 方案C:Spring StateMachine框架(企业级首选)
    • 方案D:规则引擎(Drools/ Easy Rules,适合超多变体)
  3. 【实战案例:电商订单状态机(待支付→已支付→已发货→已完成/已取消)完整代码】
  4. 【高频问答:状态机与工作流引擎的区别?并发幂等如何保证?】
  5. 【SEO关键词策略:java状态机最佳实践】

为什么你的业务系统必须使用状态机?

在真实的业务开发中,订单、工单、审批、设备运维等场景都充斥着状态流转,如果仅用if/else判断当前状态+事件,代码会迅速腐化为“意大利面条式”逻辑。

Java实现状态机案例

if (order.getStatus() == 1 && event == 5) { ... }
else if (order.getStatus() == 1 && event == 6) { ... }
// 每增加一个状态或事件,就要改这坨代码

状态机(State Machine) 的核心价值在于:

  • 清晰定义:状态、事件、动作、迁移规则被显式建模
  • 杜绝非法流转:如“已发货”订单不能直接跳转到“已完成”以外的状态(若要修改地址则需走特殊事件)
  • 可测试性:每个迁移是独立的纯函数,易于单元测试

Java实现状态机的四大方案(含代码实战)

方案A:枚举 + 二维Map(最简单,适合规则固定场景)

这是面试中最常问到的实现方式,核心思想:状态×事件 → 目标状态 的映射表。

public enum OrderStatus {
    PENDING_PAYMENT, PAID, SHIPPED, COMPLETED, CANCELLED;
}
public enum OrderEvent {
    PAY, SHIP, COMPLETE, CANCEL;
}
public class OrderStateMachine {
    // 状态迁移表:key = "当前状态+事件",value = 目标状态
    private static final Map<String, OrderStatus> TRANSITIONS = new HashMap<>();
    static {
        TRANSITIONS.put("PENDING_PAYMENT+PAY", OrderStatus.PAID);
        TRANSITIONS.put("PAID+SHIP", OrderStatus.SHIPPED);
        TRANSITIONS.put("SHIPPED+COMPLETE", OrderStatus.COMPLETED);
        TRANSITIONS.put("PENDING_PAYMENT+CANCEL", OrderStatus.CANCELLED);
        TRANSITIONS.put("PAID+CANCEL", OrderStatus.CANCELLED); // 退款场景
    }
    public static OrderStatus getNextStatus(OrderStatus current, OrderEvent event) {
        String key = current.name() + "+" + event.name();
        OrderStatus next = TRANSITIONS.get(key);
        if (next == null) {
            throw new IllegalStateException("非法状态迁移: " + key);
        }
        return next;
    }
}

优点:无框架依赖,性能极高(HashMap查找O(1))。 缺点:无法附加复杂业务动作(如发送短信)。


方案B:状态模式(GOF) —— 每个状态一个类

同一事件在不同状态下有不同的行为逻辑时(如“取消订单”在“待支付”直接取消,在“已支付”需走退款流程),推荐状态模式。

public interface OrderState {
    void pay(OrderContext ctx);
    void ship(OrderContext ctx);
    void cancel(OrderContext ctx);
}
public class PendingPaymentState implements OrderState {
    @Override
    public void pay(OrderContext ctx) {
        System.out.println("扣款成功");
        ctx.setState(new PaidState());
    }
    @Override
    public void cancel(OrderContext ctx) {
        ctx.setState(new CancelledState());
    }
}
public class PaidState implements OrderState {
    @Override
    public void cancel(OrderContext ctx) {
        System.out.println("发起退款流程");
        ctx.setState(new RefundingState());
    }
}

优点:符合开闭原则,行为内聚。 缺点:状态类数量爆炸,需配合上下文状态切换。


方案C:Spring StateMachine(企业级分布式首选)

适用场景:需要持久化状态、监听事件、乐观锁控制并发、与Spring Cloud集成。

@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderEvent> {
    @Override
    public void configure(StateMachineStateConfigurer<OrderStatus, OrderEvent> states) throws Exception {
        states.withStates()
            .initial(OrderStatus.PENDING_PAYMENT)
            .states(EnumSet.allOf(OrderStatus.class));
    }
    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvent> transitions) throws Exception {
        transitions
            .withExternal()
                .source(OrderStatus.PENDING_PAYMENT).target(OrderStatus.PAID)
                .event(OrderEvent.PAY)
                .action(context -> { 
                    System.out.println("执行支付后置动作:发送站内信");
                })
            .and()
            .withExternal()
                .source(OrderStatus.PAID).target(OrderStatus.SHIPPED)
                .event(OrderEvent.SHIP);
    }
}

关键点:必须配合数据库版本号(乐观锁)防止并发重复支付。


方案D:规则引擎(Easy Rules)

适合状态迁移规则会频繁变化(如运营配置不同等级会员的不同流转),将规则外置到数据库。


实战案例:电商订单完整状态机(含并发控制)

背景:订单有待支付→已支付→已发货→已完成,可取消(待支付/已支付),要求:支付回调接口可能重复调用。

核心设计

  1. 使用AtomicReference + CAS 避免并发重复流转
  2. 将状态更新SQL带上where status = 期望值
@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Transactional
    public boolean payOrder(Long orderId, OrderEvent event) {
        Order order = orderMapper.selectByIdForUpdate(orderId); // 悲观锁
        if (order == null) return false;
        // 使用状态机校验
        OrderStatus next = OrderStateMachine.getNextStatus(order.getStatus(), event);
        // 乐观锁更新:仅当状态匹配时更新
        int rows = orderMapper.compareAndSetStatus(orderId, order.getStatus(), next);
        return rows == 1; // 若rows=0说明被并发修改,本次调用忽略(幂等)
    }
}

幂等保障:支付回调若重复,第二次执行时状态已变为PAID,查询状态为PAID + PAY事件会抛出异常,捕获后返回“已处理”。


高频问答(FAQ)

Q1:状态机和JBPM/Activiti工作流引擎有什么区别?

  • 状态机:关注“一个对象”的状态变迁,是轻量级的(如订单维度)
  • 工作流引擎:关注“多角色、多任务”的流转,支持会签、驳回、办理人分配(如审批流),如果只是订单状态流转,硬上工作流引擎属于过度设计。

Q2:Spring StateMachine 如何持久化状态? 使用StateMachinePersister,将状态机状态存到数据库state_machine表中,通过RedisDB存储当前状态,每次事件触发时,从持久层恢复状态机。

Q3:状态机能达到多少QPS?

  • 枚举方案:纯内存运算,单机10万+ QPS
  • Spring StateMachine:状态机实例创建有开销,建议使用Object Pool,单机1万+ QPS,若并发过高,可采用预校验+异步流转(事件投递MQ,消费者再执行状态迁移)。

总结与SEO关键词布局

本文覆盖了 java状态机案例状态机设计模式Spring StateMachine 示例 等核心长尾词,实际项目中推荐组合:枚举映射表(快速校验)+ 状态模式(处理复杂行为)+ 乐观锁(并发安全),切记:不要为了用框架而用框架,简单业务用HashMap就够了。

如果本文对你有帮助,欢迎收藏转发,下一个项目,试着用状态机重构你的if/else吧!

抱歉,评论功能暂时关闭!