国产av一二三区|日本不卡动作网站|黄色天天久久影片|99草成人免费在线视频|AV三级片成人电影在线|成年人aV不卡免费播放|日韩无码成人一级片视频|人人看人人玩开心色AV|人妻系列在线观看|亚洲av无码一区二区三区在线播放

網(wǎng)易首頁 > 網(wǎng)易號 > 正文 申請入駐

項目終于用上了 Spring 狀態(tài)機,太優(yōu)雅了!

0
分享至

Java精選面試題(微信小程序):5000+道面試題和選擇題,真實面經(jīng),簡歷模版,包含Java基礎(chǔ)、并發(fā)、JVM、線程、MQ系列、Redis、Spring系列、Elasticsearch、Docker、K8s、Flink、Spark、架構(gòu)設(shè)計、大廠真題等,在線隨時刷題!

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 。

狀態(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)機,同時給定它的當前狀態(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)來自外界的各種事件。

2、狀態(tài)機圖

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

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

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

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

  • ③動作:條件滿足后執(zhí)行的動作。動作執(zhí)行完畢后,可以遷移到新的狀態(tài),也可以仍舊保持原狀態(tài)。動作不是必需的,當條件滿足后,也可以不執(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、避免把某個“程序動作”當作是一種“狀態(tài)”來處理。那么如何區(qū)分“動作”和“狀態(tài)”?“動作”是不穩(wěn)定的,即使沒有條件的觸發(fā),“動作”一旦執(zhí)行完畢就結(jié)束了;而“狀態(tài)”是相對穩(wěn)定的,如果沒有外部條件的觸發(fā),一個狀態(tài)會一直持續(xù)下去。

2、狀態(tài)劃分時漏掉一些狀態(tài),導(dǎo)致跳轉(zhuǎn)邏輯不完整。所以在設(shè)計狀態(tài)機時,我們需要反復(fù)的查看設(shè)計的狀態(tài)圖或者狀態(tài)表,最終達到一種牢不可破的設(shè)計方案。另外,推薦公眾號Java精選,回復(fù)java面試,獲取在線面試資料,支持隨時隨地刷題。

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

Spring Statemachine是應(yīng)用程序開發(fā)人員在Spring應(yīng)用程序中使用狀態(tài)機概念的框架。關(guān)于更多spring架構(gòu)知識:https://www.yoodb.com/spring/spring-ioc-architecture.html

Spring Statemachine旨在提供以下功能:

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

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

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

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

  5. 鍵入安全配置適配器。

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

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

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

  9. UML Eclipse Papyrus建模。

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

  11. 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ā)送事件,偵聽更改或僅請求當前狀態(tài)來與狀態(tài)機進行交互。官網(wǎng):https://spring.io/projects/spring-statemachine

3.2 快速開始

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

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

CREATE TABLE`tb_order` (       `id`bigint(20) unsignedNOTNULL AUTO_INCREMENT COMMENT'主鍵ID',       `order_code`varchar(128) COLLATE utf8mb4_bin DEFAULTNULLCOMMENT'訂單編碼',       `status`smallint(3) DEFAULTNULLCOMMENT'訂單狀態(tài)',       `name`varchar(64) COLLATE utf8mb4_bin DEFAULTNULLCOMMENT'訂單名稱',       `price`decimal(12,2) DEFAULTNULLCOMMENT'價格',       `delete_flag`tinyint(2) NOTNULLDEFAULT'0'COMMENT'刪除標記,0未刪除  1已刪除',       `create_time`timestampNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMPCOMMENT'創(chuàng)建時間',       `update_time`timestampNOTNULLDEFAULT'0000-00-00 00:00:00'COMMENT'更新時間',       `create_user_code`varchar(32) COLLATE utf8mb4_bin DEFAULTNULLCOMMENT'創(chuàng)建人',       `update_user_code`varchar(32) COLLATE utf8mb4_bin DEFAULTNULLCOMMENT'更新人',       `version`int(11) NOTNULLDEFAULT'0'COMMENT'版本號',       `remark`varchar(64) COLLATE utf8mb4_bin DEFAULTNULLCOMMENT'備注',       PRIMARY KEY (`id`)     ) ENGINE=InnoDB AUTO_INCREMENT=6DEFAULTCHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='訂單表';          /*Data for the table `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-15 16:14:11','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL),     (3,'A111',1,'訂單A','22.00',0,'2022-10-02 21:53:13','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL),     (4,'A111',1,'訂單A','22.00',0,'2022-10-02 21:53:13','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL),     (5,'A111',1,'訂單A','22.00',0,'2022-10-03 09:08:30','2022-10-02 21:29:14','zhangsan','zhangsan',0,NULL);
1)引入依賴

      

         

 org.springframework.statemachine groupId>         

 spring-statemachine-redis artifactId>         

 1.2.9.RELEASE version>      dependency>          

         

 org.springframework.statemachine groupId>         

 spring-statemachine-starter artifactId>         

 2.0.1.RELEASE version>      dependency>







2)定義狀態(tài)機狀態(tài)和事件

狀態(tài)枚舉:

/** * 公眾號:Java精選 */ publicenum OrderStatus {         // 待支付,待發(fā)貨,待收貨,已完成         WAIT_PAYMENT(1, "待支付"),         WAIT_DELIVER(2, "待發(fā)貨"),         WAIT_RECEIVE(3, "待收貨"),         FINISH(4, "已完成");         private Integer key;         private String desc;         OrderStatus(Integer key, String desc) {             this.key = key;             this.desc = desc;         }         public Integer getKey() {             return key;         }         public String getDesc() {             return desc;         }         public static OrderStatus getByKey(Integer key) {             for (OrderStatus e : values()) {                 if (e.getKey().equals(key)) {                     return e;                 }             }             thrownew RuntimeException("enum not exists.");         }     }

事件:

/** * 公眾號:Java精選 */ public enum OrderStatusChangeEvent {         // 支付,發(fā)貨,確認收貨         PAYED, DELIVERY, RECEIVED; }
3)定義狀態(tài)機規(guī)則和配置狀態(tài)機

 @Configuration     @EnableStateMachine(name = "orderStateMachine")     publicclass OrderStateMachineConfig extends StateMachineConfigurerAdapter

  {         /**          * 配置狀態(tài)          *          * @param states          * @throws Exception          */         public void configure(StateMachineStateConfigurer states)  throws Exception {             states                     .withStates()                     .initial(OrderStatus.WAIT_PAYMENT)                     .states(EnumSet.allOf(OrderStatus.class));         }         /**          * 配置狀態(tài)轉(zhuǎn)換事件關(guān)系          *          * @param transitions          * @throws Exception          */         public void configure(StateMachineTransitionConfigurer transitions)  throws Exception {             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);         }     }

配置持久化:

 /**  * 公眾號:Java精選  */      @Configuration     @Slf4j     publicclass Persist

  {         /**          * 持久化到內(nèi)存map中          *          * @return          */         @Bean(name = "stateMachineMemPersister")         public static StateMachinePersister getPersister() {             returnnew DefaultStateMachinePersister(new StateMachinePersist() {                 @Override                 public void write(StateMachineContext context, Object contextObj) throws Exception {                     log.info("持久化狀態(tài)機,context:{},contextObj:{}", JSON.toJSONString(context), JSON.toJSONString(contextObj));                     map.put(contextObj, context);                 }                 @Override                 public StateMachineContext read(Object contextObj) throws Exception {                     log.info("獲取狀態(tài)機,contextObj:{}", JSON.toJSONString(contextObj));                     StateMachineContext stateMachineContext = (StateMachineContext) map.get(contextObj);                     log.info("獲取狀態(tài)機結(jié)果,stateMachineContext:{}", JSON.toJSONString(stateMachineContext));                     return stateMachineContext;                 }                 private Map map = new HashMap();             });         }              @Resource         private RedisConnectionFactory redisConnectionFactory;         /**          * 持久化到redis中,在分布式系統(tǒng)中使用          *          * @return          */         @Bean(name = "stateMachineRedisPersister")         public RedisStateMachinePersister   getRedisPersister() {             RedisStateMachineContextRepository repository =  new RedisStateMachineContextRepository<>(redisConnectionFactory);             RepositoryStateMachinePersist p = new RepositoryStateMachinePersist<>(repository);             returnnew RedisStateMachinePersister<>(p);         }     }
4)業(yè)務(wù)系統(tǒng)

controller:

 /**  * 公眾號:Java精選  */      @RestController     @RequestMapping("/order")     publicclass OrderController {         @Resource         private OrderService orderService;         /**          * 根據(jù)id查詢訂單          *          * @return          */         @RequestMapping("/getById")         public Order getById(@RequestParam("id") Long id) {             //根據(jù)id查詢訂單             Order order = orderService.getById(id);             return order;         }         /**          * 創(chuàng)建訂單          *          * @return          */         @RequestMapping("/create")         public String create(@RequestBody Order order) {             //創(chuàng)建訂單             orderService.create(order);             return"sucess";         }         /**          * 對訂單進行支付          *          * @param id          * @return          */         @RequestMapping("/pay")         public String pay(@RequestParam("id") Long id) {             //對訂單進行支付             orderService.pay(id);             return"success";         }              /**          * 對訂單進行發(fā)貨          *          * @param id          * @return          */         @RequestMapping("/deliver")         public String deliver(@RequestParam("id") Long id) {             //對訂單進行確認收貨             orderService.deliver(id);             return"success";         }         /**          * 對訂單進行確認收貨          *          * @param id          * @return          */         @RequestMapping("/receive")         public String receive(@RequestParam("id") Long id) {             //對訂單進行確認收貨             orderService.receive(id);             return"success";         }     }

servie:

 /**  * 公眾號:Java精選  */      @Service("orderService")     @Slf4j     publicclass OrderServiceImpl extends ServiceImpl

  implements OrderService {         @Resource         private StateMachine orderStateMachine;         @Resource         private StateMachinePersister stateMachineMemPersister;         @Resource         private OrderMapper orderMapper;         /**          * 創(chuàng)建訂單          *          * @param order          * @return          */         public Order create(Order order) {             order.setStatus(OrderStatus.WAIT_PAYMENT.getKey());             orderMapper.insert(order);             return order;         }         /**          * 對訂單進行支付          *          * @param id          * @return          */         public Order pay(Long id) {             Order order = orderMapper.selectById(id);             log.info("線程名稱:{},嘗試支付,訂單號:{}" ,Thread.currentThread().getName() , id);             if (!sendEvent(OrderStatusChangeEvent.PAYED, order)) {                 log.error("線程名稱:{},支付失敗, 狀態(tài)異常,訂單信息:{}", Thread.currentThread().getName(), order);                 thrownew RuntimeException("支付失敗, 訂單狀態(tài)異常");             }             return order;         }         /**          * 對訂單進行發(fā)貨          *          * @param id          * @return          */         public Order deliver(Long id) {             Order order = orderMapper.selectById(id);             log.info("線程名稱:{},嘗試發(fā)貨,訂單號:{}" ,Thread.currentThread().getName() , id);             if (!sendEvent(OrderStatusChangeEvent.DELIVERY, order)) {                 log.error("線程名稱:{},發(fā)貨失敗, 狀態(tài)異常,訂單信息:{}", Thread.currentThread().getName(), order);                 thrownew RuntimeException("發(fā)貨失敗, 訂單狀態(tài)異常");             }             return order;         }         /**          * 對訂單進行確認收貨          *          * @param id          * @return          */         public Order receive(Long id) {             Order order = orderMapper.selectById(id);             log.info("線程名稱:{},嘗試收貨,訂單號:{}" ,Thread.currentThread().getName() , id);             if (!sendEvent(OrderStatusChangeEvent.RECEIVED, order)) {                 log.error("線程名稱:{},收貨失敗, 狀態(tài)異常,訂單信息:{}", Thread.currentThread().getName(), order);                 thrownew RuntimeException("收貨失敗, 訂單狀態(tài)異常");             }             return order;         }         /**          * 發(fā)送訂單狀態(tài)轉(zhuǎn)換事件          * synchronized修飾保證這個方法是線程安全的          *          * @param changeEvent          * @param order          * @return          */         private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {             boolean result = false;             try {                 //啟動狀態(tài)機                 orderStateMachine.start();                 //嘗試恢復(fù)狀態(tài)機狀態(tài)                 stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));                 Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();                 result = orderStateMachine.sendEvent(message);                 //持久化狀態(tài)機狀態(tài)                 stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));             } catch (Exception e) {                 log.error("訂單操作失敗:{}", e);             } finally {                 orderStateMachine.stop();             }             return result;         }     }

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

 /**  * 公眾號:Java精選  */          @Component("orderStateListener")     @WithStateMachine(name = "orderStateMachine")     @Slf4j     publicclass OrderStateListenerImpl {         @Resource         private OrderMapper orderMapper;                  @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")         public void payTransition(Message message)  {             Order order = (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")         public void deliverTransition(Message message)  {             Order order = (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")         public void receiveTransition(Message message)  {             Order order = (Order) message.getHeaders().get("order");             log.info("確認收貨,狀態(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

  • 對訂單進行確認收貨

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

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

報錯如下:

2)驗證持久化

內(nèi)存。java進階技術(shù)路線:https://www.yoodb.com/

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

 /**  * 公眾號:Java精選  */  @Resource     private StateMachinePersister stateMachineMemPersister;          /**      * 發(fā)送訂單狀態(tài)轉(zhuǎn)換事件      * synchronized修飾保證這個方法是線程安全的      *      * @param changeEvent      * @param order      * @return      */     private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {         boolean result = false;         try {             //啟動狀態(tài)機             orderStateMachine.start();             //嘗試恢復(fù)狀態(tài)機狀態(tài)             stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));             Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();             result = orderStateMachine.sendEvent(message);             //持久化狀態(tài)機狀態(tài)             stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));         } catch (Exception e) {             log.error("訂單操作失敗:{}", e);         } finally {             orderStateMachine.stop();         }         return result;     }

redis持久化

引入依賴:


     

 org.springframework.statemachine groupId>     

 spring-statemachine-redis artifactId>     

 1.2.9.RELEASE version> dependency>



配置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持久化類持久化:

 /**  * 公眾號:Java精選  */  @Resource     private StateMachinePersister stateMachineRedisPersister;          /**      * 發(fā)送訂單狀態(tài)轉(zhuǎn)換事件      * synchronized修飾保證這個方法是線程安全的      *      * @param changeEvent      * @param order      * @return      */     private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {         boolean result = false;         try {             //啟動狀態(tài)機             orderStateMachine.start();             //嘗試恢復(fù)狀態(tài)機狀態(tài)             stateMachineRedisPersister.restore(orderStateMachine, String.valueOf(order.getId()));             Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();             result = orderStateMachine.sendEvent(message);             //持久化狀態(tài)機狀態(tài)             stateMachineRedisPersister.persist(orderStateMachine, String.valueOf(order.getId()));         } catch (Exception e) {             log.error("訂單操作失敗:{}", e);         } finally {             orderStateMachine.stop();         }         return result;     }
3.4 狀態(tài)機存在的問題 1)stateMachine無法拋出異常,異常會被狀態(tài)機給消化掉

問題現(xiàn)象

從orderStateMachine.sendEvent(message);獲取的結(jié)果無法感知到。無論執(zhí)行正常還是拋出異常,都返回true。

 @Resource     private OrderMapper orderMapper;          @Resource     private StateMachine orderStateMachine;          @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")     @Transactional(rollbackFor = Exception.class)     public void payTransition(Message

  message) {         Order order = (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")){                 thrownew RuntimeException("執(zhí)行業(yè)務(wù)異常");             }         } catch (Exception e) {             //如果出現(xiàn)異常,記錄異常信息,拋出異常信息進行回滾             log.error("payTransition 出現(xiàn)異常:{}",e);             throw e;         }     }

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

 private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {         boolean result = false;         try {             //啟動狀態(tài)機             orderStateMachine.start();             //嘗試恢復(fù)狀態(tài)機狀態(tài)             stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));             Message message = 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 (Exception e) {             log.error("訂單操作失敗:{}", e);         } finally {             orderStateMachine.stop();         }         return result;     }

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

監(jiān)聽線程:

解決方案:自己保存異常到數(shù)據(jù)庫或者內(nèi)存中,進行判斷。java技術(shù)進階路線:https://www.yoodb.com/

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

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

public interface ExtendedState {         Map   getVariables();           T get(Object var1, Class var2) ;         void setExtendedStateChangeListener(ExtendedState.ExtendedStateChangeListener var1);         public interface ExtendedStateChangeListener {             void changed(Object var1, Object var2);         }     }

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

private final Map variables;          public DefaultExtendedState() {         this.variables = new ObservableMap(new ConcurrentHashMap(), new DefaultExtendedState.LocalMapChangeListener());     }          public Map   getVariables() {         return this.variables;     }

改造監(jiān)聽狀態(tài):把業(yè)務(wù)的執(zhí)行結(jié)果進行保存,1成功,0失敗

    @Resource     private OrderMapper orderMapper;     @Resource     private StateMachine orderStateMachine;          @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")     @Transactional(rollbackFor = Exception.class)     public void payTransition(Message

  message) {         Order order = (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")){                 thrownew RuntimeException("執(zhí)行業(yè)務(wù)異常");             }             //成功 則為1             orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);         } catch (Exception e) {             //如果出現(xiàn)異常,則進行回滾             log.error("payTransition 出現(xiàn)異常:{}",e);             //將異常信息變量信息中,失敗則為0             orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(), 0);             throw e;         }     }

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

 @Resource     private StateMachine orderStateMachine;     @Resource     private StateMachinePersister stateMachineMemPersister;          /**      * 發(fā)送訂單狀態(tài)轉(zhuǎn)換事件      * synchronized修飾保證這個方法是線程安全的      *      * @param changeEvent      * @param order      * @return      */     private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order){         boolean result = false;         try {             //啟動狀態(tài)機             orderStateMachine.start();             //嘗試恢復(fù)狀態(tài)機狀態(tài)             stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));             Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();             result = orderStateMachine.sendEvent(message);             if(!result){                 returnfalse;             }             //獲取到監(jiān)聽的結(jié)果信息             Integer o = (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 (Exception e) {             log.error("訂單操作失敗:{}", e);         } finally {             orderStateMachine.stop();         }         return result;     }

代碼優(yōu)化

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

//獲取到監(jiān)聽的結(jié)果信息 Integer o = (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 (Exception e) {         //如果出現(xiàn)異常,則進行回滾         log.error("payTransition 出現(xiàn)異常:{}",e);         //將異常信息變量信息中,失敗則為0         orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(), 0);         throw e;     }

常量類:

public interface CommonConstants {         String orderHeader="order";         String payTransition="payTransition";         String deliverTransition="deliverTransition";         String receiveTransition="receiveTransition";     }

支付發(fā)送事件:com.zengqingfa.springboot.state.demo.service.impl.OrderServiceImpl##pay

 @Resource     private StateMachine orderStateMachine;     @Resource     private StateMachinePersister stateMachineMemPersister;     @Resource     private OrderMapper orderMapper;          /**      * 對訂單進行支付      *      * @param id      * @return      */     public Order pay(Long id) {         Order order = orderMapper.selectById(id);         log.info("線程名稱:{},嘗試支付,訂單號:{}" ,Thread.currentThread().getName() , id);         if (!sendEvent(OrderStatusChangeEvent.PAYED, order,CommonConstants.payTransition)) {             log.error("線程名稱:{},支付失敗, 狀態(tài)異常,訂單信息:{}", Thread.currentThread().getName(), order);             thrownew RuntimeException("支付失敗, 訂單狀態(tài)異常");         }         return order;     }          /**      * 發(fā)送訂單狀態(tài)轉(zhuǎn)換事件      * synchronized修飾保證這個方法是線程安全的      *      * @param changeEvent      * @param order      * @return      */     private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order,String key){         boolean result = false;         try {             //啟動狀態(tài)機             orderStateMachine.start();             //嘗試恢復(fù)狀態(tài)機狀態(tài)             stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));             Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();             result = orderStateMachine.sendEvent(message);             if(!result){                 returnfalse;             }             //獲取到監(jiān)聽的結(jié)果信息             Integer o = (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 (Exception e) {             log.error("訂單操作失敗:{}", e);         } finally {             orderStateMachine.stop();         }         return result;     }

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

 @Retention(RetentionPolicy.RUNTIME)     public @interface LogResult {         /**          *執(zhí)行的業(yè)務(wù)key          *          * @return String          */         String key();     }

切面:

 @Component     @Aspect     @Slf4j     publicclass LogResultAspect {              //攔截 LogHistory注解         @Pointcut("@annotation(com.zengqingfa.springboot.state.demo.aop.annotation.LogResult)")         private void logResultPointCut() {             //logResultPointCut 日志注解切點         }         @Resource         private StateMachine orderStateMachine;                  @Around("logResultPointCut()")         public Object logResultAround(ProceedingJoinPoint pjp) throws Throwable {             //獲取參數(shù)             Object[] args = pjp.getArgs();             log.info("參數(shù)args:{}", args);             Message message = (Message) args[0];             Order order = (Order) message.getHeaders().get("order");             //獲取方法             Method method = ((MethodSignature) pjp.getSignature()).getMethod();             // 獲取LogHistory注解             LogResult logResult = method.getAnnotation(LogResult.class);             String key = logResult.key();             Object returnVal = null;             try {                 //執(zhí)行方法                 returnVal = pjp.proceed();                 //如果業(yè)務(wù)執(zhí)行正常,則保存信息                 //成功 則為1                 orderStateMachine.getExtendedState().getVariables().put(key + order.getId(), 1);             } catch (Throwable e) {                 log.error("e:{}", e.getMessage());                 //如果業(yè)務(wù)執(zhí)行異常,則保存信息                 //將異常信息變量信息中,失敗則為0                 orderStateMachine.getExtendedState().getVariables().put(key + order.getId(), 0);                 throw e;             }             return returnVal;         }     }

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

 @Component("orderStateListener")     @WithStateMachine(name = "orderStateMachine")     @Slf4j     publicclass OrderStateListenerImpl {         @Resource         private OrderMapper orderMapper;              @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")         @Transactional(rollbackFor = Exception.class)         @LogResult(key = CommonConstants.payTransition)         public void payTransition(Message message)  {             Order order = (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")) {                 thrownew RuntimeException("執(zhí)行業(yè)務(wù)異常");             }         }         @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")         @LogResult(key = CommonConstants.deliverTransition)         public void deliverTransition(Message message)  {             Order order = (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)         public void receiveTransition(Message message)  {             Order order = (Order) message.getHeaders().get("order");             log.info("確認收貨,狀態(tài)機反饋信息:{}", message.getHeaders().toString());             //更新訂單             order.setStatus(OrderStatus.FINISH.getKey());             orderMapper.updateById(order);             //TODO 其他業(yè)務(wù)         }     }

作者:betheme

http://e.betheme.net/article/show-950658.html

公眾號“Java精選”所發(fā)表內(nèi)容注明來源的,版權(quán)歸原出處所有(無法查證版權(quán)的或者未注明出處的均來自網(wǎng)絡(luò),系轉(zhuǎn)載,轉(zhuǎn)載的目的在于傳遞更多信息,版權(quán)屬于原作者。如有侵權(quán),請聯(lián)系,筆者會第一時間刪除處理!

最近有很多人問,有沒有讀者交流群!加入方式很簡單,公眾號Java精選,回復(fù)“加群”,即可入群!

文章有幫助的話,點在看,轉(zhuǎn)發(fā)吧!

特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺“網(wǎng)易號”用戶上傳并發(fā)布,本平臺僅提供信息存儲服務(wù)。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相關(guān)推薦
熱點推薦
被曝生下畸形兒傳聞僅1月,闞清子官宣喜訊,連發(fā)三文表明態(tài)度

被曝生下畸形兒傳聞僅1月,闞清子官宣喜訊,連發(fā)三文表明態(tài)度

一口娛樂
2026-01-29 09:31:09
已確認,浙江有大到暴雪!今晚變天

已確認,浙江有大到暴雪!今晚變天

魯中晨報
2026-01-29 13:39:09
不敢置信!女子十二樂坊成員直播養(yǎng)不活自己,一晚上打賞不到3000

不敢置信!女子十二樂坊成員直播養(yǎng)不活自己,一晚上打賞不到3000

云中浮生
2026-01-24 21:37:46
奇葩!35歲女子4年不回家,母親來探望卻被懟,女子:就當我死了

奇葩!35歲女子4年不回家,母親來探望卻被懟,女子:就當我死了

娛樂帝皇丸
2026-01-28 15:18:51
16歲武漢男生凌晨離家,失聯(lián)超70小時, 父親:未帶手機現(xiàn)金,最后出現(xiàn)在橋上不見蹤跡

16歲武漢男生凌晨離家,失聯(lián)超70小時, 父親:未帶手機現(xiàn)金,最后出現(xiàn)在橋上不見蹤跡

大風(fēng)新聞
2026-01-29 13:53:22
人民日報怒批!炫富、偷稅749萬、跑國外,現(xiàn)又來“割內(nèi)地韭菜”

人民日報怒批!炫富、偷稅749萬、跑國外,現(xiàn)又來“割內(nèi)地韭菜”

小熊侃史
2026-01-18 07:20:09
港股地產(chǎn)股大漲,融創(chuàng)中國漲超30%

港股地產(chǎn)股大漲,融創(chuàng)中國漲超30%

每日經(jīng)濟新聞
2026-01-29 11:39:06
外交部:繼續(xù)深化國際執(zhí)法合作 加大力度打擊網(wǎng)賭電詐

外交部:繼續(xù)深化國際執(zhí)法合作 加大力度打擊網(wǎng)賭電詐

財聯(lián)社
2026-01-29 15:26:24
尼帕病毒來勢洶洶,建議:每家最好備6樣?xùn)|西,關(guān)鍵時刻能救命呢

尼帕病毒來勢洶洶,建議:每家最好備6樣?xùn)|西,關(guān)鍵時刻能救命呢

現(xiàn)代小青青慕慕
2026-01-28 08:59:53
阿富汗的鍋,拜登不背!

阿富汗的鍋,拜登不背!

山河路口
2026-01-28 23:12:39
心理學(xué)上有一個詞叫:梅拉賓法則(搞好人際關(guān)系最好的辦法)

心理學(xué)上有一個詞叫:梅拉賓法則(搞好人際關(guān)系最好的辦法)

經(jīng)濟觀察報
2025-09-01 10:06:25
1971年,劉思齊蒙冤入獄后向毛主席求助,毛澤東作出:娃娃們無罪

1971年,劉思齊蒙冤入獄后向毛主席求助,毛澤東作出:娃娃們無罪

史海孤雁
2026-01-27 18:02:17
瞞不住了!美媒:美軍高官已進臺島指揮導(dǎo)彈戰(zhàn),戰(zhàn)時先炸福建沿海

瞞不住了!美媒:美軍高官已進臺島指揮導(dǎo)彈戰(zhàn),戰(zhàn)時先炸福建沿海

史智文道
2026-01-29 16:14:14
要打就打痛!中國手段已升級,日本:中方不批準駐重慶總領(lǐng)事任命

要打就打痛!中國手段已升級,日本:中方不批準駐重慶總領(lǐng)事任命

通文知史
2026-01-28 14:30:03
太頂了!明明什么都沒露,卻性感得要命!

太頂了!明明什么都沒露,卻性感得要命!

貴圈真亂
2025-12-20 12:02:06
最新傷勢情況曝光,德約科維奇直言:對辛納絕不舉白旗

最新傷勢情況曝光,德約科維奇直言:對辛納絕不舉白旗

體育妞世界
2026-01-29 12:38:11
起風(fēng)了!加拿大已改口,禁止與中國簽協(xié)定,65萬噸油菜籽白買了?

起風(fēng)了!加拿大已改口,禁止與中國簽協(xié)定,65萬噸油菜籽白買了?

墨印齋
2026-01-29 11:47:54
加快男性衰老的因素:喝酒僅第5,排在第1的,很多男性還沒發(fā)現(xiàn)!

加快男性衰老的因素:喝酒僅第5,排在第1的,很多男性還沒發(fā)現(xiàn)!

醫(yī)學(xué)科普匯
2026-01-27 10:41:31
中國人一年少買了10萬輛進口寶馬

中國人一年少買了10萬輛進口寶馬

第一財經(jīng)資訊
2026-01-29 10:34:13
1973年,毛主席問楊振寧:萬壽無疆科學(xué)嗎?楊振寧的回答,讓主席笑了

1973年,毛主席問楊振寧:萬壽無疆科學(xué)嗎?楊振寧的回答,讓主席笑了

寄史言志
2026-01-24 17:53:13
2026-01-29 17:15:00
Java精選
Java精選
一場永遠也演不完的戲
1768文章數(shù) 3859關(guān)注度
往期回顧 全部

科技要聞

周亞輝的AI新賭局:國內(nèi)太卷 出海另起爐灶

頭條要聞

女大學(xué)生到東北游玩暈倒雪地凍傷 三根手指或面臨截肢

頭條要聞

女大學(xué)生到東北游玩暈倒雪地凍傷 三根手指或面臨截肢

體育要聞

詹姆斯哭了!騎士視頻致敬41歲超巨

娛樂要聞

張譯不再隱瞞!公開回應(yīng)退圈息影真相

財經(jīng)要聞

崔東樹:中國汽車未來年銷或達5000萬輛

汽車要聞

車長超5米還帶后輪轉(zhuǎn)向 比亞迪海豹08/海獅08將亮相

態(tài)度原創(chuàng)

游戲
教育
時尚
本地
公開課

PS+港區(qū)2月會免來了! 包含《冰點之下》等

教育要聞

“國家課程落地吳江行動叢書”發(fā)布!聚焦課改前沿,共探育人新路!

冬天大衣怎么穿才更好看?選好顏色、找對單品,高級簡約又大氣

本地新聞

云游中國|撥開云霧,巫山每幀都是航拍大片

公開課

李玫瑾:為什么性格比能力更重要?

無障礙瀏覽 進入關(guān)懷版