当前位置 : 主页 > 编程语言 > java >

Spring Statemachine状态机的概念(三)

来源:互联网 收集:自由互联 发布时间:2023-02-04
状态机服务 状态机服务是更高级别的实现,旨在 提供更多用户级功能,简化正常运行时间 操作。目前只有一个服务接口 () 存在。​​StateMachineService​​ 用​​StateMachineService​​

Spring Statemachine状态机的概念(三)_状态机

状态机服务

状态机服务是更高级别的实现,旨在 提供更多用户级功能,简化正常运行时间 操作。目前只有一个服务接口 () 存在。​​StateMachineService​​

用​​StateMachineService​​

​​StateMachineService​​是一个用于处理正在运行的计算机的接口 并具有“获取”和“释放”机器的简单方法。它有 一个默认实现,名为 。​​DefaultStateMachineService​​

持久化状态机

传统上,状态机的实例按原样使用 正在运行的程序。您可以使用以下方法实现更动态的行为 动态构建器和工厂,允许状态机 按需实例化。构建状态机实例是一个 操作相对较重。因此,如果您需要(例如)处理 使用状态机对数据库中的任意状态更改,需要 找到更好,更快的方法来做到这一点。

持久功能允许您保存状态机的状态 到外部存储库中,然后根据 序列化状态。例如,如果您有一个数据库表保存 订单,用状态更新订单状态太昂贵了 机器,如果需要为每个更改构建一个新实例。 持久功能允许您重置状态机状态,而无需 实例化新的状态机实例。

有一个配方(请参阅持久)​和一个示例 (请参阅持久),提供以下详细信息 持久状态。

虽然您可以使用 来构建自定义持久性功能,但它有一个概念问题。当侦听器 通知状态更改,状态更改已经发生。如果 侦听器中的自定义持久方法无法更新序列化 外部存储库中的状态、状态机中的状态和状态 然后,外部存储库处于不一致状态。​​StateMachineListener​​

您可以改用状态机拦截器来尝试保存 在状态期间将状态序列化到外部存储中 状态机内的更改。如果此拦截器回调失败, 您可以停止状态更改尝试,而不是以 不一致状态,然后可以手动处理此错误。有关如何使用拦截器的信息,请参阅使用状态机拦截器。

用​​StateMachineContext​​

不能使用普通 java 持久化 序列化,因为对象图太丰富并且包含太多 对其他 Spring 上下文类的依赖。 是状态机的运行时表示形式,可用于 将现有计算机还原到由特定对象表示的状态。​​StateMachine​​​​StateMachineContext​​​​StateMachineContext​​

​​StateMachineContext​​包含两种不同的信息包含方式 对于子上下文。这些通常在机器包含以下情况时使用 正交区域。首先,上下文可以具有子上下文的列表 如果它们存在,可以按原样使用。其次,你可以 包括原始上下文子项时使用的引用列表 不到位。这些子引用确实是唯一的方法 保留正在运行多个并行区域的计算机 独立地。

数据多重持久示例显示 如何保留并行区域。

用​​StateMachinePersister​​

构建然后还原状态机 从它一直有点“黑魔法”如果完成 手动地。该界面旨在缓解这些 通过提供和方法进行操作。默认 此接口的实现为 。​​StateMachineContext​​​​StateMachinePersister​​​​persist​​​​restore​​​​DefaultStateMachinePersister​​

我们可以通过以下方式展示如何使用 来自测试的片段。我们首先创建两个类似的配置 ( 和 ) 表示状态机。请注意,我们可以构建不同的 以其他方式进行此演示的机器,但以这种方式 适用于这种情况。以下示例配置两个状态机:​​StateMachinePersister​​​​machine1​​​​machine2​​

@Configuration@EnableStateMachine(name = "machine1")static class Config1 extends Config {}@Configuration@EnableStateMachine(name = "machine2")static class Config2 extends Config {}static class Config extends StateMachineConfigurerAdapter<String, String> { @Override public void configure(StateMachineStateConfigurer<String, String> states) throws Exception { states .withStates() .initial("S1") .state("S1") .state("S2"); } @Override public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception { transitions .withExternal() .source("S1") .target("S2") .event("E1"); }}

当我们使用对象时,我们可以创建一个内存 实现。​​StateMachinePersist​​

此内存中示例仅用于演示目的。真实 应用程序,您应该使用真正的持久存储实现。

下面的清单显示了如何使用内存中示例:

static class InMemoryStateMachinePersist implements StateMachinePersist<String, String, String> { private final HashMap<String, StateMachineContext<String, String>> contexts = new HashMap<>(); @Override public void write(StateMachineContext<String, String> context, String contextObj) throws Exception { contexts.put(contextObj, context); } @Override public StateMachineContext<String, String> read(String contextObj) throws Exception { return contexts.get(contextObj); }}

在实例化了两台不同的机器之后,我们可以通过 event 转换为状态。然后我们可以 坚持它并恢复 .以下示例演示如何执行此操作:​​machine1​​​​S2​​​​E1​​​​machine2​​

InMemoryStateMachinePersist stateMachinePersist = new InMemoryStateMachinePersist();StateMachinePersister<String, String, String> persister = new DefaultStateMachinePersister<>(stateMachinePersist);StateMachine<String, String> stateMachine1 = context.getBean("machine1", StateMachine.class);StateMachine<String, String> stateMachine2 = context.getBean("machine2", StateMachine.class);stateMachine1.startReactively().block();stateMachine1 .sendEvent(Mono.just(MessageBuilder .withPayload("E1").build())) .blockLast();assertThat(stateMachine1.getState().getIds()).containsExactly("S2");persister.persist(stateMachine1, "myid");persister.restore(stateMachine2, "myid");assertThat(stateMachine2.getState().getIds()).containsExactly("S2");

使用红地

​​RepositoryStateMachinePersist​​(实现 )支持将状态机持久化到 Redis 中。 具体的实现是 ,它使用序列化来 将 A 保存到 中。​​StateMachinePersist​​​​RedisStateMachineContextRepository​​​​kryo​​​​StateMachineContext​​​​Redis​​

对于 ,我们有一个与 Redis 相关的实现,它采用 a 并用作其上下文对象。​​StateMachinePersister​​​​RedisStateMachinePersister​​​​StateMachinePersist​​​​String​​

有关详细用法,请参阅事件服务示例。

​​RedisStateMachineContextRepository​​需要一个才能工作。我们建议使用 for 它,如前面的示例所示。​​RedisConnectionFactory​​​​JedisConnectionFactory​​

用​​StateMachineRuntimePersister​​

​​StateMachineRuntimePersister​​是一个简单的扩展,它添加了一个接口级方法来与之关联。然后,此拦截器 需要在状态更改期间保留计算机,而无需 停止和启动计算机。​​StateMachinePersist​​​​StateMachineInterceptor​​

目前,此接口有 支持的 Spring 数据存储库。这些实现是 、 和。​​JpaPersistingStateMachineInterceptor​​​​MongoDbPersistingStateMachineInterceptor​​​​RedisPersistingStateMachineInterceptor​​

有关详细用法,请参阅数据保留示例。

弹簧启动支持

自动配置模块 () 包含所有 与 Spring 引导集成的逻辑,它提供了以下功能 自动配置和执行器。您所需要的只是拥有这台弹簧状态机 库作为引导应用程序的一部分。​​spring-statemachine-autoconfigure​​

监视和跟踪

​​BootStateMachineMonitor​​自动创建并关联 状态机。 是一个与 Spring 启动和端点集成的自定义实现 通过自定义.(可选)您可以禁用此自动配置 通过将键设置为 .监视示例演示如何使用此自动配置。​​BootStateMachineMonitor​​​​StateMachineMonitor​​​​MeterRegistry​​​​StateMachineTraceRepository​​​​spring.statemachine.monitor.enabled​​​​false​​

存储库配置

如果从类路径中找到所需的类, Spring 数据存储库 实体类扫描会自动配置 存储库支持。

当前支持的配置包括 、 和 。您可以分别使用 和 属性禁用存储库自动配置。​​JPA​​​​Redis​​​​MongoDB​​​​spring.statemachine.data.jpa.repositories.enabled​​​​spring.statemachine.data.redis.repositories.enabled​​​​spring.statemachine.data.mongo.repositories.enabled​​

监控状态机

您可以使用 获取有关 执行转换和操作所需的时间持续时间。以下列表 演示如何实现此接口。​​StateMachineMonitor​​

public class TestStateMachineMonitor extends AbstractStateMachineMonitor<String, String> { @Override public void transition(StateMachine<String, String> stateMachine, Transition<String, String> transition, long duration) { } @Override public void action(StateMachine<String, String> stateMachine, Function<StateContext<String, String>, Mono<Void>> action, long duration) { }}

获得实现后,可以将其添加到 通过配置的状态机,如以下示例所示:​​StateMachineMonitor​​

@Configuration@EnableStateMachinepublic class Config1 extends StateMachineConfigurerAdapter<String, String> { @Override public void configure(StateMachineConfigurationConfigurer<String, String> config) throws Exception { config .withMonitoring() .monitor(stateMachineMonitor()); } @Override public void configure(StateMachineStateConfigurer<String, String> states) throws Exception { states .withStates() .initial("S1") .state("S2"); } @Override public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception { transitions .withExternal() .source("S1") .target("S2") .event("E1"); } @Bean public StateMachineMonitor<String, String> stateMachineMonitor() { return new TestStateMachineMonitor(); }}

有关详细用法,请参阅监视示例。

使用分布式状态

分布式状态可能是 弹簧状态机。究竟什么是分布式状态?一个状态 在单个状态机内自然非常简单理解, 但是,当需要引入共享分布式状态时 通过状态机,事情变得有点复杂。

分布式状态功能仍然是预览功能,不是 但在此特定版本中被认为是稳定的。我们期待这个 功能成熟到首次正式发布。

有关通用配置支持的信息,请参阅配置通用设置。有关实际使用示例,请参阅 动物园管理员示例。

分布式状态机通过包装实际实例的类实现 的 . 拦截 与实例通信并使用 通过接口处理的分布式状态抽象。根据实际实施情况, 还可以使用该接口序列化 ,其中包含足以重置 的信息。​​DistributedStateMachine​​​​StateMachine​​​​DistributedStateMachine​​​​StateMachine​​​​StateMachineEnsemble​​​​StateMachinePersist​​​​StateMachineContext​​​​StateMachine​​

虽然分布式状态机是通过抽象实现的, 当前仅存在一个实现。它基于动物园管理员。

以下示例演示如何配置基于 Zookeeper 的分布式状态 机器':

@Configuration@EnableStateMachinepublic class Config extends StateMachineConfigurerAdapter<String, String> { @Override public void configure(StateMachineConfigurationConfigurer<String, String> config) throws Exception { config .withDistributed() .ensemble(stateMachineEnsemble()) .and() .withConfiguration() .autoStartup(true); } @Override public void configure(StateMachineStateConfigurer<String, String> states) throws Exception { // config states } @Override public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception { // config transitions } @Bean public StateMachineEnsemble<String, String> stateMachineEnsemble() throws Exception { return new ZookeeperStateMachineEnsemble<String, String>(curatorClient(), "/zkpath"); } @Bean public CuratorFramework curatorClient() throws Exception { CuratorFramework client = CuratorFrameworkFactory .builder() .defaultData(new byte[0]) .connectString("localhost:2181").build(); client.start(); return client; }}

您可以找到基于 Zookeeker 的分布式的当前技术文档 附录中的状态机。

用​​ZookeeperStateMachineEnsemble​​

​​ZookeeperStateMachineEnsemble​​本身需要两个强制性设置, 的实例和 .客户端是 ,路径是实例中树的根。​​curatorClient​​​​basePath​​​​CuratorFramework​​​​Zookeeper​​

或者,您可以设置 ,如果融合中不存在任何成员,则默认为 并清除现有数据。您可以设置 如果要保留分布式状态,则将其保留在 应用程序重新启动。​​cleanState​​​​TRUE​​​​FALSE​​

或者,您可以设置 (默认值 到 ) 以保留状态更改的历史记录。这个的价值 设置必须是 2 的幂。 通常是一个很好的默认值 价值。如果特定状态机落后于 日志的大小,它被置于错误状态并与 合奏,表明它已经失去了它的历史和完全重建的能力 已同步状态。​​logSize​​​​32​​​​32​​

测试支持

我们还添加了一组实用程序类来简化状态测试 计算机实例。这些在框架本身中使用,但也 对最终用户非常有用。

​​StateMachineTestPlanBuilder​​构建一个 , 它有一个方法(称为 )。该方法运行计划。 包含一个流畅的构建器 API,可让您添加 计划的步骤。在这些步骤中,您可以发送事件并检查 各种条件,例如状态更改、转换和扩展状态 变量。​​StateMachineTestPlan​​​​test()​​​​StateMachineTestPlanBuilder​​

以下示例用于构建状态机:​​StateMachineBuilder​​

private StateMachine<String, String> buildMachine() throws Exception { StateMachineBuilder.Builder<String, String> builder = StateMachineBuilder.builder(); builder.configureConfiguration() .withConfiguration() .autoStartup(true); builder.configureStates() .withStates() .initial("SI") .state("S1"); builder.configureTransitions() .withExternal() .source("SI").target("S1") .event("E1") .action(c -> { c.getExtendedState().getVariables().put("key1", "value1"); }); return builder.build();}

在下面的测试计划中,我们有两个步骤。首先,我们检查初始 状态 () 确实已设置。其次,我们发送一个事件()并期望 发生一种状态更改,并期望计算机最终处于 . 以下清单显示了测试计划:​​SI​​​​E1​​​​S1​​

StateMachine<String, String> machine = buildMachine();StateMachineTestPlan<String, String> plan = StateMachineTestPlanBuilder.<String, String>builder() .defaultAwaitTime(2) .stateMachine(machine) .step() .expectStates("SI") .and() .step() .sendEvent("E1") .expectStateChanged(1) .expectStates("S1") .expectVariable("key1") .expectVariable("key1", "value1") .expectVariableWith(hasKey("key1")) .expectVariableWith(hasValue("value1")) .expectVariableWith(hasEntry("key1", "value1")) .expectVariableWith(not(hasKey("key2"))) .and() .build();plan.test();

这些实用程序也用于测试分布式 状态机功能。请注意,可以将多台计算机添加到计划中。 如果添加多台计算机,还可以选择 将事件发送到特定计算机、随机计算机或所有计算机。

前面的测试示例使用以下 Hamcrest 导入:

import static org.hamcrest.CoreMatchers.not;import static org.hamcrest.collection.IsMapContaining.hasKey;import static org.hamcrest.collection.IsMapContaining.hasValue;import org.junit.jupiter.api.Test;import static org.hamcrest.collection.IsMapContaining.hasEntry;

预期结果的所有可能选项都记录在 Javadoc for StateMachineTestPlanStepBuilder 中。

日食建模支持

支持使用 UI 建模定义状态机配置 通过Eclipse Papyrus框架。

从 Eclipse 向导中,您可以使用 UML 图创建新的纸莎草模型 语言。在此示例中,它被命名为 .然后你 可以选择从各种图表类型中进行选择,并且必须选择 .​​simple-machine​​​​StateMachine Diagram​​

我们要创建一台具有两种状态(和)的机器,其中 是初始状态。然后,我们需要创建事件来执行转换 从 到 。在Papyrus中,机器看起来像什么东西。 以下示例:​​S1​​​​S2​​​​S1​​​​E1​​​​S1​​​​S2​​

Spring Statemachine状态机的概念(三)_ide_02

在后台,原始 UML 文件类似于以下示例:

<?xml version="1.0" encoding="UTF-8"?><uml:Model xmi:version="20131001" xmlns:xmi="http://www.omg.org/spec/XMI/20131001" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_AMP3IP8fEeW45bORGB4c_A" name="RootElement"> <packagedElement xmi:type="uml:StateMachine" xmi:id="_AMRFQP8fEeW45bORGB4c_A" name="StateMachine"> <region xmi:type="uml:Region" xmi:id="_AMRsUP8fEeW45bORGB4c_A" name="Region1"> <transition xmi:type="uml:Transition" xmi:id="_chgcgP8fEeW45bORGB4c_A" source="_EZrg4P8fEeW45bORGB4c_A" target="_FAvg4P8fEeW45bORGB4c_A"> <trigger xmi:type="uml:Trigger" xmi:id="_hs5jUP8fEeW45bORGB4c_A" event="_NeH84P8fEeW45bORGB4c_A"/> </transition> <transition xmi:type="uml:Transition" xmi:id="_egLIoP8fEeW45bORGB4c_A" source="_Fg0IEP8fEeW45bORGB4c_A" target="_EZrg4P8fEeW45bORGB4c_A"/> <subvertex xmi:type="uml:State" xmi:id="_EZrg4P8fEeW45bORGB4c_A" name="S1"/> <subvertex xmi:type="uml:State" xmi:id="_FAvg4P8fEeW45bORGB4c_A" name="S2"/> <subvertex xmi:type="uml:Pseudostate" xmi:id="_Fg0IEP8fEeW45bORGB4c_A"/> </region> </packagedElement> <packagedElement xmi:type="uml:Signal" xmi:id="_L01D0P8fEeW45bORGB4c_A" name="E1"/> <packagedElement xmi:type="uml:SignalEvent" xmi:id="_NeH84P8fEeW45bORGB4c_A" name="SignalEventE1" signal="_L01D0P8fEeW45bORGB4c_A"/></uml:Model>

打开已定义为 UML 的现有模型时,有三个 文件:、 和 。如果未在 Eclipse的会话,它不了解如何打开实际状态 图表。这是 Papyrus 插件中的一个已知问题,并且有一个简单的 解决方法。在 Papyrus 透视中,您可以看到一个模型浏览器 您的模型。双击图状态机图,其中 指示 Eclipse 在其适当的 Papyrus 中打开此特定模型 建模插件。​​.di​​​​.notation​​​​.uml​​

用​​UmlStateMachineModelFactory​​

在项目中放置 UML 文件后,可以将其导入到 使用 进行配置,其中 与模型相关联。 是一家知道如何 处理 Eclipse Papyrus_generated UML 结构。源 UML 文件可以 要么作为 Spring 给出,要么作为普通位置字符串给出。 下面的示例演示如何创建 的实例:​​StateMachineModelConfigurer​​​​StateMachineModelFactory​​​​UmlStateMachineModelFactory​​​​Resource​​​​UmlStateMachineModelFactory​​

@Configuration@EnableStateMachinepublic static class Config1 extends StateMachineConfigurerAdapter<String, String> { @Override public void configure(StateMachineModelConfigurer<String, String> model) throws Exception { model .withModel() .factory(modelFactory()); } @Bean public StateMachineModelFactory<String, String> modelFactory() { return new UmlStateMachineModelFactory("classpath:org/springframework/statemachine/uml/docs/simple-machine.uml"); }}

像往常一样,Spring 状态机与警卫和 操作,定义为 bean。那些需要连接到UML 通过其内部建模结构。以下部分显示 如何在 UML 定义中定义自定义 Bean 引用。 请注意,也可以手动注册特定方法 没有将它们定义为豆子。

如果创建为 Bean,则会自动连接它以查找已注册的操作和 警卫。您也可以手动定义 ,然后用于查找这些 组件。工厂还具有 registerAction 和 registerGuard 方法,您可以使用它们来注册这些组件。欲了解更多信息 关于这一点,请参阅使用状态机器组件解析程序。​​UmlStateMachineModelFactory​​​​ResourceLoader​​​​StateMachineComponentResolver​​

UML 模型在涉及实现时相对松散,例如 弹簧状态机本身。Spring 状态机留下如何实现很多功能和 功能直至实际实施。以下部分转到 通过 Spring 状态机如何实现基于 UML 模型 Eclipse Papyrus插件。

用​​StateMachineComponentResolver​​

下一个示例演示如何使用 a ,分别寄存 和 函数。请注意,这些组件 不是作为 Bean 创建的。下面的清单显示了该示例:​​UmlStateMachineModelFactory​​​​StateMachineComponentResolver​​​​myAction​​​​myGuard​​

@Configuration@EnableStateMachinepublic static class Config2 extends StateMachineConfigurerAdapter<String, String> { @Override public void configure(StateMachineModelConfigurer<String, String> model) throws Exception { model .withModel() .factory(modelFactory()); } @Bean public StateMachineModelFactory<String, String> modelFactory() { UmlStateMachineModelFactory factory = new UmlStateMachineModelFactory( "classpath:org/springframework/statemachine/uml/docs/simple-machine.uml"); factory.setStateMachineComponentResolver(stateMachineComponentResolver()); return factory; } @Bean public StateMachineComponentResolver<String, String> stateMachineComponentResolver() { DefaultStateMachineComponentResolver<String, String> resolver = new DefaultStateMachineComponentResolver<>(); resolver.registerAction("myAction", myAction()); resolver.registerGuard("myGuard", myGuard()); return resolver; } public Action<String, String> myAction() { return new Action<String, String>() { @Override public void execute(StateContext<String, String> context) { } }; } public Guard<String, String> myGuard() { return new Guard<String, String>() { @Override public boolean evaluate(StateContext<String, String> context) { return false; } }; }}

创建模型

我们首先创建一个空的状态机模型,如下图所示:

Spring Statemachine状态机的概念(三)_状态机_03

可以从创建新模型并为其命名开始,如下图所示:

Spring Statemachine状态机的概念(三)_序列化_04

然后你需要选择状态机图,如下所示:

Spring Statemachine状态机的概念(三)_状态机_05

你最终得到一个空的状态机。

在前面的图像中,您应该已经创建了一个名为 的示例。 您应该以三个文件结束:、 和 。然后,您可以在任何其他文件中使用这些文件 日食实例。此外,您可以导入到 弹簧状态机。​​model​​​​model.di​​​​model.notation​​​​model.uml​​​​model.uml​​

定义状态

状态标识符来自图中的组件名称。 您的计算机中必须有一个初始状态,您可以通过添加 一个根元素,然后绘制到您自己的初始状态的过渡, 如下图所示:

Spring Statemachine状态机的概念(三)_ide_06

在上图中,我们添加了一个根元素和一个初始状态 ()。然后我们画了一个过渡 在这两者之间,以指示这是初始状态。​​S1​​​​S1​​

Spring Statemachine状态机的概念(三)_ide_07

在上图中,我们添加了第二个状态 (),并在 S1 和 S2(表示我们有两种状态)。​​S2​​

定义事件

要将事件与转换关联,您需要创建一个信号 (,在本例中)。为此,请选择“根元素”→“新子→信号”。 下图显示了结果:​​E1​​

Spring Statemachine状态机的概念(三)_ide_08

然后,您需要使用新信号 . 为此,请选择 RootElement → New Child→ SignalEvent。 下图显示了结果:​​E1​​

Spring Statemachine状态机的概念(三)_状态机_09

现在您已经定义了 ,您可以使用它来关联 具有过渡的触发器。有关此内容的详细信息,请参阅定义转换。​​SignalEvent​​

推迟事件

您可以推迟事件以在更合适的时间处理它们。在 UML,这是从状态本身完成的。选择任何州,创建一个 在可延迟触发器下新建触发器,然后选择 SignalEvent 匹配要延迟的信号。

定义过渡

您可以通过在 源和目标状态。在前面的图片中,我们有状态和 两者之间的匿名转换。我们希望将事件与该转换相关联。我们选择过渡,创造新的 触发器,并为此定义 SignalEventE1,如下图所示:​​S1​​​​S2​​​​E1​​

Spring Statemachine状态机的概念(三)_状态机_10

这为您提供了类似于下图所示的排列方式:

Spring Statemachine状态机的概念(三)_序列化_11

如果省略转换的信号事件,它将变为 匿名转换。

定义计时器

转换也可以基于定时事件发生。弹簧状态机 支持两种类型的计时器,一种在 背景和在状态为时延迟触发一次的背景 进入。

若要将新的 TimeEvent 子项添加到模型资源管理器,请将 When 修改为 定义为文本整数的表达式。它的值(以毫秒为单位)成为计时器。 离开相对为 false,以使计时器连续触发。

Spring Statemachine状态机的概念(三)_状态机_12

要定义一个在进入状态时触发的基于定时的事件,该过程恰好 与前面所述相同,但将“相对”设置为 true。下图 显示结果:

Spring Statemachine状态机的概念(三)_序列化_13

然后,用户可以选择这些定时事件之一,而不是 特定转换的信号事件。

定义选择

通过将一个传入过渡绘制到 CHOICE 状态并绘制从它到目标的多个传出转换 国家。我们的配置模型允许您定义 一个 if/elseif/else 结构。但是,对于UML,我们需要使用 用于传出过渡的个人警卫。​​StateConfigurer​​

必须确保为过渡定义的防护不重叠,以便: 无论发生什么,在任何给定情况下,只有一个守卫的计算结果为 TRUE。 时间。这为选择分支提供了精确和可预测的结果 评估。此外,我们建议在没有防护装置的情况下留下一个过渡 以便保证至少一个过渡路径。 下图显示了使用三个分支进行选择的结果:

Spring Statemachine状态机的概念(三)_ide_14

Junction 的工作方式类似,只是它允许多个传入 转换。因此,与选择相比,它的行为纯粹是 学术。选择传出转换的实际逻辑完全相同。

定义交汇点

请参阅定义选项。

定义入口和退出点

您可以使用入口点和出口点创建受控的进入和退出 与具有次州的国家。在下面的状态图中,事件和进入和退出状态具有正常状态的行为,其中正常状态行为通过进入初始状态而发生。​​E1​​​​E2​​​​S2​​​​S21​​

使用事件将计算机带入入口点,然后 导致在任何时候都不会激活初始状态。 类似地,带有事件的退出点控制特定的出口 进入状态,而正常的退出行为将采取 机器进入状态。在状态中,您可以选择 事件,并使计算机进入状态或 , 分别。下图显示了结果:​​E3​​​​ENTRY​​​​S22​​​​S21​​​​EXIT​​​​E4​​​​S4​​​​S2​​​​S3​​​​S22​​​​E4​​​​E2​​​​S3​​​​S4​​

Spring Statemachine状态机的概念(三)_ide_15

如果状态定义为子机引用,并且需要使用入口和退出点, 您必须在外部定义一个 ConnectionPointReference,使用 其进入和退出引用设置为指向正确的入口或退出点 在子机器引用中。只有在那之后,才有可能 定位从外部正确链接到内部的过渡 子计算机引用。使用 ConnectionPointReference,您可能需要 从“高级属性”→“→ UML →中查找这些设置 进入/退出。UML 规范允许您定义多个入口和出口。然而 对于状态机,只允许一个状态机。

定义历史状态

在处理历史状态时,有三个不同的概念在起作用。 UML定义了深层历史和浅层历史。默认历史记录 当历史状态尚不为人所知时,状态就会发挥作用。这些是 在以下各节中表示。

浅历史

在下图中,选择了“浅层历史记录”,并在其中定义了过渡:

Spring Statemachine状态机的概念(三)_状态机_16

深厚的历史

深度历史记录用于具有其他深层嵌套状态的状态, 从而有机会保存整个嵌套状态结构。 下图显示了使用深层历史记录的定义:

Spring Statemachine状态机的概念(三)_序列化_17

默认历史记录

如果转换在历史记录上终止,则在以下情况下 该州在达到其 最终状态,可以选择强制 使用默认值过渡到特定子状态 历史机制。为此,您必须定义一个转换 进入此默认状态。这是从 到 的过渡。​​SH​​​​S22​​

在下图中,如果状态具有 从未活跃过,因为它的历史从未被记录过。如果状态处于活动状态,则选择任一 或。​​S22​​​​S2​​​​S2​​​​S20​​​​S21​​

Spring Statemachine状态机的概念(三)_序列化_18

定义分叉和联接

叉子和连接在纸莎草纸中都表示为条形图。如图所示 在下图中,您需要绘制一个从 into 状态到具有正交区域的传出过渡。 然后是相反的,其中 联接状态是从传入转换中一起收集的。​​FORK​​​​S2​​​​JOIN​​

Spring Statemachine状态机的概念(三)_状态机_19

定义操作

您可以使用行为关联 swtate 进入和退出操作。 有关此内容的更多信息,请参阅定义 Bean 引用。

使用初始操作

定义了初始操作(如配置操作中所示) 在 UML 中,通过在转换中添加一个从初始状态引导的操作 标记到实际状态。然后,当状态 机器已启动。

定义守卫

您可以通过先添加约束,然后定义 它的规范为不透明表达式,其工作方式相同 作为定义 Bean 引用。

定义 Bean 引用

当您需要在任何 UML 效果中进行 Bean 引用时, 操作或保护,您可以使用 或 执行此操作,其中定义的语言需要 be 和语言体 msut 有一个 Bean 引用 ID。​​FunctionBehavior​​​​OpaqueBehavior​​​​bean​​

定义 SpEL 引用

当您需要使用 SpEL 表达式而不是 Bean 引用时 任何 UML 效果、操作或保护,都可以通过使用 或 来实现,其中定义的语言需要 是,语言主体必须是 SpEL 表达式。​​FunctionBehavior​​​​OpaqueBehavior​​​​spel​​

使用子机器引用

通常,当您使用子状态时,您会将它们绘制到状态中 图表本身。图表可能变得过于复杂和大而无法 follow,所以我们也支持将子状态定义为状态机 参考。

若要创建子计算机引用,必须首先创建一个新关系图并为其命名 (例如,子状态机图)。下图显示了要使用的菜单选项:

Spring Statemachine状态机的概念(三)_状态机_20

为新图表提供所需的设计。 下图显示了一个简单的设计作为示例:

Spring Statemachine状态机的概念(三)_ide_21

从要链接的状态(在本例中为 m 状态)中,单击该字段并选择链接的计算机(在我们的示例中为 )。​​S2​​​​Submachine​​​​SubStateMachine​​

Spring Statemachine状态机的概念(三)_状态机_22

最后,在下图中,您可以看到状态链接到 子状态。​​S2​​​​SubStateMachine​​

Spring Statemachine状态机的概念(三)_序列化_23

使用计算机导入

还可以使用导入功能,其中 uml 文件可以引用其他模型。

Spring Statemachine状态机的概念(三)_ide_24

可以使用其他资源或位置 以定义参照的模型文件。​​UmlStateMachineModelFactory​​

@Configuration@EnableStateMachinepublic static class Config3 extends StateMachineConfigurerAdapter<String, String> { @Override public void configure(StateMachineModelConfigurer<String, String> model) throws Exception { model .withModel() .factory(modelFactory()); } @Bean public StateMachineModelFactory<String, String> modelFactory() { return new UmlStateMachineModelFactory( "classpath:org/springframework/statemachine/uml/import-main/import-main.uml", new String[] { "classpath:org/springframework/statemachine/uml/import-sub/import-sub.uml" }); }}

uml 模型中文件之间的链接需要相对为 否则,当从 类路径到临时目录,以便 Eclipse 解析类可以 阅读这些。

存储库支持

本节包含与使用“Spring 数据”相关的文档 Spring 状态机中的存储库。

存储库配置

您可以将计算机配置保留在外部 存储,可以从中按需加载,而不是创建静态存储 使用 Java 配置或基于 UML 的配置进行配置。这 集成通过 Spring 数据存储库抽象进行。

我们创建了一个特殊的实现 叫。它可以使用底座 存储库接口(、 和 )和基本实体 接口 (、、 和 )。​​StateMachineModelFactory​​​​RepositoryStateMachineModelFactory​​​​StateRepository​​​​TransitionRepository​​​​ActionRepository​​​​GuardRepository​​​​RepositoryState​​​​RepositoryTransition​​​​RepositoryAction​​​​RepositoryGuard​​

由于实体和存储库在 Spring 数据中的工作方式, 从用户的角度来看,读取访问可以完全抽象化 在 中完成。没有必要 了解存储库使用的实际映射实体类。 写入存储库始终依赖于使用真实的 特定于存储库的实体类。从机器配置点 当然,我们不需要知道这些,这意味着我们不需要知道 实际实现是JPA,Redis还是其他任何东西 Spring Data支持。使用与存储库相关的实际存储库 当您手动尝试编写新的实体类时,实体类开始发挥作用 状态或转换为受支持的存储库。​​RepositoryStateMachineModelFactory​​

的实体类 和 具有一个字段,该字段可供您使用,可用于 区分配置 — 例如,如果构建了机器 通过。​​RepositoryState​​​​RepositoryTransition​​​​machineId​​​​StateMachineFactory​​

实际实现将在后面的部分中记录。 下图是存储库的 UML 等效状态图 配置。

Spring Statemachine状态机的概念(三)_ide_25

图1.简单机器

Spring Statemachine状态机的概念(三)_状态机_26

图2.简单子机

Spring Statemachine状态机的概念(三)_ide_27

图3.展示机

太平绅士

JPA 的实际存储库实现是 、 、 和 ,它们由 实体类 、 和 分别。​​JpaStateRepository​​​​JpaTransitionRepository​​​​JpaActionRepository​​​​JpaGuardRepository​​​​JpaRepositoryState​​​​JpaRepositoryTransition​​​​JpaRepositoryAction​​​​JpaRepositoryGuard​​

不幸的是,版本“1.2.8”不得不对JPA的实体进行更改 关于使用的表名的模型。以前,生成的表名 始终具有前缀 ,派生自实体类 名字。因为这会导致数据库强加的中断性问题 对数据库对象长度的限制,所有实体类都有 强制表名的特定定义。例如,现在是“状态”——以此类推,其他 NTITY类。​​JPA_REPOSITORY_​​​​JPA_REPOSITORY_STATE​​

显示了手动更新 JPA 状态和转换的通用方法 在以下示例中(相当于 SimpleMachine 中显示的计算机):

@AutowiredStateRepository<JpaRepositoryState> stateRepository;@AutowiredTransitionRepository<JpaRepositoryTransition> transitionRepository;void addConfig() { JpaRepositoryState stateS1 = new JpaRepositoryState("S1", true); JpaRepositoryState stateS2 = new JpaRepositoryState("S2"); JpaRepositoryState stateS3 = new JpaRepositoryState("S3"); stateRepository.save(stateS1); stateRepository.save(stateS2); stateRepository.save(stateS3); JpaRepositoryTransition transitionS1ToS2 = new JpaRepositoryTransition(stateS1, stateS2, "E1"); JpaRepositoryTransition transitionS2ToS3 = new JpaRepositoryTransition(stateS2, stateS3, "E2"); transitionRepository.save(transitionS1ToS2); transitionRepository.save(transitionS2ToS3);}

以下示例也等效于 SimpleSubMachine 中显示的计算机。

@AutowiredStateRepository<JpaRepositoryState> stateRepository;@AutowiredTransitionRepository<JpaRepositoryTransition> transitionRepository;void addConfig() { JpaRepositoryState stateS1 = new JpaRepositoryState("S1", true); JpaRepositoryState stateS2 = new JpaRepositoryState("S2"); JpaRepositoryState stateS3 = new JpaRepositoryState("S3"); JpaRepositoryState stateS21 = new JpaRepositoryState("S21", true); stateS21.setParentState(stateS2); JpaRepositoryState stateS22 = new JpaRepositoryState("S22"); stateS22.setParentState(stateS2); stateRepository.save(stateS1); stateRepository.save(stateS2); stateRepository.save(stateS3); stateRepository.save(stateS21); stateRepository.save(stateS22); JpaRepositoryTransition transitionS1ToS2 = new JpaRepositoryTransition(stateS1, stateS2, "E1"); JpaRepositoryTransition transitionS2ToS3 = new JpaRepositoryTransition(stateS21, stateS22, "E2"); JpaRepositoryTransition transitionS21ToS22 = new JpaRepositoryTransition(stateS2, stateS3, "E3"); transitionRepository.save(transitionS1ToS2); transitionRepository.save(transitionS2ToS3); transitionRepository.save(transitionS21ToS22);}

首先,您必须访问所有存储库。 以下示例演示如何执行此操作:

@AutowiredStateRepository<JpaRepositoryState> stateRepository;@AutowiredTransitionRepository<JpaRepositoryTransition> transitionRepository;@AutowiredActionRepository<JpaRepositoryAction> actionRepository;@AutowiredGuardRepository<JpaRepositoryGuard> guardRepository;

其次,你创造行动和守卫。 以下示例演示如何执行此操作:

JpaRepositoryGuard foo0Guard = new JpaRepositoryGuard();foo0Guard.setName("foo0Guard");JpaRepositoryGuard foo1Guard = new JpaRepositoryGuard();foo1Guard.setName("foo1Guard");JpaRepositoryAction fooAction = new JpaRepositoryAction();fooAction.setName("fooAction");guardRepository.save(foo0Guard);guardRepository.save(foo1Guard);actionRepository.save(fooAction);

第三,必须创建状态。 以下示例演示如何执行此操作:

JpaRepositoryState stateS0 = new JpaRepositoryState("S0", true);stateS0.setInitialAction(fooAction);JpaRepositoryState stateS1 = new JpaRepositoryState("S1", true);stateS1.setParentState(stateS0);JpaRepositoryState stateS11 = new JpaRepositoryState("S11", true);stateS11.setParentState(stateS1);JpaRepositoryState stateS12 = new JpaRepositoryState("S12");stateS12.setParentState(stateS1);JpaRepositoryState stateS2 = new JpaRepositoryState("S2");stateS2.setParentState(stateS0);JpaRepositoryState stateS21 = new JpaRepositoryState("S21", true);stateS21.setParentState(stateS2);JpaRepositoryState stateS211 = new JpaRepositoryState("S211", true);stateS211.setParentState(stateS21);JpaRepositoryState stateS212 = new JpaRepositoryState("S212");stateS212.setParentState(stateS21);stateRepository.save(stateS0);stateRepository.save(stateS1);stateRepository.save(stateS11);stateRepository.save(stateS12);stateRepository.save(stateS2);stateRepository.save(stateS21);stateRepository.save(stateS211);stateRepository.save(stateS212);

第四,也是最后一点,您必须创建过渡。 以下示例演示如何执行此操作:

JpaRepositoryTransition transitionS1ToS1 = new JpaRepositoryTransition(stateS1, stateS1, "A");transitionS1ToS1.setGuard(foo1Guard);JpaRepositoryTransition transitionS1ToS11 = new JpaRepositoryTransition(stateS1, stateS11, "B");JpaRepositoryTransition transitionS21ToS211 = new JpaRepositoryTransition(stateS21, stateS211, "B");JpaRepositoryTransition transitionS1ToS2 = new JpaRepositoryTransition(stateS1, stateS2, "C");JpaRepositoryTransition transitionS1ToS0 = new JpaRepositoryTransition(stateS1, stateS0, "D");JpaRepositoryTransition transitionS211ToS21 = new JpaRepositoryTransition(stateS211, stateS21, "D");JpaRepositoryTransition transitionS0ToS211 = new JpaRepositoryTransition(stateS0, stateS211, "E");JpaRepositoryTransition transitionS1ToS211 = new JpaRepositoryTransition(stateS1, stateS211, "F");JpaRepositoryTransition transitionS2ToS21 = new JpaRepositoryTransition(stateS2, stateS21, "F");JpaRepositoryTransition transitionS11ToS211 = new JpaRepositoryTransition(stateS11, stateS211, "G");JpaRepositoryTransition transitionS0 = new JpaRepositoryTransition(stateS0, stateS0, "H");transitionS0.setKind(TransitionKind.INTERNAL);transitionS0.setGuard(foo0Guard);transitionS0.setActions(new HashSet<>(Arrays.asList(fooAction)));JpaRepositoryTransition transitionS1 = new JpaRepositoryTransition(stateS1, stateS1, "H");transitionS1.setKind(TransitionKind.INTERNAL);JpaRepositoryTransition transitionS2 = new JpaRepositoryTransition(stateS2, stateS2, "H");transitionS2.setKind(TransitionKind.INTERNAL);transitionS2.setGuard(foo1Guard);transitionS2.setActions(new HashSet<>(Arrays.asList(fooAction)));JpaRepositoryTransition transitionS11ToS12 = new JpaRepositoryTransition(stateS11, stateS12, "I");JpaRepositoryTransition transitionS12ToS212 = new JpaRepositoryTransition(stateS12, stateS212, "I");JpaRepositoryTransition transitionS211ToS12 = new JpaRepositoryTransition(stateS211, stateS12, "I");JpaRepositoryTransition transitionS11 = new JpaRepositoryTransition(stateS11, stateS11, "J");JpaRepositoryTransition transitionS2ToS1 = new JpaRepositoryTransition(stateS2, stateS1, "K");transitionRepository.save(transitionS1ToS1);transitionRepository.save(transitionS1ToS11);transitionRepository.save(transitionS21ToS211);transitionRepository.save(transitionS1ToS2);transitionRepository.save(transitionS1ToS0);transitionRepository.save(transitionS211ToS21);transitionRepository.save(transitionS0ToS211);transitionRepository.save(transitionS1ToS211);transitionRepository.save(transitionS2ToS21);transitionRepository.save(transitionS11ToS211);transitionRepository.save(transitionS0);transitionRepository.save(transitionS1);transitionRepository.save(transitionS2);transitionRepository.save(transitionS11ToS12);transitionRepository.save(transitionS12ToS212);transitionRepository.save(transitionS211ToS12);transitionRepository.save(transitionS11);transitionRepository.save(transitionS2ToS1);

您可以在此处找到完整的示例。此示例还演示了如何 从具有以下特征的现有 JSON 文件预填充存储库 实体类的定义。

雷迪斯

Redis 实例的实际存储库实现是 、 、 和 ,它们由 实体类 、 和 分别。​​RedisStateRepository​​​​RedisTransitionRepository​​​​RedisActionRepository​​​​RedisGuardRepository​​​​RedisRepositoryState​​​​RedisRepositoryTransition​​​​RedisRepositoryAction​​​​RedisRepositoryGuard​​

下一个示例显示了手动更新 Redis 的状态和转换的通用方法。 这相当于SimpleMachine中显示的机器。

@AutowiredStateRepository<RedisRepositoryState> stateRepository;@AutowiredTransitionRepository<RedisRepositoryTransition> transitionRepository;void addConfig() { RedisRepositoryState stateS1 = new RedisRepositoryState("S1", true); RedisRepositoryState stateS2 = new RedisRepositoryState("S2"); RedisRepositoryState stateS3 = new RedisRepositoryState("S3"); stateRepository.save(stateS1); stateRepository.save(stateS2); stateRepository.save(stateS3); RedisRepositoryTransition transitionS1ToS2 = new RedisRepositoryTransition(stateS1, stateS2, "E1"); RedisRepositoryTransition transitionS2ToS3 = new RedisRepositoryTransition(stateS2, stateS3, "E2"); transitionRepository.save(transitionS1ToS2); transitionRepository.save(transitionS2ToS3);}

以下示例等效于 SimpleSubMachine 中显示的机器:

@AutowiredStateRepository<RedisRepositoryState> stateRepository;@AutowiredTransitionRepository<RedisRepositoryTransition> transitionRepository;void addConfig() { RedisRepositoryState stateS1 = new RedisRepositoryState("S1", true); RedisRepositoryState stateS2 = new RedisRepositoryState("S2"); RedisRepositoryState stateS3 = new RedisRepositoryState("S3"); stateRepository.save(stateS1); stateRepository.save(stateS2); stateRepository.save(stateS3); RedisRepositoryTransition transitionS1ToS2 = new RedisRepositoryTransition(stateS1, stateS2, "E1"); RedisRepositoryTransition transitionS2ToS3 = new RedisRepositoryTransition(stateS2, stateS3, "E2"); transitionRepository.save(transitionS1ToS2); transitionRepository.save(transitionS2ToS3);}

蒙戈数据库

MongoDB 实例的实际存储库实现是 、 、 和 ,它们由 实体类 、 和 分别。​​MongoDbStateRepository​​​​MongoDbTransitionRepository​​​​MongoDbActionRepository​​​​MongoDbGuardRepository​​​​MongoDbRepositoryState​​​​MongoDbRepositoryTransition​​​​MongoDbRepositoryAction​​​​MongoDbRepositoryGuard​​

下一个示例显示了手动更新 MongoDB 的状态和转换的通用方法。 这相当于 SimpleMachine 中显示的机器。

@AutowiredStateRepository<MongoDbRepositoryState> stateRepository;@AutowiredTransitionRepository<MongoDbRepositoryTransition> transitionRepository;void addConfig() { MongoDbRepositoryState stateS1 = new MongoDbRepositoryState("S1", true); MongoDbRepositoryState stateS2 = new MongoDbRepositoryState("S2"); MongoDbRepositoryState stateS3 = new MongoDbRepositoryState("S3"); stateRepository.save(stateS1); stateRepository.save(stateS2); stateRepository.save(stateS3); MongoDbRepositoryTransition transitionS1ToS2 = new MongoDbRepositoryTransition(stateS1, stateS2, "E1"); MongoDbRepositoryTransition transitionS2ToS3 = new MongoDbRepositoryTransition(stateS2, stateS3, "E2"); transitionRepository.save(transitionS1ToS2); transitionRepository.save(transitionS2ToS3);}

以下示例等效于 SimpleSubMachine 中显示的计算机。

@AutowiredStateRepository<MongoDbRepositoryState> stateRepository;@AutowiredTransitionRepository<MongoDbRepositoryTransition> transitionRepository;void addConfig() { MongoDbRepositoryState stateS1 = new MongoDbRepositoryState("S1", true); MongoDbRepositoryState stateS2 = new MongoDbRepositoryState("S2"); MongoDbRepositoryState stateS3 = new MongoDbRepositoryState("S3"); MongoDbRepositoryState stateS21 = new MongoDbRepositoryState("S21", true); stateS21.setParentState(stateS2); MongoDbRepositoryState stateS22 = new MongoDbRepositoryState("S22"); stateS22.setParentState(stateS2); stateRepository.save(stateS1); stateRepository.save(stateS2); stateRepository.save(stateS3); stateRepository.save(stateS21); stateRepository.save(stateS22); MongoDbRepositoryTransition transitionS1ToS2 = new MongoDbRepositoryTransition(stateS1, stateS2, "E1"); MongoDbRepositoryTransition transitionS2ToS3 = new MongoDbRepositoryTransition(stateS21, stateS22, "E2"); MongoDbRepositoryTransition transitionS21ToS22 = new MongoDbRepositoryTransition(stateS2, stateS3, "E3"); transitionRepository.save(transitionS1ToS2); transitionRepository.save(transitionS2ToS3); transitionRepository.save(transitionS21ToS22);}

存储库持久性

除了将机器配置(如存储库配置中所示)存储在外部存储库中外,您还可以 将计算机保存到存储库中。

接口是一个中央接入点, 与机器持久互,并由实体类提供支持。​​StateMachineRepository​​​​RepositoryStateMachine​​

太平绅士

JPA 的实际存储库实现是 ,它由实体类提供支持。​​JpaStateMachineRepository​​​​JpaRepositoryStateMachine​​

以下示例显示了为 JPA 持久化计算机的通用方法:

@AutowiredStateMachineRepository<JpaRepositoryStateMachine> stateMachineRepository;void persist() { JpaRepositoryStateMachine machine = new JpaRepositoryStateMachine(); machine.setMachineId("machine"); machine.setState("S1"); // raw byte[] representation of a context machine.setStateMachineContext(new byte[] { 0 }); stateMachineRepository.save(machine);}

雷迪斯

Redis 的实际存储库实现是 ,它由实体类提供支持。​​RedisStateMachineRepository​​​​RedisRepositoryStateMachine​​

以下示例显示了为 Redis 保留计算机的通用方法:

@AutowiredStateMachineRepository<RedisRepositoryStateMachine> stateMachineRepository;void persist() { RedisRepositoryStateMachine machine = new RedisRepositoryStateMachine(); machine.setMachineId("machine"); machine.setState("S1"); // raw byte[] representation of a context machine.setStateMachineContext(new byte[] { 0 }); stateMachineRepository.save(machine);}

蒙戈数据库

MongoDB的实际存储库实现是,它由实体类支持。​​MongoDbStateMachineRepository​​​​MongoDbRepositoryStateMachine​​

以下示例显示了为 MongoDB 持久化计算机的通用方法:

@AutowiredStateMachineRepository<MongoDbRepositoryStateMachine> stateMachineRepository;void persist() { MongoDbRepositoryStateMachine machine = new MongoDbRepositoryStateMachine(); machine.setMachineId("machine"); machine.setState("S1"); // raw byte[] representation of a context machine.setStateMachineContext(new byte[] { 0 }); stateMachineRepository.save(machine);}

食谱

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

Spring Statemachine是一个基础框架。也就是说,它没有太多 更高级别的功能或 Spring 框架之外的许多依赖项。 因此,正确使用状态机可能很困难。为了提供帮助, 我们创建了一组解决常见用例的配方模块。

究竟什么是食谱?状态机配方是解决常见问题的模块 用例。从本质上讲,状态机配方既是我们尝试的一个例子 让您轻松重用和扩展。

食谱是为春天做出外部贡献的好方法 状态机项目。如果您还没有准备好为 框架核心本身,一个自定义和通用的配方是一个很好的方式 与其他用户共享功能。

坚持

持久配方是一个简单的实用程序,可让您使用单个状态 用于保留和更新任意项状态的计算机实例 存储库。

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

  • 需要使用的实例 带有 .请注意,状态和事件是必需的 的类型为 。StateMachine<String, String>PersistStateMachineHandlerString
  • ​​PersistStateChangeListener​​需要向处理程序注册 对持久请求做出反应。
  • 该方法用于协调状态更改。handleEventWithState

您可以在 Persist 中找到演示如何使用此配方的示例。

任务

任务配方是一个概念,用于运行使用 状态机。此食谱是根据引入的想法开发的 在任务示例中。​​Runnable​​

下图显示了状态机的一般概念。在此状态图中, 下面的所有内容都显示了单个的通用概念 任务被执行。因为这个食谱可以让你注册一个深 任务的分层 DAG(意味着真实的状态图将是一个深度 子州和地区的嵌套集合),我们不需要 更精确。​​TASKS​​

例如,如果只有两个已注册的任务,则以下状态图表 替换为 和 时是正确的(假设 注册的任务 ID 为 和 )。​​TASK_id​​​​TASK_1​​​​TASK_2​​​​1​​​​2​​

Spring Statemachine状态机的概念(三)_ide_28

执行 可能会导致错误。特别是如果一个复杂的 涉及任务的DAG,您希望有一种方法来处理 任务执行错误,然后有办法继续执行 无需执行已成功执行的任务。也 如果可以处理一些执行错误,那就太好了 自然而然。作为最后的回退,如果无法处理错误 状态机自动进入用户可以处理的状态 手动错误。​​Runnable​​

​​TasksHandler​​包含用于配置处理程序实例的生成器方法 并遵循简单的构建器模式。您可以使用此构建器来 注册任务和实例并定义钩子。​​Runnable​​​​TasksListener​​​​StateMachinePersist​​

现在我们可以采用一个简单的运行简单睡眠,如下所示 示例显示:​​Runnable​​

private Runnable sleepRunnable() { return new Runnable() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { } } };}

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

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

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

要侦听任务执行发生的情况,您可以注册一个实例 a 带 .这个食谱 如果您不想,请提供适配器 实现完整接口。侦听器提供各种钩子 侦听任务执行事件。下面的示例演示类的定义:​​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();

上一篇:Spring Statemachine状态机的概念(四)
下一篇:没有了
网友评论