求助:如何基于Spring State Machine与Papyrus设计正交状态机
解决Spring State Machine正交状态与主状态依赖的实现问题
嘿,我刚好处理过类似的Spring State Machine正交状态场景,给你一套完整的实现方案,从Papyrus建模到SSM代码配置都理清楚,帮你搞定这个问题:
1. 先在Papyrus里梳理正确的状态机结构
先把模型搭对,这是代码实现的基础:
- 根状态下划分两个正交区域:一个叫
SubSystems(包含A、B、C三个独立子状态机),另一个叫MainSystem(放主状态M) - 每个子系统(A/B/C)的状态流:初始状态 →
off,通过对应事件(比如TURN_ON_A)转换到on - 主状态M的状态流:初始 →
off,只有当A/B/C全为on时,才触发转换到on
2. Spring State Machine代码实现步骤
2.1 定义状态和事件枚举
先把所有状态、事件用枚举明确,避免拼写错误:
public enum States { // 子系统状态 A_OFF, A_ON, B_OFF, B_ON, C_OFF, C_ON, // 主状态 M_OFF, M_ON, // 正交状态组标识 SUB_SYSTEMS, MAIN_SYSTEM } public enum Events { TURN_ON_A, TURN_ON_B, TURN_ON_C }
2.2 配置状态机核心结构
在配置类里搭建正交状态结构,同时定义转换规则和守卫条件:
@Configuration @EnableStateMachine public class StateMachineConfig extends StateMachineConfigurerAdapter<States, Events> { @Override public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception { states // 配置主状态区域 .withStates() .parent(States.MAIN_SYSTEM) .initial(States.M_OFF) .state(States.M_ON) .and() // 配置子系统正交组:A、B、C各自独立 .withStates() .parent(States.SUB_SYSTEMS) .initial(States.A_OFF) .state(States.A_ON) .and() .withStates() .parent(States.SUB_SYSTEMS) .initial(States.B_OFF) .state(States.B_ON) .and() .withStates() .parent(States.SUB_SYSTEMS) .initial(States.C_OFF) .state(States.C_ON) .and() // 根状态绑定两个正交区域 .withStates().root().orthogonal(States.SUB_SYSTEMS, States.MAIN_SYSTEM); } @Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { // 子系统的off→on转换 transitions .withExternal() .source(States.A_OFF).target(States.A_ON).event(Events.TURN_ON_A) .and() .withExternal() .source(States.B_OFF).target(States.B_ON).event(Events.TURN_ON_B) .and() .withExternal() .source(States.C_OFF).target(States.C_ON).event(Events.TURN_ON_C) .and() // 主状态M的转换:用守卫判断所有子系统是否就绪 .withExternal() .source(States.M_OFF).target(States.M_ON) .guard(allSubsystemsOnGuard()); } // 自定义守卫:检查A、B、C是否都处于ON状态 @Bean public Guard<States, Events> allSubsystemsOnGuard() { return context -> { StateMachine<States, Events> stateMachine = context.getStateMachine(); // 正交状态机的当前状态是所有活跃子状态的集合,直接检查是否包含三个ON状态 return stateMachine.getState().getStates().containsAll(Arrays.asList(States.A_ON, States.B_ON, States.C_ON)); }; } }
2.3 触发事件与验证状态
在业务代码里触发事件,同时可以加监听器验证主状态转换:
@Autowired private StateMachine<States, Events> stateMachine; // 触发A系统开启 public void activateSystemA() { stateMachine.sendEvent(Events.TURN_ON_A); } // 同理实现activateSystemB、activateSystemC
添加状态监听器,方便调试:
@Component public class SystemStateListener extends StateMachineListenerAdapter<States, Events> { @Override public void stateChanged(State<States, Events> from, State<States, Events> to) { if (to.getStates().contains(States.M_ON)) { System.out.println("✅ 主状态M已切换至ON,所有子系统A/B/C均就绪!"); } } }
3. 常见坑点排查
- 主状态没触发转换?先检查守卫逻辑:正交状态机的
getState().getStates()会返回所有活跃子状态,确保你的枚举值和代码里的状态名完全一致 - Papyrus模型和代码要对齐:比如正交区域的划分、状态名称必须和代码枚举一一对应,避免模型和代码脱节
- 事件发送顺序不影响:不管先开哪个子系统,只要三个都到ON,SSM会在每次状态变更后自动检查守卫条件,触发M的转换
内容的提问来源于stack exchange,提问作者JD Williams
相关产品推荐
相关产品推荐

