跳到主要内容

Recipes

DeepSeek V3 中英对照 Recipes

本章包含现有内置状态机配方的文档。

Spring Statemachine 是一个基础框架。也就是说,除了 Spring Framework 之外,它并没有太多更高级的功能或依赖。因此,正确地使用状态机可能会有些困难。为了帮助解决这个问题,我们创建了一组配方模块,这些模块针对常见的用例进行了处理。

什么是配方?状态机配方是一个解决常见用例的模块。本质上,状态机配方既是一个示例,也是我们尝试让您易于重用和扩展的模板。

备注

Recipes 是为 Spring Statemachine 项目做出外部贡献的一个很好的方式。如果你还没有准备好为框架核心本身做出贡献,一个自定义且通用的 recipe 是与其他用户分享功能的好方法。

持久化

persist 配方是一个简单的实用工具,它允许你使用一个单一的状态机实例来持久化和更新存储库中任意项目的状态。

该方案的主类是 PersistStateMachineHandler,它做出了三个假设:

  • 一个 StateMachine<String, String> 的实例需要与 PersistStateMachineHandler 一起使用。注意,状态和事件必须为 String 类型。

  • 需要将 PersistStateChangeListener 注册到处理程序上,以便对持久化请求做出反应。

  • handleEventWithState 方法用于编排状态更改。

你可以在 [statemachine-examples-persist] 找到一个展示如何使用此配方的示例。

任务

任务配方(tasks recipe)是一种用于运行使用状态机的 Runnable 实例的有向无环图(DAG,Directed Acrylic Graph)的概念。该配方是从 [statemachine-examples-tasks] 示例中引入的思想发展而来的。

下图展示了状态机的一般概念。在这个状态图中,TASKS 下的所有内容展示了单个任务如何执行的一般概念。由于这个方案允许你注册一个深度层次结构的任务 DAG(意味着真实的状态图将是一个深度嵌套的子状态和区域的集合),我们不需要更加精确。

例如,如果你只有两个已注册的任务,当 TASK_id 被替换为 TASK_1TASK_2 时(假设已注册任务的 ID 分别为 12),以下状态图将是正确的。

状态图9

执行一个 Runnable 可能会导致错误。特别是当涉及到复杂的任务 DAG 时,您需要有一种方法来处理任务执行错误,并且能够在不重新执行已经成功执行的任务的情况下继续执行。此外,如果能够自动处理一些执行错误,那将是非常好的。作为最后的回退方案,如果错误无法自动处理,状态机将进入一个状态,用户可以手动处理错误。

TasksHandler 包含一个构建器方法,用于配置一个处理器实例,并遵循简单的构建器模式。你可以使用这个构建器来注册 Runnable 任务和 TasksListener 实例,并定义 StateMachinePersist 钩子。

现在我们可以使用一个简单的 Runnable 来执行一个简单的睡眠操作,如下例所示:

private Runnable sleepRunnable() {
return new Runnable() {

@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
}
};
}
备注

前面的示例是本章所有示例的基础。

要执行多个 sleepRunnable 任务,你可以注册任务并从 TasksHandler 中执行 runTasks() 方法,如下例所示:

TasksHandler handler = TasksHandler.builder()
.task("1", sleepRunnable())
.task("2", sleepRunnable())
.task("3", sleepRunnable())
.build();

handler.runTasks();

要监听任务执行过程中发生的情况,你可以将一个 TasksListener 的实例注册到 TasksHandler 中。如果你不想实现完整的接口,本指南提供了一个适配器 TasksListenerAdapter。该监听器提供了多种钩子来监听任务执行事件。以下示例展示了 MyTasksListener 类的定义:

private class MyTasksListener extends TasksListenerAdapter {

@Override
public void onTasksStarted() {
}

@Override
public void onTasksContinue() {
}

@Override
public void onTaskPreExecute(Object id) {
}

@Override
public void onTaskPostExecute(Object id) {
}

@Override
public void onTaskFailed(Object id, Exception exception) {
}

@Override
public void onTaskSuccess(Object id) {
}

@Override
public void onTasksSuccess() {
}

@Override
public void onTasksError() {
}

@Override
public void onTasksAutomaticFix(TasksHandler handler, StateContext<String, String> context) {
}
}

你可以通过使用构建器来注册监听器,或者直接向 TasksHandler 注册它们,如下例所示:

MyTasksListener listener1 = new MyTasksListener();
MyTasksListener listener2 = new MyTasksListener();

TasksHandler handler = TasksHandler.builder()
.task("1", sleepRunnable())
.task("2", sleepRunnable())
.task("3", sleepRunnable())
.listener(listener1)
.build();

handler.addTasksListener(listener2);
handler.removeTasksListener(listener2);

handler.runTasks();

每个任务都需要有一个唯一的标识符,并且(可选地)一个任务可以被定义为一个子任务。实际上,这创建了一个任务的 DAG(有向无环图)。以下示例展示了如何创建一个深度嵌套的任务 DAG:

TasksHandler handler = TasksHandler.builder()
.task("1", sleepRunnable())
.task("1", "12", sleepRunnable())
.task("1", "13", sleepRunnable())
.task("2", sleepRunnable())
.task("2", "22", sleepRunnable())
.task("2", "23", sleepRunnable())
.task("3", sleepRunnable())
.task("3", "32", sleepRunnable())
.task("3", "33", sleepRunnable())
.build();

handler.runTasks();

当发生错误并且运行这些任务的状态机进入 ERROR 状态时,你可以调用 fixCurrentProblems 处理方法来重置状态机扩展状态变量中保存的任务的当前状态。然后,你可以使用 continueFromError 处理方法来指示状态机从 ERROR 状态转换回 READY 状态,在该状态下你可以再次运行任务。以下示例展示了如何执行此操作:

TasksHandler handler = TasksHandler.builder()
.task("1", sleepRunnable())
.task("2", sleepRunnable())
.task("3", sleepRunnable())
.build();

handler.runTasks();
handler.fixCurrentProblems();
handler.continueFromError();