恢复状态机后丢失StateMachineInterceptor的问题求助
问题
重启服务后,从持久化存储恢复的状态机丢失了首次创建时添加的StateMachineInterceptor。这个拦截器是状态转换后置处理的核心,不能用Listener替代(因为我的逻辑需要依赖stateMachineInstance实例)。
另外还有个疑惑:当前只有state_machine表有数据变更,其他初始化生成的表始终没有变化。
相关代码如下:
StateMachineConfig配置类
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<S,E>{ @Override public void configure(StateMachineConfigurationConfigurer<S, E> config) throws Exception { config .withPersistence() .runtimePersister(stateMachinePersister); config .withConfiguration() .autoStartup(false) .listener(listener); } }
StateMachineService配置类
@Configuration @RequiredArgsConstructor public class StateMachineServiceConfig { private final StateMachineFactory<S, E> factory; private final StateMachineRuntimePersister<S, E, String> stateMachinePersister; @Bean public StateMachineService<S, E> stateMachineService() { return new DefaultStateMachineService<>(factory, stateMachinePersister); } }
持久化器配置类
@Configuration public class StateMachinePersistenceConfiguration { @Bean public StateMachineRuntimePersister<S, E, String> stateMachinePersister( final JpaStateMachineRepository jpaStateMachineRepository) { return new JpaPersistingStateMachineInterceptor<>(jpaStateMachineRepository); } }
状态机创建与恢复类
@Service @RequiredArgsConstructor @Slf4j public class RelocationRequestsSSMService { private final StateMachineService<S, E> stateMachineService; private final MyInterceptor stateMachineInterceptor; public StateMachine<S, E> createStateMachine() { StateMachine<S, E> stateMachine = stateMachineService.acquireStateMachine(UUID.randomUuid()); stateMachine.getStateMachineAccessor().withRegion().addStateMachineInterceptor(stateMachineInterceptor); log.debug("Created ssm with id: " + stateMachine.getId()); return stateMachine; } private StateMachine<S, E> getStateMachineBySsmId(String ssmId) { log.debug("Request ssm from persister with id: " + ssmId); StateMachine<S, E> stateMachine = stateMachineService.acquireStateMachine(ssmId); log.debug("Get ssm from persister with id and uuid: " + stateMachine.getId() + " " + stateMachine.getUuid()); return stateMachine; } }
问题分析与解决办法
核心原因
Spring StateMachine的默认JPA持久化实现只会保存状态机的核心状态信息(比如当前状态、状态机ID等)和静态配置元数据,不会持久化运行时动态添加的StateMachineInterceptor——因为Interceptor属于附加的运行时逻辑,不属于状态机的可持久化状态范畴。
解决办法
1. 全局配置Interceptor(推荐)
把Interceptor直接配置到StateMachineConfig中,这样无论创建新状态机还是恢复旧状态机,都会自动加载这个Interceptor,不需要动态添加。修改StateMachineConfig的configure方法:
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<S,E>{ private final MyInterceptor stateMachineInterceptor; // 构造注入Interceptor public StateMachineConfig(MyInterceptor stateMachineInterceptor) { this.stateMachineInterceptor = stateMachineInterceptor; } @Override public void configure(StateMachineConfigurationConfigurer<S, E> config) throws Exception { config .withPersistence() .runtimePersister(stateMachinePersister); config .withConfiguration() .autoStartup(false) .listener(listener) // 全局添加Interceptor .stateMachineInterceptor(stateMachineInterceptor); } }
之后createStateMachine方法里就可以去掉动态添加Interceptor的代码了。
2. 恢复状态机时重新附加(适用于动态场景)
如果你的Interceptor需要针对不同状态机实例做个性化配置,无法全局统一,那就在恢复状态机的getStateMachineBySsmId方法里,重新给恢复后的状态机添加Interceptor:
private StateMachine<S, E> getStateMachineBySsmId(String ssmId) { log.debug("Request ssm from persister with id: " + ssmId); StateMachine<S, E> stateMachine = stateMachineService.acquireStateMachine(ssmId); // 恢复后重新添加Interceptor stateMachine.getStateMachineAccessor().withRegion().addStateMachineInterceptor(stateMachineInterceptor); log.debug("Get ssm from persister with id and uuid: " + stateMachine.getId() + " " + stateMachine.getUuid()); return stateMachine; }
关于其他表无变更的说明
Spring StateMachine的JPA持久化中,state_machine表用来存储每个状态机实例的运行时状态(比如当前状态、最后更新时间等),状态变更时会更新这张表。而其他表(比如state_machine_state、state_machine_transition)是用来存储状态机的静态结构元数据(比如定义的状态、转换规则等),只有在首次初始化状态机结构或者结构发生变更时才会更新。如果你的状态机结构是固定的,这些表没有变更是完全正常的。
内容的提问来源于stack exchange,提问作者Ytug Ilya

