1.springstate 简介
状态机核心概念
项目 | 说明 |
---|---|
状态(state) | 对象生命周期中的特定条件(如订单的待支付、已发货) |
事件(event) | 触发状态转换的动作(如支付成功、取消订单) |
转换(transition) | 定义事件如何驱动状态迁移(如待支付 → 支付事件 → 待发货) |
守卫(guard) | 条件检查,决定是否允许转换(如“仅未超时订单可支付”) |
动作(action) | 条件检查,决定是否允许转换(如“仅未超时订单可支付”) |
应用场景
订单生命周期管理
管理订单从创建到完成的完整流程(如待支付 → 待发货 → 已完成)工作流引擎
审批流程的状态控制(如提交 → 审核中 → 已批准)游戏状态流转
角色状态切换(如空闲 → 战斗 → 死亡)物联网设备监控
设备状态跟踪(如离线 → 在线 → 故障)
2.状态机示例
2.1 项目结构和依赖包
<?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 定义事件类和状态类
事件用于驱动状态转移,状态用于记录事件进度
事件类
package org.example.common; /** * @author zhx && moon * @since 21 * @date 2025-06-18 pm 6:38 */ public enum orderevent { pay, // 支付操作 ship, // 发货操作 confirm, // 确认收货 cancel // 取消订单 }
状态类
package org.example.common; /** * @author zhx && moon * @since 21 * @date 2025-06-18 pm 6:37 */ public enum orderstate { unpaid, // 待支付 paid, // 已支付 shipped, // 已发货 confirmed, // 已确认收货 cancelled // 已取消 }
2.3 spring 事件监听器
spring 事件监听器,用于异步处理事件流,当状态机结束时,推送当前状态机到监听器,监听器则从持久化中删除该状态机
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 状态机持久化类
利用 redis 做状态机的持久化存储
2.4.1 redis 状态机持久化容器
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; /** * 创建 redisstatemachinerepository 实例 */ @bean(name = "redisstatemachinecontextrepository") public myredisstatemachineper<orderstate, orderevent> getredispersister() { // 创建 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 状态机监听器
用于监听状态机状态变化
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; /** * 状态变化 * @param from * @param to */ @override public void statechanged(state<orderstate, orderevent> from, state<orderstate, orderevent> to) { // 状态变更时的处理 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()); } } /** * 状态机启动成功时的回调 * @param sm */ @override public void statemachinestarted(statemachine<orderstate, orderevent> sm) { logger.info("state machine {} start success.", sm.getid()); } /** * 状态机结束的回调 * @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 装机器容器
用于管理状态机的创建,本地化缓存与持久化存储
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); /** * 获取状态机 * @param orderid 订单id * @return 状态机实例 */ public synchronized statemachine<orderstate, orderevent> getstatemachine(string orderid) { string key = getkey(orderid); try { // 取缓存 statemachine<orderstate, orderevent> sm = map.get(orderid); // 校验 if (objects.isnull(sm)) { // 获取状态机实例 sm = factory.getstatemachine(orderid); // 校验是否存在 if (myredisstatemachineper.isunexist(key)) { sm.startreactively().subscribe(); myredisstatemachineper.persist(sm, key); } else { // 恢复状态 myredisstatemachineper.restore(sm, key); } // 缓存状态机 map.put(orderid, sm); } return sm; } catch (exception e) { logger.error("get state machine error: {}", e.getmessage(), e); return null; } } /** * 保存状态机 * @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; } } /** * 删除状态机 * @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 状态机事件发送器
用于统一发送状态机事件,管理事件发送过程
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; /** * 初始化订单状态机 * @param orderid */ public void initorderstatemachine(string orderid) { smcontainer.getstatemachine(orderid); } /** * 发送事件 * @param orderid * @param event * @return */ public boolean send(string orderid, orderevent event) { // 获取状态 statemachine<orderstate, orderevent> sm = smcontainer.getstatemachine(orderid); // 构建事件消息 message<orderevent> message = messagebuilder .withpayload(event) .setheader("orderid", orderid) // 订单对象关联状态机 .build(); // 发送事件 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 状态机配置
状态机配置,定义事件和状态关系,以及守卫和动作
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() // 注册监听器 .listener(listener); } /** * 状态机初始化 * @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); } /** * 状态转移逻辑 * @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 -> { // (前置)守卫条件,校验支付结果 logger.info("check order {} pay result ...", context.getmessageheader("orderid")); return true; }) .action(context -> { // (后置)触发动作,通知仓库备货 logger.info("order {} pay success, notify warehouse to prepare goods ...", context.getmessageheader("orderid")); }) .event(orderevent.pay) .and() // 发货:paid -> shipped .withexternal() .source(orderstate.paid) .target(orderstate.shipped) .event(orderevent.ship) .and() // 确认收货: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 "当前状态: " + orderservice.getorderstate(orderid); } }
2.9 实现类
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; /** * 创建订单并初始化状态机 * @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(); } } /** * 发货 * @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(); } } /** * 确认收货 * @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 状态机上下文
用于管理状态机信息
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.状态机测试
3.1创建订单
3.2持久化结果
初始化时自动将数据持久化到 redis
3.3 支付订单
3.4 发货
3.5 确认收货
后台日志
到此这篇关于springboot集成springstate状态机的实现的文章就介绍到这了,更多相关springstate 状态机内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论