一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Spring狀態(tài)機存在的問題

jf_ro2CN3Fa ? 來源:芋道源碼 ? 2023-05-22 11:12 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

1、什么是狀態(tài)機

1.1 什么是狀態(tài)

1.2 四大概念

1.3 狀態(tài)機

2、狀態(tài)機圖

3、spring statemachine

3.1 狀態(tài)機spring statemachine 概述

3.2 快速開始

3.3 測試驗證

3.4 狀態(tài)機存在的問題

1、什么是狀態(tài)機

1.1 什么是狀態(tài)

先來解釋什么是“狀態(tài)”( State )?,F(xiàn)實事物是有不同狀態(tài)的,例如一個自動門,就有 open 和 closed 兩種狀態(tài)。我們通常所說的狀態(tài)機是有限狀態(tài)機,也就是被描述的事物的狀態(tài)的數(shù)量是有限個,例如自動門的狀態(tài)就是兩個 open 和 closed 。

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

狀態(tài)機,也就是 State Machine ,不是指一臺實際機器,而是指一個數(shù)學(xué)模型。說白了,一般就是指一張狀態(tài)轉(zhuǎn)換圖。例如,根據(jù)自動門的運行規(guī)則,我們可以抽象出下面這么一個圖。

自動門有兩個狀態(tài),open 和 closed ,closed 狀態(tài)下,如果讀取開門信號,那么狀態(tài)就會切換為 open 。open 狀態(tài)下如果讀取關(guān)門信號,狀態(tài)就會切換為 closed 。

狀態(tài)機的全稱是有限狀態(tài)自動機,自動兩個字也是包含重要含義的。給定一個狀態(tài)機,同時給定它的當(dāng)前狀態(tài)以及輸入,那么輸出狀態(tài)時可以明確的運算出來的。例如對于自動門,給定初始狀態(tài) closed ,給定輸入“開門”,那么下一個狀態(tài)時可以運算出來的。

這樣狀態(tài)機的基本定義我們就介紹完畢了。重復(fù)一下:狀態(tài)機是有限狀態(tài)自動機的簡稱,是現(xiàn)實事物運行規(guī)則抽象而成的一個數(shù)學(xué)模型。

1.2 四大概念

下面來給出狀態(tài)機的四大概念。

第一個是 State ,狀態(tài)。一個狀態(tài)機至少要包含兩個狀態(tài)。例如上面自動門的例子,有 open 和 closed 兩個狀態(tài)。

第二個是 Event ,事件。事件就是執(zhí)行某個操作的觸發(fā)條件或者口令。對于自動門,“按下開門按鈕”就是一個事件。

第三個是 Action ,動作。事件發(fā)生以后要執(zhí)行動作。例如事件是“按開門按鈕”,動作是“開門”。編程的時候,一個 Action一般就對應(yīng)一個函數(shù)。

第四個是 Transition ,變換。也就是從一個狀態(tài)變化為另一個狀態(tài)。例如“開門過程”就是一個變換。

1.3 狀態(tài)機

有限狀態(tài)機(Finite-state machine,FSM),又稱有限狀態(tài)自動機,簡稱狀態(tài)機,是表示有限個狀態(tài)以及在這些狀態(tài)之間的轉(zhuǎn)移和動作等行為的數(shù)學(xué)模型。

FSM是一種算法思想,簡單而言,有限狀態(tài)機由一組狀態(tài)、一個初始狀態(tài)、輸入和根據(jù)輸入及現(xiàn)有狀態(tài)轉(zhuǎn)換為下一個狀態(tài)的轉(zhuǎn)換函數(shù)組成。

其作用主要是描述對象在它的生命周期內(nèi)所經(jīng)歷的狀態(tài)序列,以及如何響應(yīng)來自外界的各種事件。

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

基于 Spring Boot + MyBatis Plus + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

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

視頻教程:https://doc.iocoder.cn/video/

2、狀態(tài)機圖

做需求時,需要了解以下六種元素:起始、終止、現(xiàn)態(tài)、次態(tài)(目標(biāo)狀態(tài))、動作、條件,我們就可以完成一個狀態(tài)機圖了:

以訂單為例:以從待支付狀態(tài)轉(zhuǎn)換為待發(fā)貨狀態(tài)為例

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

①現(xiàn)態(tài):是指當(dāng)前所處的狀態(tài)。待支付

②條件:又稱為“事件”,當(dāng)一個條件被滿足,將會觸發(fā)一個動作,或者執(zhí)行一次狀態(tài)的遷移。支付事件

③動作:條件滿足后執(zhí)行的動作。動作執(zhí)行完畢后,可以遷移到新的狀態(tài),也可以仍舊保持原狀態(tài)。動作不是必需的,當(dāng)條件滿足后,也可以不執(zhí)行任何動作,直接遷移到新狀態(tài)。狀態(tài)轉(zhuǎn)換為待發(fā)貨

④次態(tài):條件滿足后要遷往的新狀態(tài)。“次態(tài)”是相對于“現(xiàn)態(tài)”而言的,“次態(tài)”一旦被激活,就轉(zhuǎn)變成新的“現(xiàn)態(tài)”了。待發(fā)貨 注意事項

1、避免把某個“程序動作”當(dāng)作是一種“狀態(tài)”來處理。那么如何區(qū)分“動作”和“狀態(tài)”?“動作”是不穩(wěn)定的,即使沒有條件的觸發(fā),“動作”一旦執(zhí)行完畢就結(jié)束了;而“狀態(tài)”是相對穩(wěn)定的,如果沒有外部條件的觸發(fā),一個狀態(tài)會一直持續(xù)下去。關(guān)注工眾號:碼猿技術(shù)專欄,回復(fù)關(guān)鍵詞:1111 獲取阿里內(nèi)部Java性能調(diào)優(yōu)手冊!

2、狀態(tài)劃分時漏掉一些狀態(tài),導(dǎo)致跳轉(zhuǎn)邏輯不完整。所以在設(shè)計狀態(tài)機時,我們需要反復(fù)的查看設(shè)計的狀態(tài)圖或者狀態(tài)表,最終達到一種牢不可破的設(shè)計方案。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

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

視頻教程:https://doc.iocoder.cn/video/

3、spring statemachine

3.1 狀態(tài)機spring statemachine 概述

Spring Statemachine是應(yīng)用程序開發(fā)人員在Spring應(yīng)用程序中使用狀態(tài)機概念的框架

Spring Statemachine旨在提供以下功能:

易于使用的扁平單級狀態(tài)機,用于簡單的使用案例。

分層狀態(tài)機結(jié)構(gòu),以簡化復(fù)雜的狀態(tài)配置。

狀態(tài)機區(qū)域提供更復(fù)雜的狀態(tài)配置。

使用觸發(fā)器,轉(zhuǎn)換,警衛(wèi)和操作。

鍵入安全配置適配器。

生成器模式,用于在Spring Application上下文之外使用的簡單實例化通常用例的食譜

基于Zookeeper的分布式狀態(tài)機

狀態(tài)機事件監(jiān)聽器。

UML Eclipse Papyrus建模。

將計算機配置存儲在永久存儲中。

Spring IOC集成將bean與狀態(tài)機關(guān)聯(lián)起來。

狀態(tài)機功能強大,因為行為始終保證一致,使調(diào)試相對容易。這是因為操作規(guī)則是在機器啟動時寫成的。這個想法是你的應(yīng)用程序可能存在于有限數(shù)量的狀態(tài)中,某些預(yù)定義的觸發(fā)器可以將你的應(yīng)用程序從一個狀態(tài)轉(zhuǎn)移到另一個狀態(tài)。此類觸發(fā)器可以基于事件或計時器。

在應(yīng)用程序之外定義高級邏輯然后依靠狀態(tài)機來管理狀態(tài)要容易得多。您可以通過發(fā)送事件,偵聽更改或僅請求當(dāng)前狀態(tài)來與狀態(tài)機進行交互。

官網(wǎng):spring.io/projects/sp…

3.2 快速開始

以訂單狀態(tài)扭轉(zhuǎn)的例子為例:

表結(jié)構(gòu)設(shè)計如下:

CREATETABLE`tb_order`(
`id`bigint(20)unsignedNOTNULLAUTO_INCREMENTCOMMENT'主鍵ID',
`order_code`varchar(128)COLLATEutf8mb4_binDEFAULTNULLCOMMENT'訂單編碼',
`status`smallint(3)DEFAULTNULLCOMMENT'訂單狀態(tài)',
`name`varchar(64)COLLATEutf8mb4_binDEFAULTNULLCOMMENT'訂單名稱',
`price`decimal(12,2)DEFAULTNULLCOMMENT'價格',
`delete_flag`tinyint(2)NOTNULLDEFAULT'0'COMMENT'刪除標(biāo)記,0未刪除1已刪除',
`create_time`timestampNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMPCOMMENT'創(chuàng)建時間',
`update_time`timestampNOTNULLDEFAULT'0000-00-000000'COMMENT'更新時間',
`create_user_code`varchar(32)COLLATEutf8mb4_binDEFAULTNULLCOMMENT'創(chuàng)建人',
`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)定義狀態(tài)機狀態(tài)和事件

狀態(tài)枚舉:

/**
*author:芋道源碼
*/
publicenumOrderStatus{
//待支付,待發(fā)貨,待收貨,已完成
WAIT_PAYMENT(1,"待支付"),
WAIT_DELIVER(2,"待發(fā)貨"),
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{
//支付,發(fā)貨,確認(rèn)收貨
PAYED,DELIVERY,RECEIVED;
}

3)定義狀態(tài)機規(guī)則和配置狀態(tài)機

@Configuration
@EnableStateMachine(name="orderStateMachine")
publicclassOrderStateMachineConfigextendsStateMachineConfigurerAdapter{
/**
*配置狀態(tài)
*
*@paramstates
*@throwsException
*/
publicvoidconfigure(StateMachineStateConfigurerstates)throwsException{
states
.withStates()
.initial(OrderStatus.WAIT_PAYMENT)
.states(EnumSet.allOf(OrderStatus.class));
}
/**
*配置狀態(tài)轉(zhuǎn)換事件關(guān)系
*
*@paramtransitions
*@throwsException
*/
publicvoidconfigure(StateMachineTransitionConfigurertransitions)throwsException{
transitions
//支付事件:待支付-》待發(fā)貨
.withExternal().source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER).event(OrderStatusChangeEvent.PAYED)
.and()
//發(fā)貨事件:待發(fā)貨-》待收貨
.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{
/**
*持久化到內(nèi)存map中
*
*@return
*/
@Bean(name="stateMachineMemPersister")
publicstaticStateMachinePersistergetPersister(){
returnnewDefaultStateMachinePersister(newStateMachinePersist(){
@Override
publicvoidwrite(StateMachineContextcontext,ObjectcontextObj)throwsException{
log.info("持久化狀態(tài)機,context:{},contextObj:{}",JSON.toJSONString(context),JSON.toJSONString(contextObj));
map.put(contextObj,context);
}
@Override
publicStateMachineContextread(ObjectcontextObj)throwsException{
log.info("獲取狀態(tài)機,contextObj:{}",JSON.toJSONString(contextObj));
StateMachineContextstateMachineContext=(StateMachineContext)map.get(contextObj);
log.info("獲取狀態(tài)機結(jié)果,stateMachineContext:{}",JSON.toJSONString(stateMachineContext));
returnstateMachineContext;
}
privateMapmap=newHashMap();
});
}

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

4)業(yè)務(wù)系統(tǒng)

controller:

/**
*author:芋道源碼
*/
@RestController
@RequestMapping("/order")
publicclassOrderController{
@Resource
privateOrderServiceorderService;
/**
*根據(jù)id查詢訂單
*
*@return
*/
@RequestMapping("/getById")
publicOrdergetById(@RequestParam("id")Longid){
//根據(jù)id查詢訂單
Orderorder=orderService.getById(id);
returnorder;
}
/**
*創(chuàng)建訂單
*
*@return
*/
@RequestMapping("/create")
publicStringcreate(@RequestBodyOrderorder){
//創(chuàng)建訂單
orderService.create(order);
return"sucess";
}
/**
*對訂單進行支付
*
*@paramid
*@return
*/
@RequestMapping("/pay")
publicStringpay(@RequestParam("id")Longid){
//對訂單進行支付
orderService.pay(id);
return"success";
}

/**
*對訂單進行發(fā)貨
*
*@paramid
*@return
*/
@RequestMapping("/deliver")
publicStringdeliver(@RequestParam("id")Longid){
//對訂單進行確認(rèn)收貨
orderService.deliver(id);
return"success";
}
/**
*對訂單進行確認(rèn)收貨
*
*@paramid
*@return
*/
@RequestMapping("/receive")
publicStringreceive(@RequestParam("id")Longid){
//對訂單進行確認(rèn)收貨
orderService.receive(id);
return"success";
}
}

servie:

/**
*author:芋道源碼
*/
@Service("orderService")
@Slf4j
publicclassOrderServiceImplextendsServiceImplimplementsOrderService{
@Resource
privateStateMachineorderStateMachine;
@Resource
privateStateMachinePersisterstateMachineMemPersister;
@Resource
privateOrderMapperorderMapper;
/**
*創(chuàng)建訂單
*
*@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("線程名稱:{},支付失敗,狀態(tài)異常,訂單信息:{}",Thread.currentThread().getName(),order);
thrownewRuntimeException("支付失敗,訂單狀態(tài)異常");
}
returnorder;
}
/**
*對訂單進行發(fā)貨
*
*@paramid
*@return
*/
publicOrderdeliver(Longid){
Orderorder=orderMapper.selectById(id);
log.info("線程名稱:{},嘗試發(fā)貨,訂單號:{}",Thread.currentThread().getName(),id);
if(!sendEvent(OrderStatusChangeEvent.DELIVERY,order)){
log.error("線程名稱:{},發(fā)貨失敗,狀態(tài)異常,訂單信息:{}",Thread.currentThread().getName(),order);
thrownewRuntimeException("發(fā)貨失敗,訂單狀態(tài)異常");
}
returnorder;
}
/**
*對訂單進行確認(rèn)收貨
*
*@paramid
*@return
*/
publicOrderreceive(Longid){
Orderorder=orderMapper.selectById(id);
log.info("線程名稱:{},嘗試收貨,訂單號:{}",Thread.currentThread().getName(),id);
if(!sendEvent(OrderStatusChangeEvent.RECEIVED,order)){
log.error("線程名稱:{},收貨失敗,狀態(tài)異常,訂單信息:{}",Thread.currentThread().getName(),order);
thrownewRuntimeException("收貨失敗,訂單狀態(tài)異常");
}
returnorder;
}
/**
*發(fā)送訂單狀態(tài)轉(zhuǎn)換事件
*synchronized修飾保證這個方法是線程安全的
*
*@paramchangeEvent
*@paramorder
*@return
*/
privatesynchronizedbooleansendEvent(OrderStatusChangeEventchangeEvent,Orderorder){
booleanresult=false;
try{
//啟動狀態(tài)機
orderStateMachine.start();
//嘗試恢復(fù)狀態(tài)機狀態(tài)
stateMachineMemPersister.restore(orderStateMachine,String.valueOf(order.getId()));
Messagemessage=MessageBuilder.withPayload(changeEvent).setHeader("order",order).build();
result=orderStateMachine.sendEvent(message);
//持久化狀態(tài)機狀態(tài)
stateMachineMemPersister.persist(orderStateMachine,String.valueOf(order.getId()));
}catch(Exceptione){
log.error("訂單操作失敗:{}",e);
}finally{
orderStateMachine.stop();
}
returnresult;
}
}

監(jiān)聽狀態(tài)的變化:

/**
*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("支付,狀態(tài)機反饋信息:{}",message.getHeaders().toString());
//更新訂單
order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
orderMapper.updateById(order);
//TODO其他業(yè)務(wù)
}
@OnTransition(source="WAIT_DELIVER",target="WAIT_RECEIVE")
publicvoiddeliverTransition(Messagemessage){
Orderorder=(Order)message.getHeaders().get("order");
log.info("發(fā)貨,狀態(tài)機反饋信息:{}",message.getHeaders().toString());
//更新訂單
order.setStatus(OrderStatus.WAIT_RECEIVE.getKey());
orderMapper.updateById(order);
//TODO其他業(yè)務(wù)
}
@OnTransition(source="WAIT_RECEIVE",target="FINISH")
publicvoidreceiveTransition(Messagemessage){
Orderorder=(Order)message.getHeaders().get("order");
log.info("確認(rèn)收貨,狀態(tài)機反饋信息:{}",message.getHeaders().toString());
//更新訂單
order.setStatus(OrderStatus.FINISH.getKey());
orderMapper.updateById(order);
//TODO其他業(yè)務(wù)
}
}

3.3 測試驗證

1)驗證業(yè)務(wù)

新增一個訂單

http://localhost:8084/order/create

對訂單進行支付

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

對訂單進行發(fā)貨

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

對訂單進行確認(rèn)收貨

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

正常流程結(jié)束。如果對一個訂單進行支付了,再次進行支付,則會報錯:http://localhost:8084/order/pay?id=2

報錯如下:

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

2)驗證持久化

內(nèi)存

使用內(nèi)存持久化類持久化:

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

/**
*發(fā)送訂單狀態(tài)轉(zhuǎn)換事件
*synchronized修飾保證這個方法是線程安全的
*
*@paramchangeEvent
*@paramorder
*@return
*/
privatesynchronizedbooleansendEvent(OrderStatusChangeEventchangeEvent,Orderorder){
booleanresult=false;
try{
//啟動狀態(tài)機
orderStateMachine.start();
//嘗試恢復(fù)狀態(tài)機狀態(tài)
stateMachineMemPersister.restore(orderStateMachine,String.valueOf(order.getId()));
Messagemessage=MessageBuilder.withPayload(changeEvent).setHeader("order",order).build();
result=orderStateMachine.sendEvent(message);
//持久化狀態(tài)機狀態(tài)
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;

/**
*發(fā)送訂單狀態(tài)轉(zhuǎn)換事件
*synchronized修飾保證這個方法是線程安全的
*
*@paramchangeEvent
*@paramorder
*@return
*/
privatesynchronizedbooleansendEvent(OrderStatusChangeEventchangeEvent,Orderorder){
booleanresult=false;
try{
//啟動狀態(tài)機
orderStateMachine.start();
//嘗試恢復(fù)狀態(tài)機狀態(tài)
stateMachineRedisPersister.restore(orderStateMachine,String.valueOf(order.getId()));
Messagemessage=MessageBuilder.withPayload(changeEvent).setHeader("order",order).build();
result=orderStateMachine.sendEvent(message);
//持久化狀態(tài)機狀態(tài)
stateMachineRedisPersister.persist(orderStateMachine,String.valueOf(order.getId()));
}catch(Exceptione){
log.error("訂單操作失敗:{}",e);
}finally{
orderStateMachine.stop();
}
returnresult;
}

3.4 狀態(tài)機存在的問題

1)stateMachine無法拋出異常,異常會被狀態(tài)機給消化掉

問題現(xiàn)象

從orderStateMachine.sendEvent(message);獲取的結(jié)果無法感知到。無論執(zhí)行正常還是拋出異常,都返回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("支付,狀態(tài)機反饋信息:{}",message.getHeaders().toString());
try{
//更新訂單
order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
orderMapper.updateById(order);
//TODO其他業(yè)務(wù)
//模擬異常
if(Objects.equals(order.getName(),"A")){
thrownewRuntimeException("執(zhí)行業(yè)務(wù)異常");
}
}catch(Exceptione){
//如果出現(xiàn)異常,記錄異常信息,拋出異常信息進行回滾
log.error("payTransition出現(xiàn)異常:{}",e);
throwe;
}
}

監(jiān)聽事件拋出異常,在發(fā)送事件中無法感知:

privatesynchronizedbooleansendEvent(OrderStatusChangeEventchangeEvent,Orderorder){
booleanresult=false;
try{
//啟動狀態(tài)機
orderStateMachine.start();
//嘗試恢復(fù)狀態(tài)機狀態(tài)
stateMachineMemPersister.restore(orderStateMachine,String.valueOf(order.getId()));
Messagemessage=MessageBuilder.withPayload(changeEvent).setHeader("order",order).build();
//事件執(zhí)行異常了,依然返回true,無法感知異常
result=orderStateMachine.sendEvent(message);
if(result){
//持久化狀態(tài)機狀態(tài),如果根據(jù)true持久化,則會出現(xiàn)問題
stateMachineMemPersister.persist(orderStateMachine,String.valueOf(order.getId()));
}
}catch(Exceptione){
log.error("訂單操作失敗:{}",e);
}finally{
orderStateMachine.stop();
}
returnresult;
}

調(diào)試發(fā)現(xiàn):發(fā)送事件和監(jiān)聽事件是一個線程,發(fā)送事件的結(jié)果是在監(jiān)聽操作執(zhí)行完之后才返回

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

監(jiān)聽線程:

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

解決方案:自己保存異常到數(shù)據(jù)庫或者內(nèi)存中,進行判斷

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

方法把執(zhí)行狀態(tài)放入這個變量中

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;
}

改造監(jiān)聽狀態(tài):把業(yè)務(wù)的執(zhí)行結(jié)果進行保存,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("支付,狀態(tài)機反饋信息:{}",message.getHeaders().toString());
try{
//更新訂單
order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
orderMapper.updateById(order);
//TODO其他業(yè)務(wù)
//模擬異常
if(Objects.equals(order.getName(),"A")){
thrownewRuntimeException("執(zhí)行業(yè)務(wù)異常");
}
//成功則為1
orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);
}catch(Exceptione){
//如果出現(xiàn)異常,則進行回滾
log.error("payTransition出現(xiàn)異常:{}",e);
//將異常信息變量信息中,失敗則為0
orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),0);
throwe;
}
}

發(fā)送事件改造:如果獲取到業(yè)務(wù)執(zhí)行異常,則返回失敗,不進行狀態(tài)機持久化 com.zengqingfa.springboot.state.demo.service.impl.OrderServiceImpl##sendEvent

@Resource
privateStateMachineorderStateMachine;
@Resource
privateStateMachinePersisterstateMachineMemPersister;

/**
*發(fā)送訂單狀態(tài)轉(zhuǎn)換事件
*synchronized修飾保證這個方法是線程安全的
*
*@paramchangeEvent
*@paramorder
*@return
*/
privatesynchronizedbooleansendEvent(OrderStatusChangeEventchangeEvent,Orderorder){
booleanresult=false;
try{
//啟動狀態(tài)機
orderStateMachine.start();
//嘗試恢復(fù)狀態(tài)機狀態(tài)
stateMachineMemPersister.restore(orderStateMachine,String.valueOf(order.getId()));
Messagemessage=MessageBuilder.withPayload(changeEvent).setHeader("order",order).build();
result=orderStateMachine.sendEvent(message);
if(!result){
returnfalse;
}
//獲取到監(jiān)聽的結(jié)果信息
Integero=(Integer)orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition+order.getId());
//操作完成之后,刪除本次對應(yīng)的key信息
orderStateMachine.getExtendedState().getVariables().remove(CommonConstants.payTransition+order.getId());
//如果事務(wù)執(zhí)行成功,則持久化狀態(tài)機
if(Objects.equals(1,Integer.valueOf(o))){
//持久化狀態(tài)機狀態(tài)
stateMachineMemPersister.persist(orderStateMachine,String.valueOf(order.getId()));
}else{
//訂單執(zhí)行業(yè)務(wù)異常
returnfalse;
}
}catch(Exceptione){
log.error("訂單操作失敗:{}",e);
}finally{
orderStateMachine.stop();
}
returnresult;
}

代碼優(yōu)化

發(fā)送事件只針對了支付,如果是非支付事件呢?

//獲取到監(jiān)聽的結(jié)果信息
Integero=(Integer)orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition+order.getId());

監(jiān)聽設(shè)置狀態(tài)的代碼有重復(fù)代碼,需要進行優(yōu)化,可使用aop

try{
//TODO其他業(yè)務(wù)
//成功則為1
orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);
}catch(Exceptione){
//如果出現(xiàn)異常,則進行回滾
log.error("payTransition出現(xiàn)異常:{}",e);
//將異常信息變量信息中,失敗則為0
orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),0);
throwe;
}

常量類:

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

支付發(fā)送事件: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("線程名稱:{},支付失敗,狀態(tài)異常,訂單信息:{}",Thread.currentThread().getName(),order);
thrownewRuntimeException("支付失敗,訂單狀態(tài)異常");
}
returnorder;
}

/**
*發(fā)送訂單狀態(tài)轉(zhuǎn)換事件
*synchronized修飾保證這個方法是線程安全的
*
*@paramchangeEvent
*@paramorder
*@return
*/
privatesynchronizedbooleansendEvent(OrderStatusChangeEventchangeEvent,Orderorder,Stringkey){
booleanresult=false;
try{
//啟動狀態(tài)機
orderStateMachine.start();
//嘗試恢復(fù)狀態(tài)機狀態(tài)
stateMachineMemPersister.restore(orderStateMachine,String.valueOf(order.getId()));
Messagemessage=MessageBuilder.withPayload(changeEvent).setHeader("order",order).build();
result=orderStateMachine.sendEvent(message);
if(!result){
returnfalse;
}
//獲取到監(jiān)聽的結(jié)果信息
Integero=(Integer)orderStateMachine.getExtendedState().getVariables().get(key+order.getId());
//操作完成之后,刪除本次對應(yīng)的key信息
orderStateMachine.getExtendedState().getVariables().remove(key+order.getId());
//如果事務(wù)執(zhí)行成功,則持久化狀態(tài)機
if(Objects.equals(1,Integer.valueOf(o))){
//持久化狀態(tài)機狀態(tài)
stateMachineMemPersister.persist(orderStateMachine,String.valueOf(order.getId()));
}else{
//訂單執(zhí)行業(yè)務(wù)異常
returnfalse;
}
}catch(Exceptione){
log.error("訂單操作失敗:{}",e);
}finally{
orderStateMachine.stop();
}
returnresult;
}

使用aop對監(jiān)聽事件切面,把業(yè)務(wù)執(zhí)行結(jié)果封裝到狀態(tài)機的變量中,注解:

@Retention(RetentionPolicy.RUNTIME)
public@interfaceLogResult{
/**
*執(zhí)行的業(yè)務(wù)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{
//獲取參數(shù)
Object[]args=pjp.getArgs();
log.info("參數(shù)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{
//執(zhí)行方法
returnVal=pjp.proceed();
//如果業(yè)務(wù)執(zhí)行正常,則保存信息
//成功則為1
orderStateMachine.getExtendedState().getVariables().put(key+order.getId(),1);
}catch(Throwablee){
log.error("e:{}",e.getMessage());
//如果業(yè)務(wù)執(zhí)行異常,則保存信息
//將異常信息變量信息中,失敗則為0
orderStateMachine.getExtendedState().getVariables().put(key+order.getId(),0);
throwe;
}
returnreturnVal;
}
}

監(jiān)聽類使用注解:

@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("支付,狀態(tài)機反饋信息:{}",message.getHeaders().toString());
//更新訂單
order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
orderMapper.updateById(order);
//TODO其他業(yè)務(wù)
//模擬異常
if(Objects.equals(order.getName(),"A")){
thrownewRuntimeException("執(zhí)行業(yè)務(wù)異常");
}
}
@OnTransition(source="WAIT_DELIVER",target="WAIT_RECEIVE")
@LogResult(key=CommonConstants.deliverTransition)
publicvoiddeliverTransition(Messagemessage){
Orderorder=(Order)message.getHeaders().get("order");
log.info("發(fā)貨,狀態(tài)機反饋信息:{}",message.getHeaders().toString());
//更新訂單
order.setStatus(OrderStatus.WAIT_RECEIVE.getKey());
orderMapper.updateById(order);
//TODO其他業(yè)務(wù)
}
@OnTransition(source="WAIT_RECEIVE",target="FINISH")
@LogResult(key=CommonConstants.receiveTransition)
publicvoidreceiveTransition(Messagemessage){
Orderorder=(Order)message.getHeaders().get("order");
log.info("確認(rèn)收貨,狀態(tài)機反饋信息:{}",message.getHeaders().toString());
//更新訂單
order.setStatus(OrderStatus.FINISH.getKey());
orderMapper.updateById(order);
//TODO其他業(yè)務(wù)
}
}
審核編輯:彭靜
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3689

    瀏覽量

    95225
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    340

    瀏覽量

    15061
  • 數(shù)學(xué)模型
    +關(guān)注

    關(guān)注

    0

    文章

    83

    瀏覽量

    12259
  • 自動機
    +關(guān)注

    關(guān)注

    1

    文章

    28

    瀏覽量

    9490

原文標(biāo)題:項目終于用上了Spring狀態(tài)機,非常優(yōu)雅!

文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    Spring狀態(tài)機的實現(xiàn)原理和使用方法

    說起 Spring 狀態(tài)機,大家很容易聯(lián)想到這個狀態(tài)機和設(shè)計模式中狀態(tài)模式的區(qū)別是啥呢?沒錯,Spring
    的頭像 發(fā)表于 12-26 09:39 ?2687次閱讀
    <b class='flag-5'>Spring</b><b class='flag-5'>狀態(tài)機</b>的實現(xiàn)原理和使用方法

    Verilog狀態(tài)機+設(shè)計實例

    在verilog中狀態(tài)機的一種很常用的邏輯結(jié)構(gòu),學(xué)習(xí)和理解狀態(tài)機的運行規(guī)律能夠幫助我們更好地書寫代碼,同時作為一種思想方法,在別的代碼設(shè)計中也會有所幫助。 一、簡介 在使用過程中我們常說
    的頭像 發(fā)表于 02-12 19:07 ?5439次閱讀
    Verilog<b class='flag-5'>狀態(tài)機</b>+設(shè)計實例

    玩轉(zhuǎn)Spring狀態(tài)機

    說起Spring狀態(tài)機,大家很容易聯(lián)想到這個狀態(tài)機和設(shè)計模式中狀態(tài)模式的區(qū)別是啥呢?沒錯,Spring
    的頭像 發(fā)表于 06-25 14:21 ?1306次閱讀
    玩轉(zhuǎn)<b class='flag-5'>Spring</b><b class='flag-5'>狀態(tài)機</b>

    如何寫好狀態(tài)機

    如何寫好狀態(tài)機:狀態(tài)機是邏輯設(shè)計的重要內(nèi)容,狀態(tài)機的設(shè)計水平直接反應(yīng)工程師的邏輯功底,所以許多公司的硬件和邏輯工程師面試中,狀態(tài)機設(shè)計幾乎是必選題目。本章在引入
    發(fā)表于 06-14 19:24 ?97次下載

    狀態(tài)機舉例

    狀態(tài)機舉例 你可以指定狀態(tài)寄存器和狀態(tài)機狀態(tài)。以下是一個有四種狀態(tài)的普通狀態(tài)機。 // Th
    發(fā)表于 03-28 15:18 ?1094次閱讀

    狀態(tài)機代碼生成工具

    狀態(tài)機代碼生成工具狀態(tài)機代碼生成工具狀態(tài)機代碼生成工具狀態(tài)機代碼生成工具
    發(fā)表于 11-19 15:12 ?9次下載

    狀態(tài)機原理及用法

    狀態(tài)機原理及用法狀態(tài)機原理及用法狀態(tài)機原理及用法
    發(fā)表于 03-15 15:25 ?0次下載

    簡述使用QII狀態(tài)機向?qū)绾蝿?chuàng)建一個狀態(tài)機

    如何使用QII狀態(tài)機向?qū)?chuàng)建一個狀態(tài)機
    的頭像 發(fā)表于 06-20 00:11 ?4567次閱讀
    簡述使用QII<b class='flag-5'>狀態(tài)機</b>向?qū)绾蝿?chuàng)建一個<b class='flag-5'>狀態(tài)機</b>

    狀態(tài)機概述 如何理解狀態(tài)機

    本篇文章包括狀態(tài)機的基本概述以及通過簡單的實例理解狀態(tài)機
    的頭像 發(fā)表于 01-02 18:03 ?1.1w次閱讀
    <b class='flag-5'>狀態(tài)機</b>概述  如何理解<b class='flag-5'>狀態(tài)機</b>

    什么是狀態(tài)機 狀態(tài)機的描述三種方法

    狀態(tài)機 1、狀態(tài)機是許多數(shù)字系統(tǒng)的核心部件,是一類重要的時序邏輯電路。通常包括三個部分:一是下一個狀態(tài)的邏輯電路,二是存儲狀態(tài)機當(dāng)前狀態(tài)的時
    的頭像 發(fā)表于 11-16 17:39 ?2.7w次閱讀

    FPGA:狀態(tài)機簡述

    本文目錄 前言 狀態(tài)機簡介 狀態(tài)機分類 Mealy 型狀態(tài)機 Moore 型狀態(tài)機 狀態(tài)機描述 一段式
    的頭像 發(fā)表于 11-05 17:58 ?8044次閱讀
    FPGA:<b class='flag-5'>狀態(tài)機</b>簡述

    什么是狀態(tài)機?狀態(tài)機5要素

    等。 本文來說一下狀態(tài)機編程。 什么是狀態(tài)機? 狀態(tài)機(state machine)有5個要素: 狀態(tài)(state) 遷移(transition) 事件(event) 動作(actio
    的頭像 發(fā)表于 07-27 11:23 ?2.1w次閱讀
    什么是<b class='flag-5'>狀態(tài)機</b>?<b class='flag-5'>狀態(tài)機</b>5要素

    狀態(tài)模式(狀態(tài)機)

    以前寫狀態(tài)機,比較常用的方式是用 if-else 或 switch-case,高級的一點是函數(shù)指針列表。最近,看了一文章《c語言設(shè)計模式–狀態(tài)模式(狀態(tài)機)》(來源:embed linux
    發(fā)表于 12-16 16:53 ?9次下載
    <b class='flag-5'>狀態(tài)</b>模式(<b class='flag-5'>狀態(tài)機</b>)

    labview狀態(tài)機分享

    labview狀態(tài)機
    發(fā)表于 10-31 15:50 ?18次下載

    什么是狀態(tài)機狀態(tài)機的種類與實現(xiàn)

    狀態(tài)機,又稱有限狀態(tài)機(Finite State Machine,F(xiàn)SM)或米利狀態(tài)機(Mealy Machine),是一種描述系統(tǒng)狀態(tài)變化的模型。在芯片設(shè)計中,
    的頭像 發(fā)表于 10-19 10:27 ?1.2w次閱讀