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;
}
假设我们有 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;
}