女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

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

    文章

    3689

    瀏覽量

    95220
  • spring
    +關注

    關注

    0

    文章

    340

    瀏覽量

    15059
  • 數學模型
    +關注

    關注

    0

    文章

    83

    瀏覽量

    12259
  • 自動機
    +關注

    關注

    1

    文章

    28

    瀏覽量

    9489

原文標題:項目終于用上了Spring狀態機,非常優雅!

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

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

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    Spring狀態機的實現原理和使用方法

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

    Verilog狀態機+設計實例

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

    玩轉Spring狀態機

    說起Spring狀態機,大家很容易聯想到這個狀態機和設計模式中狀態模式的區別是啥呢?沒錯,Spring
    的頭像 發表于 06-25 14:21 ?1306次閱讀
    玩轉<b class='flag-5'>Spring</b><b class='flag-5'>狀態機</b>

    如何寫好狀態機

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

    狀態機舉例

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

    狀態機代碼生成工具

    狀態機代碼生成工具狀態機代碼生成工具狀態機代碼生成工具狀態機代碼生成工具
    發表于 11-19 15:12 ?9次下載

    狀態機原理及用法

    狀態機原理及用法狀態機原理及用法狀態機原理及用法
    發表于 03-15 15:25 ?0次下載

    簡述使用QII狀態機向導如何創建一個狀態機

    如何使用QII狀態機向導創建一個狀態機
    的頭像 發表于 06-20 00:11 ?4566次閱讀
    簡述使用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.7w次閱讀

    FPGA:狀態機簡述

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

    什么是狀態機狀態機5要素

    等。 本文來說一下狀態機編程。 什么是狀態機狀態機(state machine)有5個要素: 狀態(state) 遷移(transition) 事件(event) 動作(actio
    的頭像 發表于 07-27 11:23 ?2.1w次閱讀
    什么是<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 ?18次下載

    什么是狀態機狀態機的種類與實現

    狀態機,又稱有限狀態機(Finite State Machine,FSM)或米利狀態機(Mealy Machine),是一種描述系統狀態變化的模型。在芯片設計中,
    的頭像 發表于 10-19 10:27 ?1.2w次閱讀