参考
- 开源中国:spring statemachine 的企业可用级开发指南 8 - 复杂状态机的实现,choice,guard 和 action
作者:wphmoon- CSDN文章系列:spring statemachine
作者:wphmoon123- 稀土掘金:彻底搞懂Spring状态机原理,实现订单与物流解耦
作者:Tom弹架构- 稀土掘金:当转转严选订单遇到状态机
作者:转转技术团队- 代码先锋网:使用Spring StateMachine框架实现状态机
什么是状态机?
状态机,全名为确定性有穷状态自动机,也常被简称为有穷自动机,简写FSM。状态机维护一组状态集合,和事件集合,能够对特定的事件输入,作出状态流转,并执行相应的动作。
怎么用Spring实现状态机?
举个发票状态流转简单例子,自己想的,非实际业务场景。
引入依赖
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
关键要素梳理
- 状态集合(states),所有状态的枚举
- 事件集合(events),触发状态变化的事件枚举
- 检测器(guards),也就判断是否满足条件
- 转换器(transitions),指定从某状态到某状态
- 上下文(context)
画状态流转图
状态枚举
/**
* 发票状态枚举
* @date 2022/10/13
*/
@Getter
@AllArgsConstructor
public enum InvoiceStateEnum {
/** 未开票 */
NOT_INVOICE(1, "未开票"),
/** 待开票 */
WAIT_INVOICE(2, "待开票"),
/** 开票中 */
INVOICING(3, "开票中"),
/** 已开票 */
INVOICE_SUCCESS(4, "已开票"),
/** 开票失败 */
INVOICE_FAIL(5, "开票失败"),
/** 开票前校验 */
INVOICE_PRE_CHECK(6, "开票前校验"),
/** 开票结果校验 */
INVOICE_RESULT_CHECK(7, "开票结果校验");
private Integer code;
private String description;
/**
* 根据编码获取枚举
* @date 2022/10/17
* @param code 编码
* @return com.sc.cloud.designpattern.invoicestatemachine.enums.InvoiceState
*/
public static InvoiceStateEnum valueOf(Integer code) {
return Arrays.stream(values()).filter(v -> v.code.equals(code)).findFirst().orElse(null);
}
}
事件枚举
/**
* 发票事件
*@date 2022/10/13
*/
@Getter
@AllArgsConstructor
public enum InvoiceEventEnum {
/** 创建 */
INIT(1, "创建"),
/** 触发待开 */
WAIT_INVOICE(2, "触发待开"),
/** 取消发票 */
CANCEL_INVOICE(3, "取消发票"),
/** 修改发票信息 */
CHANGE_INVOICE(4, "修改发票信息"),
/** 开票前校验 */
CHECK_INVOICE_PRE(5, "开票前校验"),
/** 开票结果校验 */
CHECK_INVOICE_RESULT(6, "开票结果校验");
private Integer code;
private String description;
}
发票主体对象
/**
* 发票实体
*@date 2022/10/17
*/
@Data
public class Invoice {
private String invoiceTitle;
private String buyer;
private Integer state;
}
发票状态机builder
/**
* 发票状态配置
*@date 2022/10/13
*/
@Component
@Slf4j
@EnableStateMachine(name = InvoiceStateMachineConstant.MACHINE_ID)
public class InvoiceStateMachineBuilder {
private final static String MACHINE_ID = "invoiceMachine";
@Autowired
private InvoiceStateMachineListener invoiceStateMachineListener;
public StateMachine<InvoiceStateEnum, InvoiceEventEnum> build(BeanFactory beanFactory) throws Exception {
StateMachineBuilder.Builder<InvoiceStateEnum, InvoiceEventEnum> builder = StateMachineBuilder.builder();
log.info("构建发票状态机");
builder.configureConfiguration()
.withConfiguration()
.machineId(InvoiceStateMachineConstant.MACHINE_ID)
.listener(invoiceStateMachineListener)
.beanFactory(beanFactory);
builder.configureStates()
.withStates()
.initial(InvoiceStateEnum.NOT_INVOICE)
.choice(InvoiceStateEnum.INVOICE_PRE_CHECK)
.choice(InvoiceStateEnum.INVOICE_RESULT_CHECK)
.states(EnumSet.allOf(InvoiceStateEnum.class));
builder.configureTransitions()
// 未开票 -> 待开票
.withExternal().source(InvoiceStateEnum.NOT_INVOICE).target(InvoiceStateEnum.WAIT_INVOICE)
.event(InvoiceEventEnum.WAIT_INVOICE)
// action 可以传入两个
.action(new InvoiceWaitInvoiceAction())
.guard(new InvoicePreCheckGuard())
.and()
// 待开票 -> 未开票
.withExternal().source(InvoiceStateEnum.WAIT_INVOICE).target(InvoiceStateEnum.NOT_INVOICE)
.event(InvoiceEventEnum.CANCEL_INVOICE)
.action(new InvoiceCancelInvoiceAction())
.and()
// 待开票 -> 开票前校验
.withExternal().source(InvoiceStateEnum.WAIT_INVOICE).target(InvoiceStateEnum.INVOICE_PRE_CHECK)
.event(InvoiceEventEnum.CHECK_INVOICE_PRE)
.and()
// 开票前校验 -> 开票中 / 开票失败
.withChoice().source(InvoiceStateEnum.INVOICE_PRE_CHECK)
.first(InvoiceStateEnum.INVOICING, new InvoicePreCheckGuard())
.last(InvoiceStateEnum.INVOICE_FAIL)
.and()
// 开票中 -> 开票结果校验
.withExternal().source(InvoiceStateEnum.INVOICING).target(InvoiceStateEnum.INVOICE_RESULT_CHECK)
.event(InvoiceEventEnum.CHECK_INVOICE_RESULT)
.and()
// 开票失败 -> 待开票
.withExternal().source(InvoiceStateEnum.INVOICE_FAIL).target(InvoiceStateEnum.WAIT_INVOICE)
.event(InvoiceEventEnum.CHANGE_INVOICE)
.and()
// 开票结果校验 -> 开票成功 / 开票失败
.withChoice().source(InvoiceStateEnum.INVOICE_RESULT_CHECK)
.first(InvoiceStateEnum.INVOICE_SUCCESS, new InvoiceResultCheckGuard())
.last(InvoiceStateEnum.INVOICE_FAIL);
return builder.build();
}
}
事件处理Action
/**
*@date 2022/10/14
*/
@Slf4j
public class InvoiceWaitInvoiceAction implements Action<InvoiceStateEnum, InvoiceEventEnum> {
@Override
public void execute(StateContext<InvoiceStateEnum, InvoiceEventEnum> stateContext) {
log.info("创建发票,保存发票信息...{}", JSON.toJSONString(stateContext));
}
}
/**
*@date 2022/10/14
*/
@Slf4j
public class InvoiceCancelInvoiceAction implements Action<InvoiceStateEnum, InvoiceEventEnum> {
@Override
public void execute(StateContext<InvoiceStateEnum, InvoiceEventEnum> stateContext) {
log.info("取消开票...参数:{}", JSON.toJSONString(stateContext));
}
}
状态机转换器配置(@OnTransition)
/**
* 状态转化器,和listener实现同样的效果,这种方式更优雅,但listener有更多的设置
*@date 2022/10/13
*/
@WithStateMachine(id = "invoiceMachine")
@Slf4j
public class InvoiceEventConfig {
@OnTransition(target = "NOT_INVOICE")
public void create() {
log.info("发票创建事件...");
}
@OnTransition(source = "NOT_INVOICE", target = "WAIT_INVOICE")
public void waitInvoice(Message<InvoiceEventEnum> message) {
log.info("发票待开事件参数:{}", JSON.toJSONString(message));
log.info("发票待开事件...");
}
}
判断状态变化条件guard
/**
* 校验是否满足开票前判断
*@date 2022/10/13
*/
@Slf4j
public class InvoicePreCheckGuard implements Guard<InvoiceStateEnum, InvoiceEventEnum> {
@Override
public boolean evaluate(StateContext<InvoiceStateEnum, InvoiceEventEnum> stateContext) {
log.info("开票前进行校验...");
return true;
}
}
/**
* 校验是否满足开票结果判断
*@date 2022/10/13
*/
@Slf4j
public class InvoiceResultCheckGuard implements Guard<InvoiceStateEnum, InvoiceEventEnum> {
@Override
public boolean evaluate(StateContext<InvoiceStateEnum, InvoiceEventEnum> stateContext) {
log.info("开票结果校验...");
return false;
}
}
状态监听器Listener
/**
*@date 2022/10/17
*/
@Component
@Slf4j
public class InvoiceStateMachineListener implements StateMachineListener<InvoiceStateEnum, InvoiceEventEnum> {
@Override
public void stateChanged(State<InvoiceStateEnum, InvoiceEventEnum> state, State<InvoiceStateEnum, InvoiceEventEnum> state1) {
// 注意初始化状态没赋值 源状态state是null
log.info("更新到状态:{}", state1.getId());
}
@Override
public void stateEntered(State<InvoiceStateEnum, InvoiceEventEnum> state) {
log.info("执行stateEntered");
}
@Override
public void stateExited(State<InvoiceStateEnum, InvoiceEventEnum> state) {
log.info("执行stateExited");
}
@Override
public void eventNotAccepted(Message<InvoiceEventEnum> message) {
// 仅在状态不满足转化打印,guard判断不满足不会输出
log.info("不满足状态转化条...");
}
@Override
public void transition(Transition<InvoiceStateEnum, InvoiceEventEnum> transition) {
log.info("执行transition");
}
@Override
public void transitionStarted(Transition<InvoiceStateEnum, InvoiceEventEnum> transition) {
log.info("执行transitionStarted");
}
@Override
public void transitionEnded(Transition<InvoiceStateEnum, InvoiceEventEnum> transition) {
log.info("执行transitionEnded");
}
@Override
public void stateMachineStarted(StateMachine<InvoiceStateEnum, InvoiceEventEnum> stateMachine) {
log.info("执行stateMachineStarted");
}
@Override
public void stateMachineStopped(StateMachine<InvoiceStateEnum, InvoiceEventEnum> stateMachine) {
log.info("执行stateMachineStopped");
}
@Override
public void stateMachineError(StateMachine<InvoiceStateEnum, InvoiceEventEnum> stateMachine, Exception e) {
log.info("状态机发生异常:{}", e.getMessage(), e);
}
@Override
public void extendedStateChanged(Object o, Object o1) {
log.info("执行extendedStateChanged");
}
@Override
public void stateContext(StateContext<InvoiceStateEnum, InvoiceEventEnum> stateContext) {
log.info("执行stateContext");
}
}
发票状态持久器Persist
/**
* 发票状态机持久化(伪持久化)
*@date 2022/10/17
*/
@Component
public class InvoiceStateMachinePersist implements StateMachinePersist<InvoiceStateEnum, InvoiceEventEnum, Invoice> {
@Override
public void write(StateMachineContext<InvoiceStateEnum, InvoiceEventEnum> stateMachineContext, Invoice invoice) throws Exception {
// 这里不做任何持久化工作,实际使用中只要从数据库取出状态加载到状态机中即可
}
@Override
public StateMachineContext<InvoiceStateEnum, InvoiceEventEnum> read(Invoice invoice) throws Exception {
StateMachineContext<InvoiceStateEnum, InvoiceEventEnum> result = new DefaultStateMachineContext<>(
InvoiceStateEnum.valueOf(invoice.getState()), null, null, null, null, InvoiceStateMachineConstant.MACHINE_ID
);
return result;
}
}
持久器配置
/**
*@date 2022/10/17
*/
@Configuration
public class PersistConfig {
@Autowired
private InvoiceStateMachinePersist invoiceStateMachinePersist;
@Bean(name = "invoicePersist")
public StateMachinePersister<InvoiceStateEnum, InvoiceEventEnum, Invoice> invoicePersister() {
return new DefaultStateMachinePersister<>(invoiceStateMachinePersist);
}
}
测试
@Autowired
private InvoiceStateMachineBuilder invoiceStateMachineBuilder;
@Resource(name = "invoicePersist")
private StateMachinePersister<InvoiceStateEnum, InvoiceEventEnum, Invoice> persister;
@Autowired
private BeanFactory beanFactory;
@Test
public void builder() throws Exception {
StateMachine<InvoiceStateEnum, InvoiceEventEnum> stateMachine = invoiceStateMachineBuilder.build(beanFactory);
System.out.println(stateMachine.getId());
stateMachine.start();
Invoice invoice = new Invoice();
invoice.setInvoiceTitle("抬头");
invoice.setBuyer("购方姓名");
Message<InvoiceEventEnum> message = MessageBuilder.withPayload(InvoiceEventEnum.WAIT_INVOICE).setHeader("invoice", invoice).build();
stateMachine.sendEvent(message);
System.out.println("当前状态:" + stateMachine.getState().getId());
// 恢复状态
invoice.setState(4);
persister.restore(stateMachine, invoice);
// 查看恢复后状态机的状态
System.err.println("恢复后的状态:" + stateMachine.getState().getId());
}