Spring 状态机 - 如果任何操作有错误,如何导航到错误最终状态

Spring State Machine - How to navigate to an error final state if any action has an error

假设我们有 3 个状态 S1、S3、S3 和一个错误状态 E1。 S*状态有分别执行A1、A2、A3的动作。如果 A1、A2、A3 中的任何一个导致异常被抛出,状态机应该转到 E1。有没有一种方法可以定义一个通用转换,如果任何操作引发异常,则转到 E1?或者我是否必须明确说明每个状态的转换

states.withStates()
                .initial(START)
                .end(END)
                .end(ERROR)
                .state(S1, A1())
                .state(S2, A2())
                .state(S3, A3())



//Is this the only way of defining error transition? Can I dry it up?
    transitions
       .withExternal()
       .source(S1)
       .event(ErrorEvent)
       .target(E1)

     .and()

        .withExternal()
           .source(S2)
           .event(ErrorEvent)
           .target(E1)

    .and()

        .withExternal()
          .source(S3)
          .event(ErrorEvent)
          .target(E1)

我希望有一种方法可以解决这个问题。如果我有 10 个状态,那么这样做就太重复了。

目前还没有使用状态机构建器来执行此操作的通用方法。 根据定义,每个状态转换都应在配置中明确定义。

讨论这在 SM 世界中是否是正确的方法是另一个问题:

If any of A1, A2, A3 results in an exception being thrown, the state machine should go to E1.

SM 应该进入错误状态还是应该保持相同状态或恢复到之前的状态等等,这是值得商榷的。 我的理解是,当错误发生在任何其他状态时,有一个共同的错误状态可以转换到不是一个好方法。

这实际上取决于您需要如何处理错误。 例如,如果你想在调用代码上发出信号或将错误传播到 stateMachine 之外,你可以为此使用 StateContext 或 SM ExtendedContext。 您可以插入一个自定义标志(例如 "hasError")和异常本身,并将其公开给您的调用者代码,但这不是很优雅。应该有人在每次调用时检查该标志的存在并通知调用者代码(或者调用者代码本身应该检查它,这很丑陋)。

另一种方法是清除 stateContext 并保持相同状态,等待一些重试逻辑启动。 或者在您的 errorAction 中,您可以将 ErrorEvent 发送到您的 SM 并传递 SM 上下文中需要的任何信息,我相信这是您选择的方法。

无论哪种方式 - 所有转换都应出现在 StateMachineModel 中。 可以使用构建器方法创建此模型,或者您可以通过编程方式创建它(例如,迭代所有现有状态并仅定义 1 个到错误状态的额外转换会更容易)。

@Override
public StateMachineModel<String, String> build() {
    ConfigurationData<String, String> configurationData = new ConfigurationData<>();
    Collection<StateData<String, String>> stateData = new ArrayList<>();
    stateData.add(new StateData<String, String>("S1", true));
    stateData.add(new StateData<String, String>("S2"));
    stateData.add(new StateData<String, String>("S3"));
    stateData.add(new StateData<String, String>("ERROR_STATE"));
    StatesData<String, String> statesData = new StatesData<>(stateData);
    Collection<TransitionData<String, String>> transitionData = new ArrayList<>();
    transitionData.add(new TransitionData<String, String>("S1", "S2", "E1"));
    stateData.forEach(state -> {
        if (state.getState() != ERROR_STATE) {
            transitionData.add(new TransitionData<String, String>(state.getState(), "ERROR_STATE", "ERROR_EVENT"));
        }
    });
    TransitionsData<String, String> transitionsData = new TransitionsData<>(transitionData);
    StateMachineModel<String, String> stateMachineModel = new DefaultStateMachineModel<String, String>(configurationData,
            statesData, transitionsData);
    return stateMachineModel;
}