状态机是有限状态自动机(Finite-state machine)的简称,是现实事物运行规则抽象而成的一个数学模型。
在业务系统中,通过应用状态机的方式,将所有的状态、事件、动作都抽离出来,对复杂的状态迁移逻辑进行统一管理,来取代冗长的 if else 判断,能使系统中的复杂问题得以解耦,变得直观、方便操作,使系统更加易于维护和管理。
有限状态机
有限状态机由其状态列表、初始状态和触发每个转换过程的输入动作组成
状态机的行为可以在现代社会的许多设备中观察到,这些设备根据所呈现的事件序列执行预定的动作序列。
简单的例子有:
- 自动售货机,当正确的硬币组合被存入时分配产品
- 电梯,其停靠顺序由乘客要求的楼层决定
- 交通信号灯,当汽车等待时改变顺序
- 密码锁,它需要以正确的顺序输入一系列数字
状态机示例
由状态机建模的简单机制的一个示例是十字转门,用于控制地铁和游乐园的出入。
作为一个状态机,旋转门有两种可能的状态:
Locked
和Unlocked
。有两种可能的输入会影响其状态:将硬币放入槽中 (coin) 和推动悬臂 (push)
旋转门状态机可以用一个状态转换表来表示,显示每个可能的状态,它们之间的转换及每个输入产生的输出
Current State | Input | Next State | Output |
Locked | coin | Unlocked | 解锁旋转门,以便游客能够通过 |
Locked | push | Locked | |
Unlocked | coin | Unlocked | |
Unlocked | push | Locked | 当游客通过时,锁定旋转门 |
旋转栅门状态机也可以由称为状态图的有向图表示:
- 状态都由一个节点(圆圈)表示。
- 边缘(箭头)显示从一种状态到另一种状态的转换。
- 每个箭头都标有触发该转换的输入。
- 不引起状态变化的输入由返回原始状态的圆形箭头表示(例如处于Unlocked状态的硬币输入)
- 从黑点到Locked节点的箭头表示它是初始状态
核心概念
- State(状态):一个状态机至少要包含两个状态。
- Event(事件):事件就是执行某个操作的触发条件或者口令
- Action(动作):事件发生以后要执行动作。一个
Action
一般就对应一个函数。动作是在给定时刻要进行的活动的描述。有多种类型的动作:
- 进入动作(entry action):在进入状态时进行
- 退出动作:在退出状态时进行
- 输入动作:依赖于当前状态和输入条件进行
- 转移动作:在进行特定转移时进行
- Transition(状态转移):从一个状态变化为另一个状态
常见框架
开源状态机对比(2022.11.5)
开源状态机 | 简介 | 活跃度 |
旨在为企业使用提供一个轻量级、高度灵活和可扩展、可诊断、易于使用和类型安全的 Java 状态机实现 | star 2k | |
直接在 java 代码中创建状态机和基于轻量级状态机的工作流 | star 756 | |
旨在提供一个通用的基础设施来处理 Spring 应用程序中的状态机概念 | star 1.3k |
技术选型对比及示例可参考状态机技术选型的考量
Spring Statemachine
Spring Statemachine 旨在提供以下功能:
- 易于使用的扁平单级状态机,可用于简单案例
- 分层状态机结构,以简化复杂的状态配置
- 状态机区域提供更复杂的状态配置
- 可以使用触发器、转换流程、守卫和动作
- 类型安全配置适配器
- 构建器模式,用于在
Spring Application
上下文之外使用的简易实例化 - 常用用例的手册
- 基于
Zookeeper
的分布式状态机 - 状态机事件监听器
-
UML Eclipse Papyrus
建模 - 状态机配置的持久化存储
-
Spring IOC
集成将bean
与状态机相关联
StateContext
StateContext
是使用状态机时最重要的对象之一,因为它被传递到各种方法和回调中以给出状态机的当前状态及其可能的去向
StateContext包含以下内容:
- 当前的Message或Event
- 状态机的Extended State.
- StateMachine本身
- 状态机可能出现的异常
- 当前Transition
- 状态机的源状态
- 状态机的目标状态
- 当前Stage
StateContext
被传递到各种组件中,如Action
和Guard
State Machine Builder
使用简单的构建器模式来构建类似的实例。通过使用字符串作为状态和事件,您可以使用此构建器模式在Spring应用程序上下文之外构建完全动态的状态机
StateMachine<String, String> buildMachine1() throws Exception {
Builder<String, String> builder = StateMachineBuilder.builder();
builder.configureStates()
.withStates()
.initial("S1")
.end("SF")
.states(new HashSet<String>(Arrays.asList("S1","S2","S3","S4")));
return builder.build();
}
builder.configureStates()
、builder.configureTransitions()
和builder.configureConfiguration()
接口方法不能链接在一起,这意味着需要单独调用构建器方法
Action
动作是最有用的组件之一,可用于与状态机交互和协作
可以在状态机及其状态生命周期的各个位置执行
Action
,例如,进入或退出状态或在状态转换期间。
@Override
public void configure(StateMachineStateConfigurer<States, Events> states)
throws Exception {
states
.withStates()
.initial(States.SI)
.state(States.S1, action1(), action2())
.state(States.S2, action1(), action2())
.state(States.S3, action1(), action3());
}
Guard & Choice
满足Guard条件的状态机执行相应的方法,即为if else
的用法
builder.configureStates()
.withStates()
.initial(S1)
.choice(S1)
.states(EnumSet.allOf(State.class));
builder.configureTransitions()
.withChoice()
.source(S1)
.first(S2, guard1(), action1())
.then(S3, guard2(), action2())
.last(S4, action3());
Error Handle Action
当对应执行的Action
抛出了异常,状态机会调用相应的errorAction,可用于执行回滚等操作
builder.configureTransitions()
.withExternal()
.source(S1)
.target(S2)
.event(Events.FAIL)
.action(action1(), errorAction());
参考资料:
- Finite-state machine
- Spring Statement
- Java状态机调研报告