Recipes
本章包含现有内置状态机Recipes的文档。
Spring StateMachine 是一个基础框架。也就是说,它并不包含太多高级功能或依赖于其他许多库之外的 Spring Framework。 因此,正确使用状态机可能较为困难。为此,我们创建了一组Recipes模块来解决常见用例问题。
什么是Recipes?状态机Recipes是一个模块,用于解决常见用例。本质上,状态机Recipes既是我们尝试制作的一个示例,旨在使您能够轻松重用和扩展,也是一个可供参考的实例。
| Recipes 是一种向 Spring Statemachine 项目做出外部贡献的好方法。如果你还不准备为框架核心本身做贡献,那么自定义和常见的 Recipes 就是一个很好的方式来与其他用户分享功能。 |
36. 持久化
The persist recipe 是一个简单的工具,它允许你使用单个状态机实例来持久化并更新仓库中任意项的状态。
The recipe’s main class is PersistStateMachineHandler, which makes three assumptions:
-
一个
StateMachine<String, String>实例需要与一个PersistStateMachineHandler一起使用。请注意,状态和事件必须是String的类型。 -
PersistStateChangeListener需要注册到处理器 以响应持久化请求。 -
The
handleEventWithState方法用于协调状态变化。
您可以找到一个样例,演示如何使用此配方: 持久化.
37. 任务
The tasks 脚本是运行一个有向无环图(Directed Acyclic Graph)的概念,该图由Runnable个实例组成,并使用状态机。此脚本源自 Tasks 样例中引入的想法。
接下来的图片展示了状态机的一般概念。在这个状态图中,
在TASKS下的所有内容展示了一个单一任务执行的通用概念。
由于本Recipes允许您注册一个深度层次化的DAG(即,实际的状态图会是一个嵌套层次非常深的子状态和区域集合),所以我们无需更加精确。
例如,如果您只注册了两个任务,则在将TASK_id替换为TASK_1和TASK_2(假设注册的任务ID是1和2)时,以下状态图将是正确的。
执行一个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。此Recipes
提供了一个适配器 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();