FSM——squirrel状态机使用
1 FSM介绍
1.1 概念
FSM(finite state machine):有限状态机
- 是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
- 核心内容:有限个状态、通过外部操作引起状态的转移。
- 用来对状态的流转进行解耦,使代码逻辑更加清楚和更加容易维护
1.2 组成和分类
- 组成
- 现态:当前所处状态
- 条件(事件):当一个条件被满足,可能会触发一个动作,或执行一次状态的迁移。
- 动作:条件满足后执行的动作行为。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原有状态。动作不是必须的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。
- 次态:条件满足后要迁往的新状态。"次态"是相对于"现态"而言的,"次态"一旦被激活,就转变为新的"现态"了。
总结:
所有的状态转换都可以概括为:
F(S, E) -> (A,S')
:即如果当前状态为S,接收到一个事件E,则执行动作A,同时状态转换为下个状态S’
- 分类
- F(S) -> (A, S’) 型状态机:下一状态只由当前状态决定
- F(S, E) -> (A, S’) 型状态机:下一状态不但与当前状态有关,还与当前输入值有关
2 使用
2.1 squirrel状态机框架
就像松鼠一样,小巧,灵活,智能,警觉和可爱的动物,squirrel基础旨在为企业使用提供轻量级,高度灵活和可扩展,可诊断,易于使用和类型安全的Java状态机实现。
squirrel文档:https://www.yangguo.info/2015/02/01/squirrel/
代码中使用:
<!--FSM 状态机-->
<dependency>
<groupId>org.squirrelframework</groupId>
<artifactId>squirrel-foundation</artifactId>
<version>0.3.8</version>
</dependency>
2.1.1 普通实现
①事件枚举类
public enum Event {
/**
* 审批通过
*/
APPROVE_PASS,
/**
* 审批拒绝
*/
APPROVE_REFUSED,
/**
* 复核通过
*/
RECHECK_PASS,
/**
* 复核不通过
*/
RECHECK_REFUSED;
}
②状态枚举类
public enum State {
/**
* 待审核
*/
APPROVE,
/**
* 拒绝
*/
REFUSED,
/**
* 同意
*/
PASS;
}
③上下文参数
@Getter
@Setter
@AllArgsConstructor
public class Context {
private String param;
}
④自定义状态机(继承抽象类)
public class StateMachine extends AbstractStateMachine<StateMachine, State, Event, Context> {
private void approvePassAction(State from, State to, Event event, Context context) {
System.out.println(MessageFormat.format("审批人{0}审批了价格,审批结果为通过", context.getParam()));
//TODO 将审批状态更新为已审核通过
}
private void approveRefusedAction(State from, State to, Event event, Context context) {
System.out.println(MessageFormat.format("审批人{0}审批了价格,审批结果为拒绝", context.getParam()));
//TODO 将审批状态更新为审核拒绝
}
private void recheckPassAction(State from, State to, Event event, Context context) {
System.out.println(MessageFormat.format("审批人{0}对未审核通过的价格进行复核,审批结果为通过", context.getParam()));
//TODO 将审批状态更新为审核通过
}
private void recheckRefusedAction(State from, State to, Event event, Context context) {
System.out.println(MessageFormat.format("审批人{0}对未审核通过的价格进行复核,审批结果为不通过", context.getParam()));
//TODO 将审批状态更新为审核不通过
}
}
⑤测试状态机
一般是通过controller请求发起状态机调用,此处为了简单就直接在main方法中调用发起
public class Test {
public static void main(String[] args) {
StateMachineBuilder<StateMachine, State, Event, Context> builder =
StateMachineBuilderFactory.create(StateMachine.class, State.class, Event.class, Context.class);
/**
* 状态转移表
*/
//F(APPROVE,APPROVE_PASS)->(PASS,approvePassAction)
builder.externalTransition().from(APPROVE).to(PASS).on(APPROVE_PASS).callMethod("approvePassAction");
//F(APPROVE,APPROVE_REFUSED)->(REFUSED,ApproveRefusedAction)
builder.externalTransition().from(APPROVE).to(REFUSED).on(APPROVE_REFUSED).callMethod("approveRefusedAction");
//F(REFUSED,RECHECK_PASS)->(PASS,RecheckPassAction)复核 - 通过
builder.externalTransition().from(REFUSED).to(PASS).on(RECHECK_PASS).callMethod("recheckPassAction");
//F(REFUSED,RECHECK_REFUSED)->(REFUSED,RecheckRefusedAction)
builder.externalTransition().from(REFUSED).to(REFUSED).on(RECHECK_REFUSED).callMethod("recheckRefusedAction");
StateMachine machine = builder.newStateMachine(APPROVE);
Context ziyi = new Context("ziyi");
Context ka = new Context("ka");
machine.start();
//ziyi审批拒绝
machine.fire(APPROVE_REFUSED,ziyi);
ka复核成功
//machine.fire(RECHECK_PASS,ka);
}
}
手动builder添加状态转移:
待审核 - 审核同意 - 复核通过
…
运行结果:
2.1.2 注解实现
可以直接通过@Transitions注解定义多个状态转变,
指定从from状态到那to状态,然后on在哪个事件,最后callMethod(调用)哪个方法
@Transitions({
@Transit(from = "APPROVE", to = "REFUSED", on = "APPROVE_REFUSED", callMethod = "approveRefusedAction"),
})
public class StateMachine extends AbstractStateMachine<StateMachine, State, Event, Context> {
...
}
其他示例:
/**
* 定义 触发事件、状态变化时,调用的方法
* @States 定义状态列表,里面可以包含多个状态
* @State定义每个状态,name状态名称,entryStateInit进入状态时调用的方法,exitCallMethod 离开状态是调用的方法,initialState 为true时,为默认状态。
* */
@States({
@State(name = "INIT", entryCallMethod = "entryStateInit", exitCallMethod = "exitStateInit", initialState = true),
@State(name = "WAIT_PAY", entryCallMethod = "entryStateWaitPay", exitCallMethod = "exitStateWaitPay"),
@State(name = "WAIT_SEND", entryCallMethod = "entryStateWaitSend", exitCallMethod = "exitStateWaitSend"),
@State(name = "PART_SEND", entryCallMethod = "entryStatePartSend", exitCallMethod = "exitStatePartSend"),
@State(name = "WAIT_RECEIVE", entryCallMethod = "entryStateWaitReceive", exitCallMethod = "exitStateWaitReceive"),
@State(name = "COMPLETE", entryCallMethod = "entryStateComplete", exitCallMethod = "exitStateComplete")
})
@Transitions({
@Transit(from = "INIT", to = "WAIT_PAY", on = "SUBMIT_ORDER", callMethod = "submitOrder"),
@Transit(from = "WAIT_PAY", to = "WAIT_SEND", on = "PAY", callMethod = "pay"),
@Transit(from = "WAIT_SEND", to = "PART_SEND", on = "PART_SEND", callMethod = "partSend"),
@Transit(from = "PART_SEND", to = "WAIT_RECEIVE", on = "SEND", callMethod = "send"),
@Transit(from = "WAIT_RECEIVE", to = "COMPLETE", on = "COMPLETE", callMethod = "complete")
})
// @StateMachineParameters用来声明状态机泛型参数类型,向AbstractStateMachine传递参数
@StateMachineParameters(stateType = OrderState.class, eventType = OrderEvent.class, contextType = OrderContext.class)
@Slf4j
public class SubmitOrderStateMachine extends AbstractStateMachine<UntypedStateMachine, Object, Object, Object> implements UntypedStateMachine {
.......
}
2.1.5 状态机引擎
有限状态机是一种用来进行对象行为建模的工具,其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。在电商场景(订单、物流、售后)、社交(IM消息投递)、分布式集群管理(分布式计算平台任务编排)等场景都有大规模的使用。
有限状态机的使用场景很丰富,主要有以下三种常用框架:
- squirrel-foundation(503stars)
- spring-statemachine(305stars)
- stateless4j(293stars)
这三款finite state machine是github上stars top3的java状态机引擎框架
2.1.4 工作流与状态机区别
都是流程的管理,工作流引擎和状态机到底区别在哪里。
- 工作流引擎中,在完成之前的操作时,就会切换到下一个步骤,而状态需要一个外部事件才能让分支到下一个活动。【状态机是事件驱动的,工作流引擎不是】
- 当业务规则可能在时间上发生变化时,应当选择工作流引擎。因为如果选择状态机,在我们添加新状态时,需要保证不破坏其他流程。
- 工作
参考:
https://blog.csdn.net/footless_bird/article/details/115797710
https://blog.csdn.net/sunyufeng22/article/details/117986507