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其他业务
}
}
审核编辑:彭静
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编程
    +关注

    关注

    90

    文章

    3707

    浏览量

    96765
  • spring
    +关注

    关注

    0

    文章

    341

    浏览量

    15776
  • 数学模型
    +关注

    关注

    0

    文章

    83

    浏览量

    12397
  • 自动机
    +关注

    关注

    1

    文章

    28

    浏览量

    9578

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

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

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

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

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

    Verilog状态机+设计实例

    在verilog中状态机的一种很常用的逻辑结构,学习和理解状态机的运行规律能够帮助我们更好地书写代码,同时作为一种思想方法,在别的代码设计中也会有所帮助。 一、简介 在使用过程中我们常说
    的头像 发表于 02-12 19:07 5898次阅读
    Verilog<b class='flag-5'>状态机</b>+设计实例

    玩转Spring状态机

    说起Spring状态机,大家很容易联想到这个状态机和设计模式中状态模式的区别是啥呢?没错,Spring
    的头像 发表于 06-25 14:21 1519次阅读
    玩转<b class='flag-5'>Spring</b><b class='flag-5'>状态机</b>

    如何写好状态机

    如何写好状态机:状态机是逻辑设计的重要内容,状态机的设计水平直接反应工程师的逻辑功底,所以许多公司的硬件和逻辑工程师面试中,状态机设计几乎是必选题目。本章在引入
    发表于 06-14 19:24 98次下载

    状态机举例

    状态机举例 你可以指定状态寄存器和状态机状态。以下是一个有四种状态的普通状态机。 // Th
    发表于 03-28 15:18 1171次阅读

    状态机代码生成工具

    状态机代码生成工具状态机代码生成工具状态机代码生成工具状态机代码生成工具
    发表于 11-19 15:12 9次下载

    状态机原理及用法

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

    简述使用QII状态机向导如何创建一个状态机

    如何使用QII状态机向导创建一个状态机
    的头像 发表于 06-20 00:11 4822次阅读
    简述使用QII<b class='flag-5'>状态机</b>向导如何创建一个<b class='flag-5'>状态机</b>

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

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

    什么是状态机 状态机的描述三种方法

    状态机 1、状态机是许多数字系统的核心部件,是一类重要的时序逻辑电路。通常包括三个部分:一是下一个状态的逻辑电路,二是存储状态机当前状态的时
    的头像 发表于 11-16 17:39 2.8w次阅读

    FPGA:状态机简述

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

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

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

    状态模式(状态机)

    以前写状态机,比较常用的方式是用 if-else 或 switch-case,高级的一点是函数指针列表。最近,看了一文章《c语言设计模式–状态模式(状态机)》(来源:embed linux
    发表于 12-16 16:53 9次下载
    <b class='flag-5'>状态</b>模式(<b class='flag-5'>状态机</b>)

    labview状态机分享

    labview状态机
    发表于 10-31 15:50 20次下载

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

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