<cite id="ffb66"></cite><cite id="ffb66"><track id="ffb66"></track></cite>
      <legend id="ffb66"><li id="ffb66"></li></legend>
      色婷婷久,激情色播,久久久无码专区,亚洲中文字幕av,国产成人A片,av无码免费,精品久久国产,99视频精品3
      網易首頁 > 網易號 > 正文 申請入駐

      項目終于用上了 Spring 狀態機,太優雅了!

      0
      分享至

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

      1、什么是狀態機 1.1 什么是狀態

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


      狀態機,也就是 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是一種算法思想,簡單而言,有限狀態機由一組狀態、一個初始狀態、輸入和根據輸入及現有狀態轉換為下一個狀態的轉換函數組成。

      其作用主要是描述對象在它的生命周期內所經歷的狀態序列,以及如何響應來自外界的各種事件。

      2、狀態機圖

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

      以訂單為例:以從待支付狀態轉換為待發貨狀態為例


      • ①現態:是指當前所處的狀態。待支付

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

      • ③動作:條件滿足后執行的動作。動作執行完畢后,可以遷移到新的狀態,也可以仍舊保持原狀態。動作不是必需的,當條件滿足后,也可以不執行任何動作,直接遷移到新狀態。狀態轉換為待發貨

      • ④次態:條件滿足后要遷往的新狀態。“次態”是相對于“現態”而言的,“次態”一旦被激活,就轉變成新的“現態”了。待發貨 注意事項

      1、避免把某個“程序動作”當作是一種“狀態”來處理。那么如何區分“動作”和“狀態”?“動作”是不穩定的,即使沒有條件的觸發,“動作”一旦執行完畢就結束了;而“狀態”是相對穩定的,如果沒有外部條件的觸發,一個狀態會一直持續下去。

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

      3.1 狀態機spring statemachine 概述

      Spring Statemachine是應用程序開發人員在Spring應用程序中使用狀態機概念的框架。關于更多spring架構知識:https://www.yoodb.com/spring/spring-ioc-architecture.html

      Spring Statemachine旨在提供以下功能:

      1. 易于使用的扁平單級狀態機,用于簡單的使用案例。

      2. 分層狀態機結構,以簡化復雜的狀態配置。

      3. 狀態機區域提供更復雜的狀態配置。

      4. 使用觸發器,轉換,警衛和操作。

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

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

      7. 基于Zookeeper的分布式狀態機

      8. 狀態機事件監聽器。

      9. UML Eclipse Papyrus建模。

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

      11. Spring IOC集成將bean與狀態機關聯起來。

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

      在應用程序之外定義高級邏輯然后依靠狀態機來管理狀態要容易得多。您可以通過發送事件,偵聽更改或僅請求當前狀態來與狀態機進行交互。官網:https://spring.io/projects/spring-statemachine

      3.2 快速開始

      以訂單狀態扭轉的例子為例:

      表結構設計如下:

      CREATE TABLE`tb_order` (       `id`bigint(20) unsignedNOTNULL AUTO_INCREMENT COMMENT'主鍵ID',       `order_code`varchar(128) COLLATE utf8mb4_bin DEFAULTNULLCOMMENT'訂單編碼',       `status`smallint(3) DEFAULTNULLCOMMENT'訂單狀態',       `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'創建時間',       `update_time`timestampNOTNULLDEFAULT'0000-00-00 00:00:00'COMMENT'更新時間',       `create_user_code`varchar(32) COLLATE utf8mb4_bin DEFAULTNULLCOMMENT'創建人',       `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)定義狀態機狀態和事件

      狀態枚舉:

      /** * 公眾號:Java精選 */ publicenum OrderStatus {         // 待支付,待發貨,待收貨,已完成         WAIT_PAYMENT(1, "待支付"),         WAIT_DELIVER(2, "待發貨"),         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 {         // 支付,發貨,確認收貨         PAYED, DELIVERY, RECEIVED; }
      3)定義狀態機規則和配置狀態機

       @Configuration     @EnableStateMachine(name = "orderStateMachine")     publicclass OrderStateMachineConfig extends StateMachineConfigurerAdapter
      
        {         /**          * 配置狀態          *          * @param states          * @throws Exception          */         public void configure(StateMachineStateConfigurer states)  throws Exception {             states                     .withStates()                     .initial(OrderStatus.WAIT_PAYMENT)                     .states(EnumSet.allOf(OrderStatus.class));         }         /**          * 配置狀態轉換事件關系          *          * @param transitions          * @throws Exception          */         public void configure(StateMachineTransitionConfigurer transitions)  throws Exception {             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);         }     }
      

      配置持久化:

       /**  * 公眾號:Java精選  */      @Configuration     @Slf4j     publicclass Persist
      
        {         /**          * 持久化到內存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("持久化狀態機,context:{},contextObj:{}", JSON.toJSONString(context), JSON.toJSONString(contextObj));                     map.put(contextObj, context);                 }                 @Override                 public StateMachineContext read(Object contextObj) throws Exception {                     log.info("獲取狀態機,contextObj:{}", JSON.toJSONString(contextObj));                     StateMachineContext stateMachineContext = (StateMachineContext) map.get(contextObj);                     log.info("獲取狀態機結果,stateMachineContext:{}", JSON.toJSONString(stateMachineContext));                     return stateMachineContext;                 }                 private Map map = new HashMap();             });         }              @Resource         private RedisConnectionFactory redisConnectionFactory;         /**          * 持久化到redis中,在分布式系統中使用          *          * @return          */         @Bean(name = "stateMachineRedisPersister")         public RedisStateMachinePersister   getRedisPersister() {             RedisStateMachineContextRepository repository =  new RedisStateMachineContextRepository<>(redisConnectionFactory);             RepositoryStateMachinePersist p = new RepositoryStateMachinePersist<>(repository);             returnnew RedisStateMachinePersister<>(p);         }     }
      
      4)業務系統

      controller:

       /**  * 公眾號:Java精選  */      @RestController     @RequestMapping("/order")     publicclass OrderController {         @Resource         private OrderService orderService;         /**          * 根據id查詢訂單          *          * @return          */         @RequestMapping("/getById")         public Order getById(@RequestParam("id") Long id) {             //根據id查詢訂單             Order order = orderService.getById(id);             return order;         }         /**          * 創建訂單          *          * @return          */         @RequestMapping("/create")         public String create(@RequestBody Order order) {             //創建訂單             orderService.create(order);             return"sucess";         }         /**          * 對訂單進行支付          *          * @param id          * @return          */         @RequestMapping("/pay")         public String pay(@RequestParam("id") Long id) {             //對訂單進行支付             orderService.pay(id);             return"success";         }              /**          * 對訂單進行發貨          *          * @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;         /**          * 創建訂單          *          * @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("線程名稱:{},支付失敗, 狀態異常,訂單信息:{}", Thread.currentThread().getName(), order);                 thrownew RuntimeException("支付失敗, 訂單狀態異常");             }             return order;         }         /**          * 對訂單進行發貨          *          * @param id          * @return          */         public Order deliver(Long id) {             Order order = orderMapper.selectById(id);             log.info("線程名稱:{},嘗試發貨,訂單號:{}" ,Thread.currentThread().getName() , id);             if (!sendEvent(OrderStatusChangeEvent.DELIVERY, order)) {                 log.error("線程名稱:{},發貨失敗, 狀態異常,訂單信息:{}", Thread.currentThread().getName(), order);                 thrownew RuntimeException("發貨失敗, 訂單狀態異常");             }             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("線程名稱:{},收貨失敗, 狀態異常,訂單信息:{}", Thread.currentThread().getName(), order);                 thrownew RuntimeException("收貨失敗, 訂單狀態異常");             }             return order;         }         /**          * 發送訂單狀態轉換事件          * synchronized修飾保證這個方法是線程安全的          *          * @param changeEvent          * @param order          * @return          */         private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {             boolean result = false;             try {                 //啟動狀態機                 orderStateMachine.start();                 //嘗試恢復狀態機狀態                 stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));                 Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();                 result = orderStateMachine.sendEvent(message);                 //持久化狀態機狀態                 stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));             } catch (Exception e) {                 log.error("訂單操作失敗:{}", e);             } finally {                 orderStateMachine.stop();             }             return result;         }     }
      

      監聽狀態的變化:

       /**  * 公眾號: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("支付,狀態機反饋信息:{}",  message.getHeaders().toString());             //更新訂單             order.setStatus(OrderStatus.WAIT_DELIVER.getKey());             orderMapper.updateById(order);             //TODO 其他業務         }         @OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")         public void deliverTransition(Message message)  {             Order order = (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")         public void receiveTransition(Message message)  {             Order order = (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

      報錯如下:


      2)驗證持久化

      內存。java進階技術路線:https://www.yoodb.com/

      使用內存持久化類持久化:

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

      問題現象

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

      監聽事件拋出異常,在發送事件中無法感知:

       private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {         boolean result = false;         try {             //啟動狀態機             orderStateMachine.start();             //嘗試恢復狀態機狀態             stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));             Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();              //事件執行異常了,依然返回true,無法感知異常             result = orderStateMachine.sendEvent(message);             if(result){                 //持久化狀態機狀態,如果根據true持久化,則會出現問題                 stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));             }         } catch (Exception e) {             log.error("訂單操作失敗:{}", e);         } finally {             orderStateMachine.stop();         }         return result;     }

      調試發現:發送事件和監聽事件是一個線程,發送事件的結果是在監聽操作執行完之后才返回


      監聽線程:


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

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

      方法把執行狀態放入這個變量中

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

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

      發送事件改造:如果獲取到業務執行異常,則返回失敗,不進行狀態機持久化 com.zengqingfa.springboot.state.demo.service.impl.OrderServiceImpl##sendEvent

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

      代碼優化

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

      //獲取到監聽的結果信息 Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition + order.getId());
      • 監聽設置狀態的代碼有重復代碼,需要進行優化,可使用aop

      try {         //TODO 其他業務         //成功 則為1         orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);     } catch (Exception e) {         //如果出現異常,則進行回滾         log.error("payTransition 出現異常:{}",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";     }

      支付發送事件: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("線程名稱:{},支付失敗, 狀態異常,訂單信息:{}", Thread.currentThread().getName(), order);             thrownew RuntimeException("支付失敗, 訂單狀態異常");         }         return order;     }          /**      * 發送訂單狀態轉換事件      * synchronized修飾保證這個方法是線程安全的      *      * @param changeEvent      * @param order      * @return      */     private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order,String key){         boolean result = false;         try {             //啟動狀態機             orderStateMachine.start();             //嘗試恢復狀態機狀態             stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));             Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();             result = orderStateMachine.sendEvent(message);             if(!result){                 returnfalse;             }             //獲取到監聽的結果信息             Integer o = (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 (Exception e) {             log.error("訂單操作失敗:{}", e);         } finally {             orderStateMachine.stop();         }         return result;     }

      使用aop對監聽事件切面,把業務執行結果封裝到狀態機的變量中,注解:

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

      監聽類使用注解:

       @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("支付,狀態機反饋信息:{}", message.getHeaders().toString());             //更新訂單             order.setStatus(OrderStatus.WAIT_DELIVER.getKey());             orderMapper.updateById(order);             //TODO 其他業務             //模擬異常             if (Objects.equals(order.getName(), "A")) {                 thrownew RuntimeException("執行業務異常");             }         }         @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("發貨,狀態機反饋信息:{}", message.getHeaders().toString());             //更新訂單             order.setStatus(OrderStatus.WAIT_RECEIVE.getKey());             orderMapper.updateById(order);             //TODO 其他業務         }         @OnTransition(source = "WAIT_RECEIVE", target = "FINISH")         @LogResult(key = CommonConstants.receiveTransition)         public void receiveTransition(Message message)  {             Order order = (Order) message.getHeaders().get("order");             log.info("確認收貨,狀態機反饋信息:{}", message.getHeaders().toString());             //更新訂單             order.setStatus(OrderStatus.FINISH.getKey());             orderMapper.updateById(order);             //TODO 其他業務         }     }

      作者:betheme

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

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

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

      文章有幫助的話,點在看,轉發吧!

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

      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.

      相關推薦
      熱點推薦
      水貨實錘?才一年就全面下滑,這可是狀元啊,老鷹虧大發了

      水貨實錘?才一年就全面下滑,這可是狀元啊,老鷹虧大發了

      球童無忌
      2025-12-20 23:40:16
      輸吉林發布會!劉煒將防守甩鍋納托爾,不滿籃板,西爾扎提談失誤

      輸吉林發布會!劉煒將防守甩鍋納托爾,不滿籃板,西爾扎提談失誤

      籃球資訊達人
      2025-12-21 02:56:57
      停飛中國航班,拒絕中國游客,這個在東南亞小國”處處和中國作對

      停飛中國航班,拒絕中國游客,這個在東南亞小國”處處和中國作對

      南權先生
      2025-12-20 17:01:37
      68歲青島市人大常委會原主任被查,今年已有61名中管干部落馬

      68歲青島市人大常委會原主任被查,今年已有61名中管干部落馬

      上觀新聞
      2025-12-19 18:57:02
      寧波衛健委回應小洛熙事件最新進展:醫療事故鑒定仍在進行

      寧波衛健委回應小洛熙事件最新進展:醫療事故鑒定仍在進行

      極目新聞
      2025-12-20 19:49:35
      北京阿姨20年守茅臺股票:90萬本金,分紅326萬,成本歸零!

      北京阿姨20年守茅臺股票:90萬本金,分紅326萬,成本歸零!

      趣文說娛
      2025-12-20 18:29:20
      2026央視跨年晚會陣容曝光,看完明星名單難掩激動,該來的都來了

      2026央視跨年晚會陣容曝光,看完明星名單難掩激動,該來的都來了

      阿纂看事
      2025-12-19 14:38:58
      僅差32次!僅差45個!老詹迎來兩項神級里程碑,愈老愈妖

      僅差32次!僅差45個!老詹迎來兩項神級里程碑,愈老愈妖

      世界體育圈
      2025-12-20 13:22:08
      掛斷中方電話后,馬杜羅下令出兵,油輪保衛戰打響,白宮措辭變強

      掛斷中方電話后,馬杜羅下令出兵,油輪保衛戰打響,白宮措辭變強

      策前論
      2025-12-20 23:12:33
      12架日機攜48導彈直撲遼寧艦,美媒直接報喪:被解放軍全面碾壓?

      12架日機攜48導彈直撲遼寧艦,美媒直接報喪:被解放軍全面碾壓?

      科普100克克
      2025-12-16 16:26:18
      央企“最牛女副處長”落馬:兩年與上司開房410次,細節曝光

      央企“最牛女副處長”落馬:兩年與上司開房410次,細節曝光

      西門老爹
      2025-12-16 15:35:31
      普京拒付金正恩派兵報酬,因朝鮮軍隊戰績不佳,僅支付20%報酬

      普京拒付金正恩派兵報酬,因朝鮮軍隊戰績不佳,僅支付20%報酬

      環球熱點快評
      2025-12-17 09:19:03
      上海一對情侶戀愛時男方說“我養你”,分手后女方以此為證,拒絕返還40余萬元!測謊實驗后,法院判了

      上海一對情侶戀愛時男方說“我養你”,分手后女方以此為證,拒絕返還40余萬元!測謊實驗后,法院判了

      都市快報橙柿互動
      2025-12-20 09:35:12
      商K上線了新花樣…

      商K上線了新花樣…

      微微熱評
      2025-12-20 15:22:00
      明宣宗朱瞻基陵墓,下周一開放

      明宣宗朱瞻基陵墓,下周一開放

      上觀新聞
      2025-12-20 16:31:19
      市監局通報“南極磷蝦油”事件:已成立聯合調查組進駐北京同仁堂(四川)健康藥業有限公司開展調查,已對該企業立案

      市監局通報“南極磷蝦油”事件:已成立聯合調查組進駐北京同仁堂(四川)健康藥業有限公司開展調查,已對該企業立案

      極目新聞
      2025-12-20 19:28:56
      國乒教練組巨震,林詩棟新教練讓人意外,王曼昱主管教練情理之中

      國乒教練組巨震,林詩棟新教練讓人意外,王曼昱主管教練情理之中

      月亮的麥片
      2025-12-20 21:18:01
      繼父、同母異父弟弟?墜亡女教師身世曝光,這才是她走絕路的真相

      繼父、同母異父弟弟?墜亡女教師身世曝光,這才是她走絕路的真相

      知法而形
      2025-12-19 23:33:57
      1984年他一聲令下,把老山幾千噸炮彈當水潑,2019年葬禮現場,昔日部下已是軍委副主席,含淚送別這位鐵血師長!

      1984年他一聲令下,把老山幾千噸炮彈當水潑,2019年葬禮現場,昔日部下已是軍委副主席,含淚送別這位鐵血師長!

      史海孤雁
      2025-12-17 16:50:24
      凡人微光 | “童”行有你

      凡人微光 | “童”行有你

      新華社
      2025-12-20 13:14:20
      2025-12-21 05:43:00
      Java精選
      Java精選
      一場永遠也演不完的戲
      1764文章數 3859關注度
      往期回顧 全部

      科技要聞

      許四清:具身智能的"ChatGPT時刻"還未到來

      頭條要聞

      高市早苗擔心被邊緣化 要趕在特朗普訪華前行動

      頭條要聞

      高市早苗擔心被邊緣化 要趕在特朗普訪華前行動

      體育要聞

      我開了20年大巴,現在是一名西甲主帥

      娛樂要聞

      2026央視跨年晚會陣容曝光,豪華陣仗

      財經要聞

      求解“地方財政困難”

      汽車要聞

      嵐圖推進L3量產測試 已完成11萬公里實際道路驗證

      態度原創

      家居
      房產
      時尚
      藝術
      軍事航空

      家居要聞

      高端私宅 理想隱居圣地

      房產要聞

      廣州有態度,一座國際化社區給出的城市答案

      最顯腿細的騎士靴,誰穿誰是腿精

      藝術要聞

      中國老香煙品牌,你知道多少?

      軍事要聞

      澤連斯基:前線局勢愈發艱難

      無障礙瀏覽 進入關懷版 主站蜘蛛池模板: 99在线国产视频| 久久一本人碰碰人碰| 人妻无码专区精品| 国产精品99久久久久久成人| 国产综合视频一区二区三区| 亚洲中文字| 国产黄色免费看| 中文字幕在线播放| 无码人妻一区二区三区线花季传件| xxx69国产| 亚洲www啪成人一区二区| wwwjizzjizzjizz| 91视频免费观看| 中文无码人妻有码人妻中文字幕| 国精品99久9在线 | 免费| 麻豆一区二区三区精品视频 | 伊人久久大香线蕉精品,亚洲国产一成人久久精品,久久99精品久久久久久三级,亚 | 欧美?级毛片一进一出夜本色| 九一九色国产| 草草地址线路①屁屁影院成人| 日产国产精品亚洲系列| 白银市| 男人天堂中文字幕| 毛片内射久久久一区| 夜夜添狠狠添高潮出水| 中文字幕无码av波多野吉衣| 自拍偷拍第一页| 91在线观看| 又粗又黄又硬又爽的免费视频| 成人精品免费视频在线观看| 免费视频国产在线观看| 国内自拍欧美亚洲| 97色婷婷| 欧美777| 亚洲AV无码成人片在线| 豆国产97在线 | 亚洲| 亚洲无码一二| 国产97在线?|?日韩| 青青av| 国产精品理论片| 真人作爱免费视频|