0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Spring状态机存在的问题

jf_ro2CN3Fa 来源:芋道源码 2023-05-22 11:12 次阅读

1、什么是状态机

1.1 什么是状态

1.2 四大概念

1.3 状态机

2、状态机图

3、spring statemachine

3.1 状态机spring statemachine 概述

3.2 快速开始

3.3 测试验证

3.4 状态机存在的问题

1、什么是状态机

1.1 什么是状态

先来解释什么是“状态”( State )。现实事物是有不同状态的,例如一个自动门,就有 open 和 closed 两种状态。我们通常所说的状态机是有限状态机,也就是被描述的事物的状态的数量是有限个,例如自动门的状态就是两个 open 和 closed 。

eeff0844-f84b-11ed-90ce-dac502259ad0.jpg

状态机,也就是 State Machine ,不是指一台实际机器,而是指一个数学模型。说白了,一般就是指一张状态转换图。例如,根据自动门的运行规则,我们可以抽象出下面这么一个图。

自动门有两个状态,open 和 closed ,closed 状态下,如果读取开门信号,那么状态就会切换为 open 。open 状态下如果读取关门信号,状态就会切换为 closed 。

状态机的全称是有限状态自动机,自动两个字也是包含重要含义的。给定一个状态机,同时给定它的当前状态以及输入,那么输出状态时可以明确的运算出来的。例如对于自动门,给定初始状态 closed ,给定输入“开门”,那么下一个状态时可以运算出来的。

这样状态机的基本定义我们就介绍完毕了。重复一下:状态机是有限状态自动机的简称,是现实事物运行规则抽象而成的一个数学模型。

1.2 四大概念

下面来给出状态机的四大概念。

第一个是 State ,状态。一个状态机至少要包含两个状态。例如上面自动门的例子,有 open 和 closed 两个状态。

第二个是 Event ,事件。事件就是执行某个操作的触发条件或者口令。对于自动门,“按下开门按钮”就是一个事件。

第三个是 Action ,动作。事件发生以后要执行动作。例如事件是“按开门按钮”,动作是“开门”。编程的时候,一个 Action一般就对应一个函数。

第四个是 Transition ,变换。也就是从一个状态变化为另一个状态。例如“开门过程”就是一个变换。

1.3 状态机

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

FSM是一种算法思想,简单而言,有限状态机由一组状态、一个初始状态、输入和根据输入及现有状态转换为下一个状态的转换函数组成。

其作用主要是描述对象在它的生命周期内所经历的状态序列,以及如何响应来自外界的各种事件。

Java指南:https://java-family.cn

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

项目地址:https://github.com/YunaiV/ruoyi-vue-pro

视频教程:https://doc.iocoder.cn/video/

2、状态机图

做需求时,需要了解以下六种元素:起始、终止、现态、次态(目标状态)、动作、条件,我们就可以完成一个状态机图了:

以订单为例:以从待支付状态转换为待发货状态为例

ef0affbe-f84b-11ed-90ce-dac502259ad0.jpg

①现态:是指当前所处的状态。待支付

②条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。支付事件

③动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。状态转换为待发货

④次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。待发货 注意事项

1、避免把某个“程序动作”当作是一种“状态”来处理。那么如何区分“动作”和“状态”?“动作”是不稳定的,即使没有条件的触发,“动作”一旦执行完毕就结束了;而“状态”是相对稳定的,如果没有外部条件的触发,一个状态会一直持续下去。关注工众号:码猿技术专栏,回复关键词:1111 获取阿里内部Java性能调优手册!

2、状态划分时漏掉一些状态,导致跳转逻辑不完整。所以在设计状态机时,我们需要反复的查看设计的状态图或者状态表,最终达到一种牢不可破的设计方案。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

项目地址:https://github.com/YunaiV/yudao-cloud

视频教程:https://doc.iocoder.cn/video/

3、spring statemachine

3.1 状态机spring statemachine 概述

Spring Statemachine是应用程序开发人员在Spring应用程序中使用状态机概念的框架

Spring Statemachine旨在提供以下功能:

易于使用的扁平单级状态机,用于简单的使用案例。

分层状态机结构,以简化复杂的状态配置。

状态机区域提供更复杂的状态配置。

使用触发器,转换,警卫和操作。

键入安全配置适配器。

生成器模式,用于在Spring Application上下文之外使用的简单实例化通常用例的食谱

基于Zookeeper的分布式状态机

状态机事件监听器。

UML Eclipse Papyrus建模。

将计算机配置存储在永久存储中。

Spring IOC集成将bean与状态机关联起来。

状态机功能强大,因为行为始终保证一致,使调试相对容易。这是因为操作规则是在机器启动时写成的。这个想法是你的应用程序可能存在于有限数量的状态中,某些预定义的触发器可以将你的应用程序从一个状态转移到另一个状态。此类触发器可以基于事件或计时器。

在应用程序之外定义高级逻辑然后依靠状态机来管理状态要容易得多。您可以通过发送事件,侦听更改或仅请求当前状态来与状态机进行交互。

官网:spring.io/projects/sp…

3.2 快速开始

以订单状态扭转的例子为例:

表结构设计如下:

CREATETABLE`tb_order`(
`id`bigint(20)unsignedNOTNULLAUTO_INCREMENTCOMMENT'主键ID',
`order_code`varchar(128)COLLATEutf8mb4_binDEFAULTNULLCOMMENT'订单编码',
`status`smallint(3)DEFAULTNULLCOMMENT'订单状态',
`name`varchar(64)COLLATEutf8mb4_binDEFAULTNULLCOMMENT'订单名称',
`price`decimal(12,2)DEFAULTNULLCOMMENT'价格',
`delete_flag`tinyint(2)NOTNULLDEFAULT'0'COMMENT'删除标记,0未删除1已删除',
`create_time`timestampNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMPCOMMENT'创建时间',
`update_time`timestampNOTNULLDEFAULT'0000-00-000000'COMMENT'更新时间',
`create_user_code`varchar(32)COLLATEutf8mb4_binDEFAULTNULLCOMMENT'创建人',
`update_user_code`varchar(32)COLLATEutf8mb4_binDEFAULTNULLCOMMENT'更新人',
`version`int(11)NOTNULLDEFAULT'0'COMMENT'版本号',
`remark`varchar(64)COLLATEutf8mb4_binDEFAULTNULLCOMMENT'备注',
PRIMARYKEY(`id`)
)ENGINE=InnoDBAUTO_INCREMENT=6DEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_binCOMMENT='订单表';

/*Dataforthetable`tb_order`*/

insertinto`tb_order`(`id`,`order_code`,`status`,`name`,`price`,`delete_flag`,`create_time`,`update_time`,`create_user_code`,`update_user_code`,`version`,`remark`)values
(2,'A111',1,'A','22.00',0,'2022-10-151611','2022-10-022114','zhangsan','zhangsan',0,NULL),
(3,'A111',1,'订单A','22.00',0,'2022-10-022113','2022-10-022114','zhangsan','zhangsan',0,NULL),
(4,'A111',1,'订单A','22.00',0,'2022-10-022113','2022-10-022114','zhangsan','zhangsan',0,NULL),
(5,'A111',1,'订单A','22.00',0,'2022-10-030930','2022-10-022114','zhangsan','zhangsan',0,NULL);

1)引入依赖

 

org.springframework.statemachine
spring-statemachine-redis
1.2.9.RELEASE

 

org.springframework.statemachine
spring-statemachine-starter
2.0.1.RELEASE

2)定义状态机状态和事件

状态枚举:

/**
*author:芋道源码
*/
publicenumOrderStatus{
//待支付,待发货,待收货,已完成
WAIT_PAYMENT(1,"待支付"),
WAIT_DELIVER(2,"待发货"),
WAIT_RECEIVE(3,"待收货"),
FINISH(4,"已完成");
privateIntegerkey;
privateStringdesc;
OrderStatus(Integerkey,Stringdesc){
this.key=key;
this.desc=desc;
}
publicIntegergetKey(){
returnkey;
}
publicStringgetDesc(){
returndesc;
}
publicstaticOrderStatusgetByKey(Integerkey){
for(OrderStatuse:values()){
if(e.getKey().equals(key)){
returne;
}
}
thrownewRuntimeException("enumnotexists.");
}
}

事件:

/**
*author:芋道源码
*/
publicenumOrderStatusChangeEvent{
//支付,发货,确认收货
PAYED,DELIVERY,RECEIVED;
}

3)定义状态机规则和配置状态机

@Configuration
@EnableStateMachine(name="orderStateMachine")
publicclassOrderStateMachineConfigextendsStateMachineConfigurerAdapter{
/**
*配置状态
*
*@paramstates
*@throwsException
*/
publicvoidconfigure(StateMachineStateConfigurerstates)throwsException{
states
.withStates()
.initial(OrderStatus.WAIT_PAYMENT)
.states(EnumSet.allOf(OrderStatus.class));
}
/**
*配置状态转换事件关系
*
*@paramtransitions
*@throwsException
*/
publicvoidconfigure(StateMachineTransitionConfigurertransitions)throwsException{
transitions
//支付事件:待支付-》待发货
.withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED)
.and()
//发货事件:待发货-》待收货
.withExternal().source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE).event(OrderStatusChangeEvent.DELIVERY)
.and()
//收货事件:待收货-》已完成
.withExternal().source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH).event(OrderStatusChangeEvent.RECEIVED);
}
}

配置持久化:

/**
*author:芋道源码
*/
@Configuration
@Slf4j
publicclassPersist{
/**
*持久化到内存map中
*
*@return
*/
@Bean(name="stateMachineMemPersister")
publicstaticStateMachinePersistergetPersister(){
returnnewDefaultStateMachinePersister(newStateMachinePersist(){
@Override
publicvoidwrite(StateMachineContextcontext,ObjectcontextObj)throwsException{
log.info("持久化状态机,context:{},contextObj:{}",JSON.toJSONString(context),JSON.toJSONString(contextObj));
map.put(contextObj,context);
}
@Override
publicStateMachineContextread(ObjectcontextObj)throwsException{
log.info("获取状态机,contextObj:{}",JSON.toJSONString(contextObj));
StateMachineContextstateMachineContext=(StateMachineContext)map.get(contextObj);
log.info("获取状态机结果,stateMachineContext:{}",JSON.toJSONString(stateMachineContext));
returnstateMachineContext;
}
privateMapmap=newHashMap();
});
}

@Resource
privateRedisConnectionFactoryredisConnectionFactory;
/**
*持久化到redis中,在分布式系统中使用
*
*@return
*/
@Bean(name="stateMachineRedisPersister")
publicRedisStateMachinePersistergetRedisPersister(){
RedisStateMachineContextRepositoryrepository=newRedisStateMachineContextRepository<>(redisConnectionFactory);
RepositoryStateMachinePersistp=newRepositoryStateMachinePersist<>(repository);
returnnewRedisStateMachinePersister<>(p);
}
}

4)业务系统

controller:

/**
*author:芋道源码
*/
@RestController
@RequestMapping("/order")
publicclassOrderController{
@Resource
privateOrderServiceorderService;
/**
*根据id查询订单
*
*@return
*/
@RequestMapping("/getById")
publicOrdergetById(@RequestParam("id")Longid){
//根据id查询订单
Orderorder=orderService.getById(id);
returnorder;
}
/**
*创建订单
*
*@return
*/
@RequestMapping("/create")
publicStringcreate(@RequestBodyOrderorder){
//创建订单
orderService.create(order);
return"sucess";
}
/**
*对订单进行支付
*
*@paramid
*@return
*/
@RequestMapping("/pay")
publicStringpay(@RequestParam("id")Longid){
//对订单进行支付
orderService.pay(id);
return"success";
}

/**
*对订单进行发货
*
*@paramid
*@return
*/
@RequestMapping("/deliver")
publicStringdeliver(@RequestParam("id")Longid){
//对订单进行确认收货
orderService.deliver(id);
return"success";
}
/**
*对订单进行确认收货
*
*@paramid
*@return
*/
@RequestMapping("/receive")
publicStringreceive(@RequestParam("id")Longid){
//对订单进行确认收货
orderService.receive(id);
return"success";
}
}

servie:

/**
*author:芋道源码
*/
@Service("orderService")
@Slf4j
publicclassOrderServiceImplextendsServiceImplimplementsOrderService{
@Resource
privateStateMachineorderStateMachine;
@Resource
privateStateMachinePersisterstateMachineMemPersister;
@Resource
privateOrderMapperorderMapper;
/**
*创建订单
*
*@paramorder
*@return
*/
publicOrdercreate(Orderorder){
order.setStatus(OrderStatus.WAIT_PAYMENT.getKey());
orderMapper.insert(order);
returnorder;
}
/**
*对订单进行支付
*
*@paramid
*@return
*/
publicOrderpay(Longid){
Orderorder=orderMapper.selectById(id);
log.info("线程名称:{},尝试支付,订单号:{}",Thread.currentThread().getName(),id);
if(!sendEvent(OrderStatusChangeEvent.PAYED,order)){
log.error("线程名称:{},支付失败,状态异常,订单信息:{}",Thread.currentThread().getName(),order);
thrownewRuntimeException("支付失败,订单状态异常");
}
returnorder;
}
/**
*对订单进行发货
*
*@paramid
*@return
*/
publicOrderdeliver(Longid){
Orderorder=orderMapper.selectById(id);
log.info("线程名称:{},尝试发货,订单号:{}",Thread.currentThread().getName(),id);
if(!sendEvent(OrderStatusChangeEvent.DELIVERY,order)){
log.error("线程名称:{},发货失败,状态异常,订单信息:{}",Thread.currentThread().getName(),order);
thrownewRuntimeException("发货失败,订单状态异常");
}
returnorder;
}
/**
*对订单进行确认收货
*
*@paramid
*@return
*/
publicOrderreceive(Longid){
Orderorder=orderMapper.selectById(id);
log.info("线程名称:{},尝试收货,订单号:{}",Thread.currentThread().getName(),id);
if(!sendEvent(OrderStatusChangeEvent.RECEIVED,order)){
log.error("线程名称:{},收货失败,状态异常,订单信息:{}",Thread.currentThread().getName(),order);
thrownewRuntimeException("收货失败,订单状态异常");
}
returnorder;
}
/**
*发送订单状态转换事件
*synchronized修饰保证这个方法是线程安全的
*
*@paramchangeEvent
*@paramorder
*@return
*/
privatesynchronizedbooleansendEvent(OrderStatusChangeEventchangeEvent,Orderorder){
booleanresult=false;
try{
//启动状态机
orderStateMachine.start();
//尝试恢复状态机状态
stateMachineMemPersister.restore(orderStateMachine,String.valueOf(order.getId()));
Messagemessage=MessageBuilder.withPayload(changeEvent).setHeader("order",order).build();
result=orderStateMachine.sendEvent(message);
//持久化状态机状态
stateMachineMemPersister.persist(orderStateMachine,String.valueOf(order.getId()));
}catch(Exceptione){
log.error("订单操作失败:{}",e);
}finally{
orderStateMachine.stop();
}
returnresult;
}
}

监听状态的变化:

/**
*author:芋道源码
*/
@Component("orderStateListener")
@WithStateMachine(name="orderStateMachine")
@Slf4j
publicclassOrderStateListenerImpl{
@Resource
privateOrderMapperorderMapper;

@OnTransition(source="WAIT_PAYMENT",target="WAIT_DELIVER")
publicvoidpayTransition(Messagemessage){
Orderorder=(Order)message.getHeaders().get("order");
log.info("支付,状态机反馈信息:{}",message.getHeaders().toString());
//更新订单
order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
orderMapper.updateById(order);
//TODO其他业务
}
@OnTransition(source="WAIT_DELIVER",target="WAIT_RECEIVE")
publicvoiddeliverTransition(Messagemessage){
Orderorder=(Order)message.getHeaders().get("order");
log.info("发货,状态机反馈信息:{}",message.getHeaders().toString());
//更新订单
order.setStatus(OrderStatus.WAIT_RECEIVE.getKey());
orderMapper.updateById(order);
//TODO其他业务
}
@OnTransition(source="WAIT_RECEIVE",target="FINISH")
publicvoidreceiveTransition(Messagemessage){
Orderorder=(Order)message.getHeaders().get("order");
log.info("确认收货,状态机反馈信息:{}",message.getHeaders().toString());
//更新订单
order.setStatus(OrderStatus.FINISH.getKey());
orderMapper.updateById(order);
//TODO其他业务
}
}

3.3 测试验证

1)验证业务

新增一个订单

http://localhost:8084/order/create

对订单进行支付

http://localhost:8084/order/pay?id=2

对订单进行发货

http://localhost:8084/order/deliver?id=2

对订单进行确认收货

http://localhost:8084/order/receive?id=2

正常流程结束。如果对一个订单进行支付了,再次进行支付,则会报错:http://localhost:8084/order/pay?id=2

报错如下:

ef14c922-f84b-11ed-90ce-dac502259ad0.jpg

2)验证持久化

内存

使用内存持久化类持久化:

/**
*author:芋道源码
*/
@Resource
privateStateMachinePersisterstateMachineMemPersister;

/**
*发送订单状态转换事件
*synchronized修饰保证这个方法是线程安全的
*
*@paramchangeEvent
*@paramorder
*@return
*/
privatesynchronizedbooleansendEvent(OrderStatusChangeEventchangeEvent,Orderorder){
booleanresult=false;
try{
//启动状态机
orderStateMachine.start();
//尝试恢复状态机状态
stateMachineMemPersister.restore(orderStateMachine,String.valueOf(order.getId()));
Messagemessage=MessageBuilder.withPayload(changeEvent).setHeader("order",order).build();
result=orderStateMachine.sendEvent(message);
//持久化状态机状态
stateMachineMemPersister.persist(orderStateMachine,String.valueOf(order.getId()));
}catch(Exceptione){
log.error("订单操作失败:{}",e);
}finally{
orderStateMachine.stop();
}
returnresult;
}

redis持久化

引入依赖:

 

org.springframework.statemachine
spring-statemachine-redis
1.2.9.RELEASE

配置yaml:

spring:
redis:
database:0
host:localhost
jedis:
pool:
max-active:8
max-idle:8
max-wait:''
min-idle:0
password:''
port:6379
timeout:0

使用redis持久化类持久化:

/**
*author:芋道源码
*/
@Resource
privateStateMachinePersisterstateMachineRedisPersister;

/**
*发送订单状态转换事件
*synchronized修饰保证这个方法是线程安全的
*
*@paramchangeEvent
*@paramorder
*@return
*/
privatesynchronizedbooleansendEvent(OrderStatusChangeEventchangeEvent,Orderorder){
booleanresult=false;
try{
//启动状态机
orderStateMachine.start();
//尝试恢复状态机状态
stateMachineRedisPersister.restore(orderStateMachine,String.valueOf(order.getId()));
Messagemessage=MessageBuilder.withPayload(changeEvent).setHeader("order",order).build();
result=orderStateMachine.sendEvent(message);
//持久化状态机状态
stateMachineRedisPersister.persist(orderStateMachine,String.valueOf(order.getId()));
}catch(Exceptione){
log.error("订单操作失败:{}",e);
}finally{
orderStateMachine.stop();
}
returnresult;
}

3.4 状态机存在的问题

1)stateMachine无法抛出异常,异常会被状态机给消化掉

问题现象

从orderStateMachine.sendEvent(message);获取的结果无法感知到。无论执行正常还是抛出异常,都返回true。

@Resource
privateOrderMapperorderMapper;

@Resource
privateStateMachineorderStateMachine;

@OnTransition(source="WAIT_PAYMENT",target="WAIT_DELIVER")
@Transactional(rollbackFor=Exception.class)
publicvoidpayTransition(Messagemessage){
Orderorder=(Order)message.getHeaders().get("order");
log.info("支付,状态机反馈信息:{}",message.getHeaders().toString());
try{
//更新订单
order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
orderMapper.updateById(order);
//TODO其他业务
//模拟异常
if(Objects.equals(order.getName(),"A")){
thrownewRuntimeException("执行业务异常");
}
}catch(Exceptione){
//如果出现异常,记录异常信息,抛出异常信息进行回滚
log.error("payTransition出现异常:{}",e);
throwe;
}
}

监听事件抛出异常,在发送事件中无法感知:

privatesynchronizedbooleansendEvent(OrderStatusChangeEventchangeEvent,Orderorder){
booleanresult=false;
try{
//启动状态机
orderStateMachine.start();
//尝试恢复状态机状态
stateMachineMemPersister.restore(orderStateMachine,String.valueOf(order.getId()));
Messagemessage=MessageBuilder.withPayload(changeEvent).setHeader("order",order).build();
//事件执行异常了,依然返回true,无法感知异常
result=orderStateMachine.sendEvent(message);
if(result){
//持久化状态机状态,如果根据true持久化,则会出现问题
stateMachineMemPersister.persist(orderStateMachine,String.valueOf(order.getId()));
}
}catch(Exceptione){
log.error("订单操作失败:{}",e);
}finally{
orderStateMachine.stop();
}
returnresult;
}

调试发现:发送事件和监听事件是一个线程,发送事件的结果是在监听操作执行完之后才返回

ef1b945a-f84b-11ed-90ce-dac502259ad0.jpg

监听线程:

ef278292-f84b-11ed-90ce-dac502259ad0.jpg

解决方案:自己保存异常到数据库或者内存中,进行判断

也可以通过接口:org.springframework.statemachine.StateMachine##getExtendedState

方法把执行状态放入这个变量中

publicinterfaceExtendedState{
MapgetVariables();
Tget(Objectvar1,Classvar2);
voidsetExtendedStateChangeListener(ExtendedState.ExtendedStateChangeListenervar1);
publicinterfaceExtendedStateChangeListener{
voidchanged(Objectvar1,Objectvar2);
}
}

org.springframework.statemachine.support.DefaultExtendedState##getVariables

privatefinalMapvariables;

publicDefaultExtendedState(){
this.variables=newObservableMap(newConcurrentHashMap(),newDefaultExtendedState.LocalMapChangeListener());
}

publicMapgetVariables(){
returnthis.variables;
}

改造监听状态:把业务的执行结果进行保存,1成功,0失败

@Resource
privateOrderMapperorderMapper;
@Resource
privateStateMachineorderStateMachine;

@OnTransition(source="WAIT_PAYMENT",target="WAIT_DELIVER")
@Transactional(rollbackFor=Exception.class)
publicvoidpayTransition(Messagemessage){
Orderorder=(Order)message.getHeaders().get("order");
log.info("支付,状态机反馈信息:{}",message.getHeaders().toString());
try{
//更新订单
order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
orderMapper.updateById(order);
//TODO其他业务
//模拟异常
if(Objects.equals(order.getName(),"A")){
thrownewRuntimeException("执行业务异常");
}
//成功则为1
orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);
}catch(Exceptione){
//如果出现异常,则进行回滚
log.error("payTransition出现异常:{}",e);
//将异常信息变量信息中,失败则为0
orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),0);
throwe;
}
}

发送事件改造:如果获取到业务执行异常,则返回失败,不进行状态机持久化 com.zengqingfa.springboot.state.demo.service.impl.OrderServiceImpl##sendEvent

@Resource
privateStateMachineorderStateMachine;
@Resource
privateStateMachinePersisterstateMachineMemPersister;

/**
*发送订单状态转换事件
*synchronized修饰保证这个方法是线程安全的
*
*@paramchangeEvent
*@paramorder
*@return
*/
privatesynchronizedbooleansendEvent(OrderStatusChangeEventchangeEvent,Orderorder){
booleanresult=false;
try{
//启动状态机
orderStateMachine.start();
//尝试恢复状态机状态
stateMachineMemPersister.restore(orderStateMachine,String.valueOf(order.getId()));
Messagemessage=MessageBuilder.withPayload(changeEvent).setHeader("order",order).build();
result=orderStateMachine.sendEvent(message);
if(!result){
returnfalse;
}
//获取到监听的结果信息
Integero=(Integer)orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition+order.getId());
//操作完成之后,删除本次对应的key信息
orderStateMachine.getExtendedState().getVariables().remove(CommonConstants.payTransition+order.getId());
//如果事务执行成功,则持久化状态机
if(Objects.equals(1,Integer.valueOf(o))){
//持久化状态机状态
stateMachineMemPersister.persist(orderStateMachine,String.valueOf(order.getId()));
}else{
//订单执行业务异常
returnfalse;
}
}catch(Exceptione){
log.error("订单操作失败:{}",e);
}finally{
orderStateMachine.stop();
}
returnresult;
}

代码优化

发送事件只针对了支付,如果是非支付事件呢?

//获取到监听的结果信息
Integero=(Integer)orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition+order.getId());

监听设置状态的代码有重复代码,需要进行优化,可使用aop

try{
//TODO其他业务
//成功则为1
orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);
}catch(Exceptione){
//如果出现异常,则进行回滚
log.error("payTransition出现异常:{}",e);
//将异常信息变量信息中,失败则为0
orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),0);
throwe;
}

常量类:

publicinterfaceCommonConstants{
StringorderHeader="order";
StringpayTransition="payTransition";
StringdeliverTransition="deliverTransition";
StringreceiveTransition="receiveTransition";
}

支付发送事件:com.zengqingfa.springboot.state.demo.service.impl.OrderServiceImpl##pay

@Resource
privateStateMachineorderStateMachine;
@Resource
privateStateMachinePersisterstateMachineMemPersister;
@Resource
privateOrderMapperorderMapper;

/**
*对订单进行支付
*
*@paramid
*@return
*/
publicOrderpay(Longid){
Orderorder=orderMapper.selectById(id);
log.info("线程名称:{},尝试支付,订单号:{}",Thread.currentThread().getName(),id);
if(!sendEvent(OrderStatusChangeEvent.PAYED,order,CommonConstants.payTransition)){
log.error("线程名称:{},支付失败,状态异常,订单信息:{}",Thread.currentThread().getName(),order);
thrownewRuntimeException("支付失败,订单状态异常");
}
returnorder;
}

/**
*发送订单状态转换事件
*synchronized修饰保证这个方法是线程安全的
*
*@paramchangeEvent
*@paramorder
*@return
*/
privatesynchronizedbooleansendEvent(OrderStatusChangeEventchangeEvent,Orderorder,Stringkey){
booleanresult=false;
try{
//启动状态机
orderStateMachine.start();
//尝试恢复状态机状态
stateMachineMemPersister.restore(orderStateMachine,String.valueOf(order.getId()));
Messagemessage=MessageBuilder.withPayload(changeEvent).setHeader("order",order).build();
result=orderStateMachine.sendEvent(message);
if(!result){
returnfalse;
}
//获取到监听的结果信息
Integero=(Integer)orderStateMachine.getExtendedState().getVariables().get(key+order.getId());
//操作完成之后,删除本次对应的key信息
orderStateMachine.getExtendedState().getVariables().remove(key+order.getId());
//如果事务执行成功,则持久化状态机
if(Objects.equals(1,Integer.valueOf(o))){
//持久化状态机状态
stateMachineMemPersister.persist(orderStateMachine,String.valueOf(order.getId()));
}else{
//订单执行业务异常
returnfalse;
}
}catch(Exceptione){
log.error("订单操作失败:{}",e);
}finally{
orderStateMachine.stop();
}
returnresult;
}

使用aop对监听事件切面,把业务执行结果封装到状态机的变量中,注解:

@Retention(RetentionPolicy.RUNTIME)
public@interfaceLogResult{
/**
*执行的业务key
*
*@returnString
*/
Stringkey();
}

切面:

@Component
@Aspect
@Slf4j
publicclassLogResultAspect{

//拦截LogHistory注解
@Pointcut("@annotation(com.zengqingfa.springboot.state.demo.aop.annotation.LogResult)")
privatevoidlogResultPointCut(){
//logResultPointCut日志注解切点
}
@Resource
privateStateMachineorderStateMachine;

@Around("logResultPointCut()")
publicObjectlogResultAround(ProceedingJoinPointpjp)throwsThrowable{
//获取参数
Object[]args=pjp.getArgs();
log.info("参数args:{}",args);
Messagemessage=(Message)args[0];
Orderorder=(Order)message.getHeaders().get("order");
//获取方法
Methodmethod=((MethodSignature)pjp.getSignature()).getMethod();
//获取LogHistory注解
LogResultlogResult=method.getAnnotation(LogResult.class);
Stringkey=logResult.key();
ObjectreturnVal=null;
try{
//执行方法
returnVal=pjp.proceed();
//如果业务执行正常,则保存信息
//成功则为1
orderStateMachine.getExtendedState().getVariables().put(key+order.getId(),1);
}catch(Throwablee){
log.error("e:{}",e.getMessage());
//如果业务执行异常,则保存信息
//将异常信息变量信息中,失败则为0
orderStateMachine.getExtendedState().getVariables().put(key+order.getId(),0);
throwe;
}
returnreturnVal;
}
}

监听类使用注解:

@Component("orderStateListener")
@WithStateMachine(name="orderStateMachine")
@Slf4j
publicclassOrderStateListenerImpl{
@Resource
privateOrderMapperorderMapper;

@OnTransition(source="WAIT_PAYMENT",target="WAIT_DELIVER")
@Transactional(rollbackFor=Exception.class)
@LogResult(key=CommonConstants.payTransition)
publicvoidpayTransition(Messagemessage){
Orderorder=(Order)message.getHeaders().get("order");
log.info("支付,状态机反馈信息:{}",message.getHeaders().toString());
//更新订单
order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
orderMapper.updateById(order);
//TODO其他业务
//模拟异常
if(Objects.equals(order.getName(),"A")){
thrownewRuntimeException("执行业务异常");
}
}
@OnTransition(source="WAIT_DELIVER",target="WAIT_RECEIVE")
@LogResult(key=CommonConstants.deliverTransition)
publicvoiddeliverTransition(Messagemessage){
Orderorder=(Order)message.getHeaders().get("order");
log.info("发货,状态机反馈信息:{}",message.getHeaders().toString());
//更新订单
order.setStatus(OrderStatus.WAIT_RECEIVE.getKey());
orderMapper.updateById(order);
//TODO其他业务
}
@OnTransition(source="WAIT_RECEIVE",target="FINISH")
@LogResult(key=CommonConstants.receiveTransition)
publicvoidreceiveTransition(Messagemessage){
Orderorder=(Order)message.getHeaders().get("order");
log.info("确认收货,状态机反馈信息:{}",message.getHeaders().toString());
//更新订单
order.setStatus(OrderStatus.FINISH.getKey());
orderMapper.updateById(order);
//TODO其他业务
}
}
审核编辑:彭静
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编程
    +关注

    关注

    88

    文章

    3441

    浏览量

    92404
  • spring
    +关注

    关注

    0

    文章

    332

    浏览量

    14161
  • 数学模型
    +关注

    关注

    0

    文章

    62

    浏览量

    11875
  • 自动机
    +关注

    关注

    1

    文章

    27

    浏览量

    9213

原文标题:项目终于用上了Spring状态机,非常优雅!

文章出处:【微信号:芋道源码,微信公众号:芋道源码】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Spring状态机的实现原理和使用方法

    说起 Spring 状态机,大家很容易联想到这个状态机和设计模式中状态模式的区别是啥呢?没错,Spring
    的头像 发表于 12-26 09:39 972次阅读
    <b class='flag-5'>Spring</b><b class='flag-5'>状态机</b>的实现原理和使用方法

    状态机编程

    状态机编程基于状态机的按键输入软件接口设计一般的教课书中给出的按键输入软件接口程序通常非常简单,在程序中一旦检测到按键输入口为低电平时(图9-2),便采用(调用)软件延时程序延时10ms。然后再
    发表于 07-10 18:00

    如何写好状态机

    一篇经典文献,详细讲解了一段、两段、三段式状态机的实现,效率、优缺点。看完后相信会对状态机有一个详细的了解。 状态机是逻辑设计的重要内容,状态机的设计水平直接反应工程师的逻辑功底,所以
    发表于 10-24 11:43

    状态机

    控制状态机控制状态机的初始化和状态转换的最佳方法是使用枚丽型输入控件。一般使用自定义类型的枚丽变量。使用子定义类型的枚丽变量可以是控件和实例乊间存在关联,使得添加或删除
    发表于 02-13 12:39

    状态机如何暂停

    程序一运行 就开始自动运行程序 状态机各种各种状态开始执行 我这里是布尔变量 每一秒点亮一个布尔按钮。我现在想在界面增加一个暂停按钮 当点暂停时候 此时暂停按钮文字成为继续 如果 状态机执行第二步
    发表于 04-09 09:23

    状态机跳跃错误的解决办法?

    大家好,我对状态机有疑问。我写了一个状态机来实现一些功能,设备是virtex-ii。机器中有26个状态,其中4个是A,B,C,D,E;输入信号为rdy_i和smp_rdy_i,输出信号为p_o
    发表于 07-08 10:51

    状态机是什么?什么是消息触发类型的状态机

    状态机可归纳为哪几个要素?状态机可分为哪几种?什么是消息触发类型的状态机
    发表于 04-19 06:02

    什么是状态机状态机是如何编程的?

    什么是状态机状态机是如何编程的?
    发表于 10-20 07:43

    什么是状态机

    一. 什么是状态机我们以生活中的小区的停车系统为例:停车杆一般没车的是不动的(初态),有车来的时候需要抬杆(状态1),车通过需要放杆(状态2),如果在放杆的过程中突然有车,又需要抬杆(状态
    发表于 01-06 08:01

    什么是状态机

    目录1 前言2 状态机2.1 什么是状态机2.2 状态机的概念2.3 使用状态机写键盘的思路3 代码实例3.1 使用软件3.2 protues电路图3.2
    发表于 01-24 06:23

    状态机原理及用法

    状态机原理及用法状态机原理及用法状态机原理及用法
    发表于 03-15 15:25 0次下载

    状态机概述 如何理解状态机

    本篇文章包括状态机的基本概述以及通过简单的实例理解状态机
    的头像 发表于 01-02 18:03 1w次阅读
    <b class='flag-5'>状态机</b>概述  如何理解<b class='flag-5'>状态机</b>

    FPGA:状态机简述

    本文目录 前言 状态机简介 状态机分类 Mealy 型状态机 Moore 型状态机 状态机描述 一段式
    的头像 发表于 11-05 17:58 6233次阅读
    FPGA:<b class='flag-5'>状态机</b>简述

    什么是状态机状态机5要素

    等。 本文来说一下状态机编程。 什么是状态机状态机(state machine)有5个要素: 状态(state) 迁移(transition) 事件(event) 动作(actio
    的头像 发表于 07-27 11:23 1.9w次阅读
    什么是<b class='flag-5'>状态机</b>?<b class='flag-5'>状态机</b>5要素

    什么是状态机状态机的种类与实现

    状态机,又称有限状态机(Finite State Machine,FSM)或米利状态机(Mealy Machine),是一种描述系统状态变化的模型。在芯片设计中,
    的头像 发表于 10-19 10:27 5037次阅读