Springboot集成SpringState狀態(tài)機(jī)的實現(xiàn)
1.SpringState 簡介
狀態(tài)機(jī)核心概念??
項目 | 說明 |
---|---|
狀態(tài)(State)?? | 對象生命周期中的特定條件(如訂單的待支付、已發(fā)貨) |
事件(Event)?? | 觸發(fā)狀態(tài)轉(zhuǎn)換的動作(如支付成功、取消訂單) |
轉(zhuǎn)換(Transition)?? | 定義事件如何驅(qū)動狀態(tài)遷移(如待支付 → 支付事件 → 待發(fā)貨) |
守衛(wèi)(Guard)?? | 條件檢查,決定是否允許轉(zhuǎn)換(如“僅未超時訂單可支付”) |
??動作(Action)?? | 條件檢查,決定是否允許轉(zhuǎn)換(如“僅未超時訂單可支付”) |
應(yīng)用場景
訂單生命周期管理??
管理訂單從創(chuàng)建到完成的完整流程(如待支付 → 待發(fā)貨 → 已完成)工作流引擎??
審批流程的狀態(tài)控制(如提交 → 審核中 → 已批準(zhǔn))??游戲狀態(tài)流轉(zhuǎn)??
角色狀態(tài)切換(如空閑 → 戰(zhàn)斗 → 死亡)物聯(lián)網(wǎng)設(shè)備監(jiān)控??
設(shè)備狀態(tài)跟蹤(如離線 → 在線 → 故障)
2.狀態(tài)機(jī)示例
2.1 項目結(jié)構(gòu)和依賴包
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>spring-state-m</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>21</maven.compiler.source> <maven.compiler.target>21</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring-boot.version>3.5.3</spring-boot.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- Spring State Machine --> <dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-starter</artifactId> <version>4.0.0</version> </dependency> <!-- Spring State Machine Redis Persistence --> <dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-data-redis</artifactId> <version>4.0.0</version> </dependency> </dependencies> </project>
啟動類
package org.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @EnableAsync @SpringBootApplication public class SpringStateMachine { public static void main(String[] args) { SpringApplication.run(SpringStateMachine.class, args); } }
2.2 定義事件類和狀態(tài)類
事件用于驅(qū)動狀態(tài)轉(zhuǎn)移,狀態(tài)用于記錄事件進(jìn)度
事件類
package org.example.common; /** * @Author zhx && moon * @Since 21 * @Date 2025-06-18 PM 6:38 */ public enum OrderEvent { PAY, // 支付操作 SHIP, // 發(fā)貨操作 CONFIRM, // 確認(rèn)收貨 CANCEL // 取消訂單 }
狀態(tài)類
package org.example.common; /** * @Author zhx && moon * @Since 21 * @Date 2025-06-18 PM 6:37 */ public enum OrderState { UNPAID, // 待支付 PAID, // 已支付 SHIPPED, // 已發(fā)貨 CONFIRMED, // 已確認(rèn)收貨 CANCELLED // 已取消 }
2.3 Spring 事件監(jiān)聽器
Spring 事件監(jiān)聽器,用于異步處理事件流,當(dāng)狀態(tài)機(jī)結(jié)束時,推送當(dāng)前狀態(tài)機(jī)到監(jiān)聽器,監(jiān)聽器則從持久化中刪除該狀態(tài)機(jī)
package org.example.config; import org.example.entity.OrderSMContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; /** * @Author zhx && moon * @Since 21 * @Date 2025-06-20 AM 10:18 */ @Component public class AsyncEventListener { Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired SMContainer smContainer; @Async @EventListener public void handleAsyncEvent(OrderSMContext context) { logger.info("order id {} has delete {}", context.getOrderId(), smContainer.delete(context.getOrderId())); } }
2.4 狀態(tài)機(jī)持久化類
利用 Redis 做狀態(tài)機(jī)的持久化存儲
2.4.1 Redis 狀態(tài)機(jī)持久化容器
package org.example.config; import org.example.common.OrderEvent; import org.example.common.OrderState; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.statemachine.data.redis.RedisStateMachineContextRepository; import org.springframework.statemachine.persist.RepositoryStateMachinePersist; import org.springframework.stereotype.Component; /** * @Author zhx && moon * @Since 21 * @Date 2025-06-18 PM 6:54 */ @Component public class MYRedisPerSisterConfig { @Autowired private RedisConnectionFactory factory; /** * 創(chuàng)建 RedisStateMachineRepository 實例 */ @Bean(name = "redisStateMachineContextRepository") public MYRedisStateMachinePer<OrderState, OrderEvent> getRedisPerSister() { // 創(chuàng)建 RedisStateMachineRepository 實例 RedisStateMachineContextRepository<OrderState, OrderEvent> repository = new RedisStateMachineContextRepository<>(factory); // 持久化 RepositoryStateMachinePersist perSister = new RepositoryStateMachinePersist(repository); // 獲取 Redis StateMachinePerSister 實例 MYRedisStateMachinePer machine = new MYRedisStateMachinePer<>(perSister); RedisTemplate<String, byte[]> redisTemplate = createDefaultTemplate(factory); machine.setRedisTemplate(redisTemplate); // 返回 return machine; } /** * 與 RedisStateMachineContextRepository 使用相同的序列化配置 * @param connectionFactory * @return */ private static RedisTemplate<String, byte[]> createDefaultTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<String, byte[]> template = new RedisTemplate(); template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setConnectionFactory(connectionFactory); template.afterPropertiesSet(); return template; } }
2.4.2 Redis 配置
package org.example.config; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.statemachine.StateMachinePersist; import org.springframework.statemachine.data.redis.RedisStateMachinePersister; /** * @Author zhx && moon * @Since 21 * @Date 2025-06-19 PM 5:21 */ public class MYRedisStateMachinePer<S, E> extends RedisStateMachinePersister<S, E> { RedisTemplate<String, byte[]> redisTemplate; public MYRedisStateMachinePer(StateMachinePersist<S, E, String> stateMachinePersist) { super(stateMachinePersist); } public void setRedisTemplate(RedisTemplate<String, byte[]> redisTemplate){ this.redisTemplate = redisTemplate; } /** * 檢查 Redis 中是否存在指定的 key * @param key * @return */ public boolean isUnExist(String key){ return !this.redisTemplate.hasKey(key); } /** * 刪除 Redis 中指定的 key * @param key * @return */ public boolean deleteKey(String key){ return this.redisTemplate.delete(key); } }
2.4.3 狀態(tài)機(jī)監(jiān)聽器
用于監(jiān)聽狀態(tài)機(jī)狀態(tài)變化
package org.example.config; import org.example.common.OrderEvent; import org.example.common.OrderState; import org.example.entity.OrderSMContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.messaging.Message; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.listener.StateMachineListenerAdapter; import org.springframework.statemachine.state.State; import org.springframework.stereotype.Component; /** * @Author zhx && moon * @Since 21 * @Date 2025-06-19 PM 4:58 */ @Component public class RedStateMachineListener extends StateMachineListenerAdapter<OrderState, OrderEvent> { Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private ApplicationEventPublisher publisher; /** * 狀態(tài)變化 * @param from * @param to */ @Override public void stateChanged(State<OrderState, OrderEvent> from, State<OrderState, OrderEvent> to) { // 狀態(tài)變更時的處理 if (null == from) { logger.info("state machine init, from Init to {}", to.getId()); } else { logger.info("state machine change, from {} to {}", from.getId(), to.getId()); } } /** * 狀態(tài)機(jī)啟動成功時的回調(diào) * @param sm */ @Override public void stateMachineStarted(StateMachine<OrderState, OrderEvent> sm) { logger.info("state machine {} start success.", sm.getId()); } /** * 狀態(tài)機(jī)結(jié)束的回調(diào) * @param sm */ @Override public void stateMachineStopped(StateMachine<OrderState, OrderEvent> sm) { logger.info("state machine {} stop success.", sm.getId()); publisher.publishEvent(new OrderSMContext(sm.getId())); } @Override public void eventNotAccepted(Message<OrderEvent> event) { logger.error("Event not accepted: {}", event.getPayload()); } }
2.5 裝機(jī)器容器
用于管理狀態(tài)機(jī)的創(chuàng)建,本地化緩存與持久化存儲
package org.example.config; import org.example.common.OrderEvent; import org.example.common.OrderState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.config.StateMachineFactory; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * @Author zhx && moon * @Since 21 * @Date 2025-06-19 PM 4:30 */ @Component public class SMContainer { Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private StateMachineFactory<OrderState, OrderEvent> factory; @Autowired private MYRedisStateMachinePer<OrderState, OrderEvent> myRedisStateMachinePer; private Map<String, StateMachine<OrderState, OrderEvent>> map = new HashMap<>(16); /** * 獲取狀態(tài)機(jī) * @param orderId 訂單ID * @return 狀態(tài)機(jī)實例 */ public synchronized StateMachine<OrderState, OrderEvent> getStateMachine(String orderId) { String key = getKey(orderId); try { // 取緩存 StateMachine<OrderState, OrderEvent> sm = map.get(orderId); // 校驗 if (Objects.isNull(sm)) { // 獲取狀態(tài)機(jī)實例 sm = factory.getStateMachine(orderId); // 校驗是否存在 if (myRedisStateMachinePer.isUnExist(key)) { sm.startReactively().subscribe(); myRedisStateMachinePer.persist(sm, key); } else { // 恢復(fù)狀態(tài) myRedisStateMachinePer.restore(sm, key); } // 緩存狀態(tài)機(jī) map.put(orderId, sm); } return sm; } catch (Exception e) { logger.error("get state machine error: {}", e.getMessage(), e); return null; } } /** * 保存狀態(tài)機(jī) * @param orderId * @param stateMachine */ public synchronized boolean save(StateMachine<OrderState, OrderEvent> stateMachine, String orderId){ try { String key = getKey(orderId); myRedisStateMachinePer.persist(stateMachine, key); return true; } catch (Exception e) { logger.error("save state machine error: {}", e.getMessage(), e); return false; } } /** * 刪除狀態(tài)機(jī) * @param orderId * @return */ public synchronized boolean delete(String orderId){ return myRedisStateMachinePer.deleteKey(getKey(orderId)); } /** * 獲取 KEY * @param orderId * @return */ private String getKey(String orderId){ return "STATE-MACHINE:" +orderId; } }
2.6 狀態(tài)機(jī)事件發(fā)送器
用于統(tǒng)一發(fā)送狀態(tài)機(jī)事件,管理事件發(fā)送過程
package org.example.config; import org.example.common.OrderEvent; import org.example.common.OrderState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.StateMachineEventResult; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; import java.util.concurrent.atomic.AtomicBoolean; /** * @Author zhx && moon * @Since 21 * @Date 2025-06-20 PM 2:22 */ @Component public class SMEventSender { Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired SMContainer smContainer; /** * 初始化訂單狀態(tài)機(jī) * @param orderId */ public void initOrderStateMachine(String orderId) { smContainer.getStateMachine(orderId); } /** * 發(fā)送事件 * @param orderId * @param event * @return */ public boolean send(String orderId, OrderEvent event) { // 獲取狀態(tài) StateMachine<OrderState, OrderEvent> sm = smContainer.getStateMachine(orderId); // 構(gòu)建事件消息 Message<OrderEvent> message = MessageBuilder .withPayload(event) .setHeader("orderId", orderId) // 訂單對象關(guān)聯(lián)狀態(tài)機(jī) .build(); // 發(fā)送事件 AtomicBoolean result = new AtomicBoolean(false); sm.sendEvent(Mono.just(message)).subscribe(r->{ if (r.getResultType() == StateMachineEventResult.ResultType.ACCEPTED) { // 成功 result.set(true); // 在未完成時持久化 if (!OrderEvent.CONFIRM.equals(event)) { smContainer.save(sm, orderId); } } else { result.set(false); } }); // 輸出 logger.info("send event: {}, orderId: {}, result: {}", event, orderId, result.get()); // 返回 return result.get(); } }
2.7 狀態(tài)機(jī)配置
狀態(tài)機(jī)配置,定義事件和狀態(tài)關(guān)系,以及守衛(wèi)和動作
package org.example.config; import org.example.common.OrderEvent; import org.example.common.OrderState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.statemachine.config.EnableStateMachineFactory; import org.springframework.statemachine.config.StateMachineConfigurerAdapter; import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; import java.util.EnumSet; /** * @Author zhx && moon * @Since 21 * @Date 2025-06-18 PM 6:38 */ @Configuration @EnableStateMachineFactory public class StateMachineConfig extends StateMachineConfigurerAdapter<OrderState, OrderEvent> { Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired RedStateMachineListener listener; @Override public void configure(StateMachineConfigurationConfigurer<OrderState, OrderEvent> config) throws Exception { config.withConfiguration() // 注冊監(jiān)聽器 .listener(listener); } /** * 狀態(tài)機(jī)初始化 * @param states * @throws Exception */ @Override public void configure(StateMachineStateConfigurer<OrderState, OrderEvent> states) throws Exception { states.withStates() .initial(OrderState.UNPAID) .states(EnumSet.allOf(OrderState.class)) .end(OrderState.CONFIRMED) .end(OrderState.CANCELLED); } /** * 狀態(tài)轉(zhuǎn)移邏輯 * @param transitions * @throws Exception */ @Override public void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) throws Exception { transitions // 支付:UNPAID -> PAID .withExternal() .source(OrderState.UNPAID) .target(OrderState.PAID) .guard(context -> { // (前置)守衛(wèi)條件,校驗支付結(jié)果 logger.info("check order {} pay result ...", context.getMessageHeader("orderId")); return true; }) .action(context -> { // (后置)觸發(fā)動作,通知倉庫備貨 logger.info("order {} pay success, notify warehouse to prepare goods ...", context.getMessageHeader("orderId")); }) .event(OrderEvent.PAY) .and() // 發(fā)貨:PAID -> SHIPPED .withExternal() .source(OrderState.PAID) .target(OrderState.SHIPPED) .event(OrderEvent.SHIP) .and() // 確認(rèn)收貨:SHIPPED -> CONFIRMED .withExternal() .source(OrderState.SHIPPED) .target(OrderState.CONFIRMED) .event(OrderEvent.CONFIRM) .and() // 取消訂單(僅在待支付可取消) .withExternal() .source(OrderState.UNPAID) .target(OrderState.CANCELLED) .event(OrderEvent.CANCEL); } }
2.8 接口類
用于模擬訂單操作
package org.example.controller; import org.example.service.OrderService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @Author zhx && moon * @Since 21 * @Date 2025-06-18 PM 6:41 */ @RestController @RequestMapping("/orders") public class OrderController { @Autowired private OrderService orderService; @GetMapping("/create") public String create() { return orderService.createOrder(); } @GetMapping("/pay/{orderId}") public String pay(@PathVariable("orderId") String orderId) { return orderService.payOrder(orderId); } @GetMapping("/shipped/{orderId}") public String shipped(@PathVariable("orderId") String orderId) { return orderService.shipped(orderId); } @GetMapping("/confirm/{orderId}") public String confirm(@PathVariable("orderId") String orderId) { return orderService.confirm(orderId); } @GetMapping("/{orderId}/status") public String status(@PathVariable String orderId) { return "當(dāng)前狀態(tài): " + orderService.getOrderState(orderId); } }
2.9 實現(xiàn)類
package org.example.service; import org.example.common.OrderEvent; import org.example.common.OrderState; import org.example.config.SMEventSender; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @Author zhx && moon * @Since 21 * @Date 2025-06-18 PM 6:40 */ @Service public class OrderService { @Autowired SMEventSender smEventSender; /** * 創(chuàng)建訂單并初始化狀態(tài)機(jī) * @return */ public String createOrder() { try { // 使用時間戳作為訂單ID String orderId = String.valueOf(System.currentTimeMillis()); // 初始化 smEventSender.initOrderStateMachine(orderId); // 返回訂單ID return orderId; } catch (Exception e) { return e.getMessage(); } } /** * 支付 * @param orderId * @return */ public String payOrder(String orderId) { try { boolean result = smEventSender.send(orderId, OrderEvent.PAY); return "success:" + result; } catch (Exception e) { return e.getMessage(); } } /** * 發(fā)貨 * @param orderId * @return */ public String shipped(String orderId) { try { boolean result = smEventSender.send(orderId, OrderEvent.SHIP); return "success:" + result; } catch (Exception e) { return e.getMessage(); } } /** * 確認(rèn)收貨 * @param orderId * @return */ public String confirm(String orderId) { try { boolean result = smEventSender.send(orderId, OrderEvent.CONFIRM); return "success:" + result; } catch (Exception e) { return e.getMessage(); } } public OrderState getOrderState(String orderId) { return OrderState.UNPAID; } }
2.10 狀態(tài)機(jī)上下文
用于管理狀態(tài)機(jī)信息
package org.example.entity; import org.example.common.OrderState; import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; /** * @Author zhx && moon * @Since 21 * @Date 2025-06-18 PM 6:54 */ public class OrderSMContext { public OrderSMContext(String orderId){ this.orderId = orderId; } private String orderId; private OrderState currentState; private Map<String, Object> extendedState = new HashMap<>(); private LocalDateTime createdAt; private LocalDateTime lastModifiedAt; public String getOrderId() { return orderId; } public void setOrderId(String orderId) { this.orderId = orderId; } }
2.11 配置文件
spring: data: redis: database: 0 host: 192.168.1.103 port: 6379 password: 123456 timeout: 5000
3.狀態(tài)機(jī)測試
3.1創(chuàng)建訂單
3.2持久化結(jié)果
初始化時自動將數(shù)據(jù)持久化到 Redis
3.3 支付訂單
3.4 發(fā)貨
3.5 確認(rèn)收貨
后臺日志
到此這篇關(guān)于Springboot集成SpringState狀態(tài)機(jī)的實現(xiàn)的文章就介紹到這了,更多相關(guān)SpringState 狀態(tài)機(jī)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JAVA HashSet和TreeSet 保證存入元素不會重復(fù)的操作
這篇文章主要介紹了JAVA HashSet和TreeSet 保證存入元素不會重復(fù)的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09Spring+SpringMVC+MyBatis深入學(xué)習(xí)及搭建(二)之MyBatis原始Dao開發(fā)和mapper代理開發(fā)
這篇文章主要介紹了Spring+SpringMVC+MyBatis深入學(xué)習(xí)及搭建(二)之MyBatis原始Dao開發(fā)和mapper代理開發(fā),需要的朋友可以參考下2017-05-05freemarker?jsp?java內(nèi)存方式實現(xiàn)分頁示例
這篇文章主要介紹了freemarker?jsp?java內(nèi)存方式實現(xiàn)分頁示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06spring-boot整合dubbo:Spring-boot-dubbo-starter
這篇文章主要介紹了spring-boot整合dubbo:Spring-boot-dubbo-starter的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-05-05Spring boot中自定義Json參數(shù)解析器的方法
這篇文章主要介紹了Spring boot中自定義Json參數(shù)解析器的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01java中VO PO DTO POJO BO DO對象的應(yīng)用場景及使用
文章介紹了Java開發(fā)中常用的幾種對象類型及其應(yīng)用場景,包括VO、PO、DTO、POJO、BO和DO等,并通過示例說明了它們在不同場景下的應(yīng)用2025-01-01Netty分布式NioEventLoop任務(wù)隊列執(zhí)行源碼分析
這篇文章主要為大家介紹了Netty分布式NioEventLoop任務(wù)隊列執(zhí)行源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03Java里得到00:00:00格式的時分秒的Timestamp
Java里如何得到00:00:00格式的時分秒的Timestamp ,下面是具體的實現(xiàn)代碼,需要的朋友可以參考下。2009-09-09