【微服务】spring状态机模式使用详解

一、前言

在很多系统中,通常会涉及到某个业务需要进行各种状态的切换操作,例如在审批流程场景下,某个审批的向下流转需要依赖于上一个状态的结束,再比如电商购物场景中,一个订单的生命周期往往伴随着不同的状态,比如待支付,支付完成,已发货等等,状态的存在,让一个业务的完整流程得以串联,所以状态在真实的场景中具有重要的意义。

二、spring状态机介绍

在开始学习spring状态机之前,有一些概念需要弄清楚,弄清这些概念,才能更好的理解spring状态机。

2.1 什么是状态

在java中,状态(State)是指对象在某一时刻所处的条件或情况,类比于生活中的场景理解,一台机器有运行和停止状态,一个门有关闭和打开状态。状态通常是描述某种事务在某个时刻所处的一种情形。

在Spring状态机中,状态(State)是指对象在某一时刻所处的条件或情况。状态描述了对象的特定属性、行为或情况,是有限状态机(Finite State Machine,FSM)模型中的一个重要概念。

2.2 状态的几个概念

这里特指spring状态机中关于状态的描述。在spring中,状态机通常是有限状态机,即事务实际发生的情形,其状态是可控的,在一定的范围内发生。具体来说:

  • 状态(State):状态是指对象在某一时刻的特定条件或情况。它描述了对象所处的状态,可以是系统中的一个正常状态、初始状态或终止状态。状态是有限状态机的核心概念,用于描述对象的属性和行为。
  • 事件(Event):事件是导致状态转换发生的触发器或信号。当系统接收到特定的事件时,状态机会根据预先定义的规则将对象从当前状态转移到下一个状态。事件可以是外部输入、时间触发、条件满足等引起状态变化的因素。
  • 转移(Transition):转移定义了从一个状态到另一个状态的过程。它描述了状态之间的关系和转换规则,指定了在接收特定事件时应该执行的状态转换操作。转移通常包括源状态、目标状态和触发转移的事件。
  • 行为(Action):行为,也叫动作,是与状态转换相关联的具体动作或逻辑。当状态机执行状态转换时,可以触发与该转换相关的行为来处理额外的逻辑操作。行为可以是方法调用、数据更新、日志记录等对状态变化进行响应的操作。

2.3 什么是状态机

状态机,又称有限状态机,全称:Finite-state machine(FSM)。是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。

FSM是一种算法思想,简单而言,有限状态机由一组状态、一个初始状态、输入和根据输入及现有状态转换为下一个状态的转换函数组成。其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。

2.4 spring 状态机

2.4.1 spring 状态机概述

Spring状态机(Spring State Machine)是Spring框架提供的一个模块,用于帮助开发者轻松地实现状态机功能。Spring状态机模块提供了一个基于有限状态机(Finite State Machine, FSM)模型的框架,使开发者可以定义状态、事件和状态之间的转换,并能够对状态变化进行管理和监控。通过Spring状态机,开发者可以更方便地实现复杂的业务逻辑、工作流程和状态管理。

2.4.2 spring 状态机特点

Spring状态机的特点包括:

  • 灵活性好

    • 开发者可以自定义状态、事件、转换规则以及监听器,以满足各种场景下的需求。

  • 可扩展性强

    • Spring状态机提供了丰富的扩展点和API,开发者可以根据实际需求进行扩展和定制。

  • 易用性高

    • Spring状态机封装了状态机模型的复杂性,提供了简洁的API和注解,使得使用起来更加方便快捷。

  • 集成方便

    • Spring状态机与Spring框架无缝集成,可以与其他Spring组件(如Spring Boot、Spring MVC等)结合使用。

通过使用Spring状态机,开发者可以更好地管理应用程序中的状态,简化状态转换逻辑,提高代码可读性和可维护性。

2.4.3 spring 状态机中的几个状态

在Spring状态机中,状态通常用字符串或枚举类型表示,开发者可以定义不同的状态来描述对象在系统中可能存在的各种情况。状态在状态机中扮演着重要的角色,确定了对象如何响应事件以及转移到下一个状态的规则。

在使用Spring状态机时,可以通过配置来定义状态,并且将这些状态与具体的业务逻辑进行关联。在状态机中,通常包括以下几种类型的状态:

  • Initial State(初始状态):状态机启动时的初始状态,是状态机的起点。只能有一个初始状态。

  • Normal State(普通状态):描述对象在系统中的一种正常状态,可以根据业务需求定义多个普通状态。

  • Final State(终止状态):描述对象完成某种操作或任务后达到的结束状态,表示状态机执行完毕的终点。通常对应于任务完成或异常终止等情况。

定义好状态后,接着需要定义状态之间的转换规则,即指定在接收特定事件时,对象应该从当前状态转移到哪个状态。这样,通过触发事件,状态机会根据定义的规则自动进行状态转换,驱动对象在不同状态下进行行为变化。

2.4.4 spring状态机原理

  • Spring状态机建立在有限状态机(FSM)的概念之上,提供了一种简洁且灵活的方式来定义、管理和执行状态机;

  • 它将状态定义为Java对象,并通过配置来定义状态之间的转换规则;

  • 状态转换通常由外部事件触发,我们可以根据业务逻辑定义不同的事件类型,并与状态转换关联;

  • Spring状态机还提供了状态监听器,用于在状态变化时执行特定的逻辑。同时,状态机的状态可以持久化到数据库或其他存储介质中,以便在系统重启或故障恢复时保持状态的一致性。

2.4.5 spring状态机的几个核心元素

Spring状态机核心主要包括以下三个关键元素:

  • 状态(State):定义了系统可能处于的各个状态。

    • 如订单状态中的待支付、已支付等。

  • 转换(Transition):描述了在何种条件下,当接收到特定事件时,系统可以从一个状态转移到另一个状态。

    • 例如,接收到“支付成功”事件时,订单状态从“待支付”转变为“已支付”。

  • 事件(Event):触发状态转换的动作或者消息,它是引起状态机从当前状态迁移到新状态的原因。

2.5 spring 状态机使用场景

Spring状态机在实际应用中有许多使用场景,下面列举一些常用的应用场景。

工作流管理

Spring状态机可用于实现复杂的工作流控制,如订单处理、审批流程、报销流程等。通过定义不同状态和事件,可以规范流程执行顺序,提高工作效率和可控性。

设备控制

在物联网和嵌入式系统中,设备通常存在不同的工作状态和模式。通过使用Spring状态机,可以实现设备状态管理和控制,如设备启动、停止、故障处理等。

订单生命周期管理

电商系统中,订单状态订单生命周期包含多个状态(待支付、已支付、待发货、已发货等)。利用Spring状态机可以清晰地定义订单状态及状态转换规则,简化订单状态管理。

流程控制

在业务系统中,一些复杂的流程需要根据不同条件或事件进行状态转换。通过Spring状态机,可以将业务流程分解成状态和事件,实现清晰的流程控制和跟踪。

会话管理

在web应用中,用户的会话状态,登录,登出,活跃,超时等,可以借助状态机有效控制,简化会话状态相关的复杂业务逻辑。

游戏开发

在游戏开发中,状态机经常用于描述角色状态、技能释放、战斗流程等。借助Spring状态机框架,开发者可以轻松地管理游戏对象的状态转换和行为逻辑。

自动化测试

在自动化测试中,状态机可以用于描述测试用例的执行流程和状态切换,帮助进行整体测试计划的规划和执行。

总的来说,Spring状态机适用于各种需要状态管理和状态转换的场景,特别是那些涉及复杂流程、状态变化频繁或需要规范化控制的应用领域。通过合理应用状态机模型,可以简化系统设计、提高代码可维护性,并实现更清晰、可控的业务流程和状态管理。希望这些场景示例对您有所启发。

三、与springboot整合使用

接下来演示如何在springboot中使用状态机。工程目录结构如下:

3.1 操作步骤

本例将以一个订单支付的业务为场景进行说明,下面来看具体的操作步骤。

3.1.1 引入依赖

    <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.5</version><relativePath/> <!-- lookup parent from repository --></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-starter</artifactId><version>2.2.1.RELEASE</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>

3.1.2 定义订单状态枚举类

该类定义了订单在实际业务中的几种常用的状态,作为后面业务逻辑操作过程中的执行依据。

状态是状态机的核心组成单元,比如这里的一个订单在实际业务中的各种状态,状态代表了系统或对象在某一时刻可能存在的条件或模式。在状态机中,每一个状态都是系统可能处于的一种明确的条件或阶段。每个状态都是独一无二的,且在任何给定时间,系统只能处于其中一个状态。

public enum OrderStatusEnum {/*** 待提交*/DRAFT,/*** 待出库*/SUBMITTED,/*** 已出库*/DELIVERING,/*** 已签收*/SIGNED,/*** 已完成*/FINISHED,;
}

3.1.3 定义订单事件流转枚举类

转状态换(状态切换),指状态之间的转变过程,它是状态机模型动态性体现。当一个外部事件,如用户按下按钮、接收到信号、满足特定条件等触发时,状态机会从当前状态转移到另一个状态。在定义转换时,需要指出触发转换的事件(Event),以及事件发生时系统的响应,即从哪个状态(Source State)转到哪个状态(Target State)。在如下的枚举类中,定义了与订单状态相对应的触发事件,比如大家熟悉的订单发货,订单签收等。

public enum OrderStatusOperateEventEnum {/*** 确认订单,已提交*/CONFIRMED,/*** 订单发货*/DELIVERY,/*** 订单签收*/RECEIVED,/*** 订单完成*/CONFIRMED_FINISH,;
}

3.1.4 订单状态机配置类

状态机配置类,是在使用Spring State Machine或其他状态机框架时的一个重要步骤,该类主要用于定义状态机的核心结构,包括状态(states)、事件(events)、状态之间的转换规则(transitions),以及可能的状态迁移动作和决策逻辑。

在Spring State Machine中,创建状态机配置类通常是通过继承StateMachineConfigurerAdapter类来实现的。这个适配器类提供了几个模板方法,允许开发者重写它们来配置状态机的各种组成部分:

  • 配置状态(configureStates(StateMachineStateConfigurer))

    • 该方法中,开发者定义状态机中所有的状态,包括初始状态(initial state)和结束状态(final/terminal states)。例如,定义状态A、B、C,并指定状态A作为初始状态。

  • 配置转换(configureTransitions(StateMachineTransitionConfigurer))

    • 在这里,开发者描述状态之间的转换规则,也就是当某个事件(event)发生时,状态机如何从一个状态转移到另一个状态。例如,当事件X发生时,状态机从状态A转移到状态B。

  • 配置初始状态(configureInitialState(ConfigurableStateMachineInitializer))

    • 如果需要显式指定状态机启动时的初始状态,可以在该方法中设置。

import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;import java.util.EnumSet;/*** 订单状态机配置* 处理订单流程流转,与订单事件的配置*/
@Configuration
@EnableStateMachine(name = "orderStateMachine")
public class OrderStatusMachineConfig extends StateMachineConfigurerAdapter<OrderStatusEnum, OrderStatusOperateEventEnum> {/*** 设置状态机的状态 ,初始态和结束态* StateMachineStateConfigurer 即 状态机状态配置* @param states 状态机状态* @throws Exception 异常*/@Overridepublic void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderStatusOperateEventEnum> states) throws Exception {states.withStates().initial(OrderStatusEnum.DRAFT).end(OrderStatusEnum.FINISHED)//囊括了订单的所有状态.states(EnumSet.allOf(OrderStatusEnum.class));}/*** 配置状态转换与事件的关系,说明了订单状态的转换与订单事件之间的关系,其中source target event 是一组配合使用* @param transitions* @throws Exception* source 原始的状态* target 目标状态* event 通过什么事件触发*/@Overridepublic void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderStatusOperateEventEnum> transitions) throws Exception {transitions.withExternal().source(OrderStatusEnum.DRAFT).target(OrderStatusEnum.SUBMITTED)   //待提交与待出库状态的扭转.event(OrderStatusOperateEventEnum.CONFIRMED)                       //待提交与待出库状态的扭转,当被触发了 CONFIRMED 这个状态的时候,下同.and().withExternal().source(OrderStatusEnum.SUBMITTED).target(OrderStatusEnum.DELIVERING).event(OrderStatusOperateEventEnum.DELIVERY).and().withExternal().source(OrderStatusEnum.DELIVERING).target(OrderStatusEnum.SIGNED).event(OrderStatusOperateEventEnum.RECEIVED).and().withExternal().source(OrderStatusEnum.SIGNED).target(OrderStatusEnum.FINISHED).event(OrderStatusOperateEventEnum.CONFIRMED_FINISH);}
}

3.1.5 定义状态机监听器

状态机监听器(State Machine Listener)是一种组件,它可以监听并响应状态机在运行过程中的各种事件,例如状态变迁、进入或退出状态、转换被拒绝等。

在Spring Statemachine中,监听器可以通过实现StateMachineListener接口来定义。该接口提供了一系列回调方法,如transitionTriggered、stateEntered、stateExited等,当状态机触发转换、进入新状态或离开旧状态时,这些方法会被调用。同时,我们也可以通过注解实现监听器。注解方式可以在类的方法上直接声明该方法应该在何种状态下被调用,简化监听器的编写和配置。例如@OnTransition,@OnTransitionEnd,@OnTransitionStart等

import com.congge.entity.OrderDO;
import org.springframework.messaging.Message;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.annotation.WithStateMachine;
import org.springframework.stereotype.Component;@Component("orderStatusListener")
@WithStateMachine(name = "orderStateMachine")
public class OrderStatusListener {/*** 待提交到出库事件扭转时,该方法将会被触发* @param message* @return*/@OnTransition(source = "DRAFT", target = "SUBMITTED")public boolean pay(Message<OrderStatusOperateEventEnum> message) {OrderDO order = (OrderDO) message.getHeaders().get("order");order.setOrderStatusEnum(OrderStatusEnum.SUBMITTED);System.out.println(String.format("出库订单[%s]确认,状态机信息:%s", order.getOrderNo(), message.getHeaders()));return true;}/*** 待出库到出库的事件扭转* @param message* @return*/@OnTransition(source = "SUBMITTED", target = "DELIVERING")public boolean deliver(Message<OrderStatusOperateEventEnum> message) {OrderDO order = (OrderDO) message.getHeaders().get("order");order.setOrderStatusEnum(OrderStatusEnum.DELIVERING);System.out.println(String.format("出库订单[%s]发货出库,状态机信息:%s", order.getOrderNo(), message.getHeaders()));return true;}/*** 出库到签收的事件扭转* @param message* @return*/@OnTransition(source = "DELIVERING", target = "SIGNED")public boolean receive(Message<OrderStatusOperateEventEnum> message){OrderDO order = (OrderDO) message.getHeaders().get("order");order.setOrderStatusEnum(OrderStatusEnum.SIGNED);System.out.println(String.format("出库订单[%s]签收,状态机信息:%s", order.getOrderNo(), message.getHeaders()));return true;}/*** 签收到订单完成状态扭转* @param message* @return*/@OnTransition(source = "SIGNED", target = "FINISHED")public boolean finish(Message<OrderStatusOperateEventEnum> message){OrderDO order = (OrderDO) message.getHeaders().get("order");order.setOrderStatusEnum(OrderStatusEnum.FINISHED);System.out.println(String.format("出库订单[%s]完成,状态机信息:%s", order.getOrderNo(), message.getHeaders()));return true;}
}

3.1.6 配置状态机持久化类

状态机持久化,是指将状态机在某一时刻的状态信息存储到数据库、缓存等介质中,这样的话,即使在系统重启、网络故障或进程终止等情况下,状态机仍能从先前保存的状态继续执行,而不是从初始状态重新开始。

在实际业务场景中,如订单处理、工作流引擎、游戏进度跟踪等,状态机通常用于表示某个实体在其生命周期内的状态变迁。如果没有持久化机制,一旦发生意外情况导致系统宕机或重启,未完成的状态变迁将会丢失,这对于业务连续性和一致性是非常不利的。

状态机持久化通常涉及以下几个方面:

  • 状态记录:记录当前状态机实例处于哪个状态;

  • 上下文数据:除了状态外,可能还需要持久化与状态关联的上下文数据,例如触发状态变迁的事件参数、额外的状态属性等;

  • 历史轨迹:某些复杂场景下可能需要记录状态机的历史变迁轨迹,以便于审计、回溯分析或错误恢复;

  • 并发控制:在多线程或多节点环境下,状态机的持久化还要考虑并发访问和同步的问题;

import com.congge.entity.OrderDO;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.persist.DefaultStateMachinePersister;import java.util.HashMap;
import java.util.Map;@Configuration
public class OrderPersist {/*** 持久化配置* 在实际使用中,可以配合数据库或者Redis等进行持久化操作* @return*/@Beanpublic DefaultStateMachinePersister<OrderStatusEnum, OrderStatusOperateEventEnum, OrderDO> stateMachinePersister(){Map<OrderDO, StateMachineContext<OrderStatusEnum, OrderStatusOperateEventEnum>> map = new HashMap<>();return new DefaultStateMachinePersister<>(new StateMachinePersist<OrderStatusEnum, OrderStatusOperateEventEnum, OrderDO>() {@Overridepublic void write(StateMachineContext<OrderStatusEnum, OrderStatusOperateEventEnum> context, OrderDO order) throws Exception {//持久化操作map.put(order, context);}@Overridepublic StateMachineContext<OrderStatusEnum, OrderStatusOperateEventEnum> read(OrderDO order) throws Exception {//从库中或者redis中读取order的状态信息return map.get(order);}});}
}

3.1.7 发送状态转换类

在上面定义了状态机的监听器,而监听器需要监听到状态流转的事件才会发挥作用,才能监听到某个状态事件之后,完成状态的变更,这就需要一个 发送状态转换的处理方法。

import com.congge.entity.OrderDO;
import org.springframework.messaging.Message;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.persist.StateMachinePersister;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.util.Objects;@Component
public class StateEventUtil {@Resourceprivate StateMachine<OrderStatusEnum, OrderStatusOperateEventEnum> orderStateMachine;@Resourceprivate StateMachinePersister<OrderStatusEnum, OrderStatusOperateEventEnum, OrderDO> stateMachinePersister;/*** 发送状态转换事件*  synchronized修饰保证这个方法是线程安全的* @param message* @return*/public synchronized boolean sendEvent(Message<OrderStatusOperateEventEnum> message) {boolean result = false;try {//启动状态机orderStateMachine.start();OrderDO order = (OrderDO) message.getHeaders().get("order");//尝试恢复状态机状态stateMachinePersister.restore(orderStateMachine, order);result = orderStateMachine.sendEvent(message);//持久化状态机状态stateMachinePersister.persist(orderStateMachine, order);} catch (Exception e) {e.printStackTrace();} finally {if (Objects.nonNull(message)) {OrderDO order = (OrderDO) message.getHeaders().get("order");if (Objects.nonNull(order) && Objects.equals(order.getOrderStatusEnum(), OrderStatusEnum.FINISHED)) {orderStateMachine.stop();}}}return result;}
}

3.1.8 订单业务类

下面是订单核心业务逻辑的实现,包括与订单的各种状态相关的方法

import com.congge.config.OrderStatusEnum;
import com.congge.config.OrderStatusOperateEventEnum;
import com.congge.config.StateEventUtil;
import com.congge.entity.OrderDO;
import com.congge.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;@Service
public class OrderServiceImpl implements OrderService {private StateEventUtil stateEventUtil;private static final AtomicInteger ID_COUNTER = new AtomicInteger(0);private static final Map<Long, OrderDO> ORDER_MAP = new ConcurrentHashMap<>();/*** 创建订单* @param orderDO*/@Overridepublic Long createOrder(OrderDO orderDO) {long orderId = ID_COUNTER.incrementAndGet();orderDO.setOrderId(orderId);orderDO.setOrderNo("AHbb1-" + orderId);orderDO.setOrderStatusEnum(OrderStatusEnum.DRAFT);ORDER_MAP.put(orderId, orderDO);System.out.println(String.format("订单[%s]创建成功:", orderDO.getOrderNo()));return orderId;}/*** 确认订单** @param orderId*/@Overridepublic void confirmOrder(Long orderId) {OrderDO order = ORDER_MAP.get(orderId);System.out.println(String.format("确认订单,订单号:【%s】" ,order.getOrderNo()));Message message = MessageBuilder.withPayload(OrderStatusOperateEventEnum.CONFIRMED).setHeader("order", order).build();if (!stateEventUtil.sendEvent(message)) {System.out.println(" 确认订单失败, 状态异常,订单号:" + order.getOrderNo());}}/*** 订单发货** @param orderId*/@Overridepublic void deliver(Long orderId) {OrderDO order = ORDER_MAP.get(orderId);System.out.println("订单出库,当前订单号:" + order.getOrderNo());Message message = MessageBuilder.withPayload(OrderStatusOperateEventEnum.DELIVERY).setHeader("order", order).build();if (!stateEventUtil.sendEvent(message)) {System.out.println(" 订单出库失败, 状态异常,订单号:" + order.getOrderNo());}}/*** 签收订单** @param orderId*/@Overridepublic void signOrder(Long orderId) {OrderDO order = ORDER_MAP.get(orderId);System.out.println("订单签收,订单号:" + order.getOrderNo());Message message = MessageBuilder.withPayload(OrderStatusOperateEventEnum.RECEIVED).setHeader("order", order).build();if (!stateEventUtil.sendEvent(message)) {System.out.println(" 订单签收失败, 状态异常,订单号:" + order.getOrderNo());}}/*** 确认完成** @param orderId*/@Overridepublic void finishOrder(Long orderId) {OrderDO order = ORDER_MAP.get(orderId);System.out.println("订单完成,订单号:" + order.getOrderNo());Message message = MessageBuilder.withPayload(OrderStatusOperateEventEnum.CONFIRMED_FINISH).setHeader("order", order).build();if (!stateEventUtil.sendEvent(message)) {System.out.println(" 订单完成失败, 状态异常,订单号:" + order.getOrderNo());}}/*** 获取所有订单信息*/@Overridepublic List<OrderDO> listOrders() {return new ArrayList<>(ORDER_MAP.values());}@Autowiredpublic void setStateEventUtil(StateEventUtil stateEventUtil) {this.stateEventUtil = stateEventUtil;}
}

订单对象

import com.congge.config.OrderStatusEnum;public class OrderDO {private OrderStatusEnum orderStatusEnum;private Object orderNo;private long orderId;public void setOrderStatusEnum(OrderStatusEnum orderStatusEnum) {this.orderStatusEnum = orderStatusEnum;}public OrderStatusEnum getOrderStatusEnum() {return orderStatusEnum;}public Object getOrderNo() {return orderNo;}public void setOrderNo(Object orderNo) {this.orderNo = orderNo;}public void setOrderId(long orderId) {this.orderId = orderId;}public long getOrderId() {return orderId;}
}

3.1.9 测试接口

添加一个测试的接口,在测试接口中,开启了 两个线程,理论上讲,不同的线程处理有快有慢,但是各自处理订单时互不影响,所以对每个线程来讲,都能得到订单处理的完整流程。

import com.congge.entity.OrderDO;
import com.congge.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/order")
public class OrderController {@Autowiredprivate OrderService orderService;//localhost:8088/order/machine/test@GetMapping("/machine/test")public void testOrderStatusMachine(){Long orderId1 = orderService.createOrder(new OrderDO());Long orderId2 = orderService.createOrder(new OrderDO());orderService.confirmOrder(orderId1);new Thread("客户线程1"){@Overridepublic void run() {orderService.deliver(orderId1);orderService.signOrder(orderId1);orderService.finishOrder(orderId1);}}.start();orderService.confirmOrder(orderId2);orderService.deliver(orderId2);orderService.signOrder(orderId2);orderService.finishOrder(orderId2);System.out.println("全部订单状态:" + orderService.listOrders());}
}

启动工程之后,调用接口,http://localhost:8088/order/machine/test,观察控制台输出日志信息,从输出的日志信息来看,与上述我们的预期也是一致的,订单的状态在不同的线程中各自执行,互不干扰。

3.2 待优化建议

3.2.1 状态持久化存储

上述代码中,状态的存储是基于JVM内存的,如果发生意外,状态数据将会丢失,实际业务中,可以考虑采用redis或mysql进行存储。

3.2.2 异常处理

在上面的逻辑中,状态机逻辑内部,对异常没有做处理,在状态转换过程中可能出现异常情况,需要适当地捕获和处理这些异常,防止状态机进入无效状态。

3.2.3 增加监控与审计

在实际应用中,为了便于调试和追溯,可以考虑集成日志记录或事件监听器来记录状态机的每一次状态变迁。

3.2.4 扩展性与维护性设计

伴随着业务的发展,后续系统中将会增加更多的状态机,因此状态机的设计应当具有足够的灵活性,以便于新增状态或调整转换规则。

四、写在文末

状态机模式的这一思想在java很多技术组件中均有体现,比如流程引擎,一些编排用的框架,甚至像设计模式中的模板模式等,合理使用状态机可以让复杂的状态切换转换为模板性的操作步骤,省去了复杂的业务逻辑编写,大大提升效率,本文到此结束,感谢观看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/292423.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

蓝桥集训之游戏

蓝桥集训之游戏 核心思想&#xff1a;博弈论 区间dp 设玩家1的最优解为A 玩家2的最优解为B 1的目标就是使A-B最大 2的目标就是使B-A最大 当玩家1取L左端点时 右边子区间结果就是玩家2的最优解B-A 即当前结果为w[L] – (B-A) 当玩家1取R右端点时 左边子区间结果就是玩家2的最…

docker部署DOS游戏

下载镜像 docker pull registry.cn-beijing.aliyuncs.com/wuxingge123/dosgame-web-docker:latestdocker-compose部署 vim docker-compose.yml version: 3 services:dosgame:container_name: dosgameimage: registry.cn-beijing.aliyuncs.com/wuxingge123/dosgame-web-docke…

【Leetcode每日一题】 递归 - 求根节点到叶节点数字之和(难度⭐⭐)(47)

1. 题目解析 题目链接&#xff1a;129. 求根节点到叶节点数字之和 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 递归函数设计&#xff1a; 我们设计了一个递归函数 int dfs(TreeNode* root, int num)&#xff0c;其…

酷得单片机方案 2.4G儿童遥控漂移车

电子方案开发定制&#xff0c;我们是专业的 东莞酷得智能单片机方案之2.4G遥控玩具童车具有以下比较有特色的特点&#xff1a; 1、内置充电电池&#xff1a;这款小车配备了可充电的电池&#xff0c;无需频繁更换电池&#xff0c;既环保又方便。充电方式可能为USB充电或者专用…

如何使用Docker轻松构建和管理应用程序(二)

上一篇文章介绍了 Docker 基本概念&#xff0c;其中镜像、容器和 Dockerfile 。我们使用 Dockerfile 定义镜像&#xff0c;依赖镜像来运行容器&#xff0c;因此 Dockerfile 是镜像和容器的关键&#xff0c;Dockerfile 可以非常容易的定义镜像内容&#xff0c;同时在我们后期的微…

【Consul】Linux安装Consul保姆级教程

【Consul】Linux安装Consul保姆级教程 大家好 我是寸铁&#x1f44a; 总结了一篇【Consul】Linux安装Consul保姆级教程✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 今天要把编写的go程序放到linux上进行测试Consul服务注册与发现&#xff0c;那怎么样才能实现这一过程&am…

docker在线安装centos7(windows版)

目录 1、docker本地安装2、拉取centos7镜像3、启动容器4、配置SSH以访问centos7 1、docker本地安装 windows安装docker比较简单&#xff0c;官网搜索有个docker desktop装上就完事。 2、拉取centos7镜像 可以登录到docker hub上拉&#xff0c;也可以搜出来对应的centos7镜像…

3D检测:从pointnet,voxelnet,pointpillar到centerpoint

记录centerpoint学习笔记。目前被引用1275次&#xff0c;非常高。 地址&#xff1a;Center-Based 3D Object Detection and Tracking (thecvf.com) GitHub - tianweiy/CenterPoint CenterPoint&#xff1a;三维点云目标检测算法梳理及最新进展&#xff08;CVPR2021&#xff…

【蓝桥杯嵌入式】六、真题演练(一)-1演练篇:第 届真题

温馨提示&#xff1a; 真题演练分为模拟篇和研究篇。本专栏的主要作用是记录我的备赛过程&#xff0c;我打算先自己做一遍&#xff0c;把遇到的问题和不同之处记录到演练篇&#xff0c;然后再返回来仔细研究一下&#xff0c;找到最佳的解题方法记录到研究篇。 解题记录&#x…

android WMS服务

android WMS服务 WMS的定义 窗口的分类 WMS的启动 WindowManager Activity、Window、DecorView、ViewRootImpl 之间的关系 WindowToken WMS的定义 WMS是WindowManagerService的简称&#xff0c;它是android系统的核心服务之一&#xff0c;它在android的显示功能中扮演着…

python基础——异常捕获【try-except、else、finally】

&#x1f4dd;前言&#xff1a; 这篇文章主要介绍一下python基础中的异常处理&#xff1a; 1&#xff0c;异常 2&#xff0c;异常的捕获 3&#xff0c;finally语句 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;C语言入门基础以及python入门…

github配置ssh

生成公钥 在电脑用户的目录下打开终端执行 ssh-keygen -t rsa: 执行完不要关 配置文件 看看用户的目录里 .ssh 目录&#xff1a; Host github.comHostname ssh.github.comPort 443配置公钥 复制 id_rsa.pub 文件里的内容 粘贴到 github上 连接密钥 回到刚才的终端…

牛客NC30 缺失的第一个正整数【simple map Java,Go,PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/50ec6a5b0e4e45348544348278cdcee5 核心 Map参考答案Java import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可…

AcWing刷题-区间合并

校门外的树 区间合并&#xff1a; from typing import List def merge(intervals: List[List[int]]) -> List[List[int]]:# 按照第一个元素从小到大进行排序intervals.sort(keylambda x: x[0])# 初始化一个新的数组new_list list()for i in intervals:# 把第一个数组元素添…

Dockerfile:自定义镜像

Dockerfile 是一个文本文件&#xff0c;其中包含了一系列用于自动化构建Docker镜像的指令。通过编写Dockerfile&#xff0c;开发者能够明确地定义一个软件应用及其运行环境应该如何被封装进一个可移植、可重复构建的Docker镜像中。 第一步&#xff1a;在/tmp文件下新建docker…

阿里云效CICD流水线提交前后端项目

后端 一、新建流水线 1进入流水线 2新建流水线 3选择流水线模板 二、上传后端项目 1 将后端项目发布至代码仓库后&#xff0c;在流水线中选择流水线源 我们在选择流水线源之后会出现扫描失败的情况 查看日志发现是因为我们的项目是多模块项目&#xff0c;再扫描的时候无法在…

Android MediaRecorder

AndroidManifest.xml中添加权限标记 <uses-permission android:name"android.permission.RECORD_AUDIO"/> 动态添加权限MainActivity requestPermissions(new String[]{Manifest.permission.CAMERA,Manifest.permission.RECORD_AUDIO},100); 创建MediaReco…

深度学习基础模型之Mamba

Mamba模型简介 问题&#xff1a;许多亚二次时间架构(运行时间复杂度低于O(n^2)&#xff0c;但高于O(n)的情况)&#xff08;例如线性注意力、门控卷积和循环模型以及结构化状态空间模型&#xff08;SSM&#xff09;&#xff09;已被开发出来&#xff0c;以解决 Transformer 在长…

centos2anolis

我的centos7原地升级到anolis7记录 注意&#xff1a;如果是桌面版请先卸载firefox&#xff0c;否则so文件冲突。 参考&#xff1a; CentOS 7和8Linux系统迁移到国产Linux龙蜥Anolis OS 8手册_disable pam_pkcs11 module in pam configuration-CSDN博客 关于 CentOS 迁移龙蜥…

Intellij IDEA 类注释模板设置

1、配置全局USER 在此配置全局USER&#xff0c;用于填充自动生成的注释中的作者author属性。 注释模板中的user参数是默认是获取系统的用户&#xff08;当然注释作者也可以直接写固定值&#xff09;&#xff0c;如果不想和系统用户用同一个信息&#xff0c;可以在IDEA中进行配…