SpringBoot-Statemachine实现状态机持久化
状态机之所以强大,是因为始终保证行为是一致的,这使得调试相对容易。这是因为在机器启动时,操作规则是不可更改的。其思想是,应用程序可能存在有限数量的状态,某些预定义的触发器可以将应用程序从一种状态转移到另一种状态。这样的触发器可以基于事件或计时器。
在应用程序之外定义高级逻辑,然后依靠状态机来管理状态要容易得多。您可以通过发送事件、侦听更改或请求当前状态来与状态机交互。
依赖
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
状态事件
状态,示例:创建–>执行中–>结束
import lombok.extern.slf4j.Slf4j;
/**
* Description:状态枚举
* @author W
* @version 1.0.0
* @date 2022/9/29
*/
@Slf4j
public enum StatusEnum {
/**
* 创建
*/
STATUS_CREATE("100", "创建"),
/**
* 执行中
*/
STATUS_RUNNING("200", "执行中"),
/**
* 结束
*/
STATUS_FINISH("300", "结束");
private String value;
private String desc;
StatusEnum(String value, String desc) {
this.value = value;
this.desc = desc;
}
public static String getDescByCode(String code) {
StatusEnum[] values = StatusEnum.values();
for (StatusEnum value : values) {
if (value.getValue().equals(code)) {
return value.getDesc();
}
}
return null;
}
public static StatusEnum getWaveStatusByValue(String value) {
for (StatusEnum statusEnum : StatusEnum.values()) {
if (statusEnum.getValue().equals(value)) {
return statusEnum;
}
}
return null;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
事件,示例:创建–>执行中–>结束
/**
* Description:事件枚举
* @author W
* @version 1.0.0
* @date 2022/9/29
*/
public enum EventEnum {
CREATE("创建"),
RUNNING("执行中"),
FINISH("结束");
private String desc;
EventEnum(String desc) {
this.desc = desc;
}
}
常量工具类:
import java.util.HashMap;
import java.util.Set;
/**
* Description:常量
* @author W
* @version 1.0.0
* @date 2022/9/29
*/
public class Constants {
//目标状态和事件的对应关系
public static HashMap<String, EventEnum> statusEventMap = Maps.newHashMap();
static {
statusEventMap.put(StatusEnum.STATUS_CREATE.getValue(), EventEnum.RUNNING);
statusEventMap.put(StatusEnum.STATUS_RUNNING.getValue(), EventEnum.FINISH);
}
}
数据库对象
基础表,抽取用于引用,头表和明细表,都包含状态status
import lombok.Data;
@Data
public class BaseVO {
private String id;
private String status;
}
public class HeaderVO extends BaseVO{
}
public class DetailVO extends BaseVO{
}
状态机持久化
头表持久化逻辑:
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.support.DefaultStateMachineContext;
import org.springframework.stereotype.Component;
/**
* Description:状态持久化,如持久到数据库
* @author W
* @version 1.0.0
* @date 2022/9/29
*/
@Slf4j
@Component
public class HeaderStateMachinePersist implements StateMachinePersist<StatusEnum, EventEnum, HeaderVO> {
@Autowired
private HeaderService headerService;
@Override
public void write(StateMachineContext<StatusEnum, EventEnum> stateMachineContext, HeaderVO header)
throws Exception {
String oldWaveStatus = header.getStatus();
String targetStatus = stateMachineContext.getState().getValue();
header.setStatus(targetStatus);
headerService.updateHeaderStatus(header);
// header表操作
// ...
log.info("header id={},oldStatus={},targetStatus={}", header.getId(), oldWaveStatus, targetStatus);
// 日志记录
// ..
}
@Override
public StateMachineContext<StatusEnum, EventEnum> read(HeaderVO header) throws Exception {
log.info("header id={},oldStatus={}", header.getId(), header.getStatus());
return new DefaultStateMachineContext<>(StatusEnum.getWaveStatusByValue(header.getStatus()), null, null,
null, null);
}
}
明细表持久化逻辑:
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.support.DefaultStateMachineContext;
import org.springframework.stereotype.Component;
/**
* Description:状态持久化,如持久到数据库
* @author W
* @version 1.0.0
* @date 2022/9/29
*/
@Slf4j
@Component
public class DetailStateMachinePersist implements StateMachinePersist<StatusEnum, EventEnum, DetailVO> {
@Autowired
private DetailService detailService;
@Override
public void write(StateMachineContext<StatusEnum, EventEnum> stateMachineContext, DetailVO detail)
throws Exception {
String oldWaveStatus = detail.getStatus();
String targetStatus = stateMachineContext.getState().getValue();
detail.setStatus(targetStatus);
detailService.updateDetailStatus(detail);
// detail表操作
// ...
log.info("detail id={},oldStatus={},targetStatus={}", detail.getId(), oldWaveStatus, targetStatus);
// 日志记录
// ..
}
@Override
public StateMachineContext<StatusEnum, EventEnum> read(DetailVO detail) throws Exception {
log.info("detail id={},oldStatus={}", detail.getId(), detail.getStatus());
return new DefaultStateMachineContext<>(StatusEnum.getWaveStatusByValue(detail.getStatus()), null, null,
null, null);
}
}
事件触发入口
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.persist.StateMachinePersister;
import java.util.UUID;
/**
* Description:持久化入口
* @author W
* @version 1.0.0
* @date 2022/9/29
*/
@Slf4j
@Data
public class StatemachineService<S, E, T extends BaseVO> {
private StateMachinePersister<S, E, T> stateMachinePersist;
private StateMachineFactory<S, E> stateMachineFactory;
private String headerName;
public boolean execute(T data, E event) throws Exception {
// 利用随记ID创建状态机,创建时没有与具体定义状态机绑定
StateMachine<S, E> stateMachine = stateMachineFactory.getStateMachine(UUID.randomUUID());
stateMachine.start();
boolean success = false;
try {
stateMachinePersist.restore(stateMachine, data);
// 发送事件,返回是否执行成功
success = stateMachine.sendEvent(event);
if (success) {
log.info("persist begin!id={},event={}", data.getId(), event);
// 持久化
stateMachinePersist.persist(stateMachine, data);
log.info("persist end!id={},event={}", data.getId(), event);
} else {
log.info("id={},event={},send Event fail", data.getId(), event);
}
} catch (Exception e) {
log.error("id={},event={},state machine execute error", data.getId(), event, e);
throw e;
} finally {
stateMachine.stop();
}
return success;
}
}
构造器,创建不同状态机对象(头表、明细表):
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.persist.StateMachinePersister;
import java.util.Objects;
/**
* Description:状态机builder
* @author W
* @version 1.0.0
* @date 2022/9/29
*/
public class StatemachineServiceBuilder<S, E, T extends BaseVO> {
private StateMachinePersister<S, E, T> stateMachinePersist;
private StateMachineFactory<S, E> stateMachineFactory;
public StatemachineServiceBuilder<S, E, T>
setStateMachinePersister(StateMachinePersister<S, E, T> stateMachinePersist) {
this.stateMachinePersist = stateMachinePersist;
return this;
}
public StatemachineServiceBuilder<S, E, T> setStateMachineFactory(StateMachineFactory<S, E> stateMachineFactory) {
this.stateMachineFactory = stateMachineFactory;
return this;
}
public StatemachineService<S, E, T> builder() {
Objects.requireNonNull(stateMachineFactory, "stateMachineFactory 不能为空");
Objects.requireNonNull(stateMachinePersist, "stateMachinePersist 不能");
StatemachineService<S, E, T> service = new StatemachineService<>();
service.setStateMachineFactory(stateMachineFactory);
service.setStateMachinePersist(stateMachinePersist);
return service;
}
}
配置
状态机配置:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.persist.DefaultStateMachinePersister;
import java.util.EnumSet;
/**
* Description:状态机配置
* @author W
* @version 1.0.0
* @date 2022/9/29
*/
@Configuration
@EnableStateMachineFactory
public class StatemachineConfigurer extends EnumStateMachineConfigurerAdapter<StatusEnum, EventEnum> {
@Autowired
private HeaderStateMachinePersist headerStateMachinePersist;
@Autowired
private DetailStateMachinePersist detailStateMachinePersist;
@Autowired
private StateMachineFactory<StatusEnum, EventEnum> stateMachineFactory;
// 定义头表状态机入口bean
@Bean
public StatemachineService<StatusEnum, EventEnum, HeaderVO> headerStatemachineService() {
return new StatemachineServiceBuilder<StatusEnum, EventEnum, HeaderVO>()
.setStateMachineFactory(stateMachineFactory)
.setStateMachinePersister(new DefaultStateMachinePersister<>(headerStateMachinePersist)).builder();
}
// 定义明细表状态机入口bean
@Bean
public StatemachineService<StatusEnum, EventEnum, DetailVO> detailStatemachineService() {
return new StatemachineServiceBuilder<StatusEnum, EventEnum, DetailVO>()
.setStateMachineFactory(stateMachineFactory)
.setStateMachinePersister(new DefaultStateMachinePersister<>(detailStateMachinePersist)).builder();
}
@Override
public void configure(StateMachineStateConfigurer<StatusEnum, EventEnum> states) throws Exception {
// 起始状态
states.withStates()
.initial(StatusEnum.STATUS_CREATE)
.states(EnumSet.allOf(StatusEnum.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<StatusEnum, EventEnum> transitions) throws Exception {
// 状态与事件的关系
transitions.withExternal()
// 从当前状态到目标状态
.source(StatusEnum.STATUS_CREATE).target(StatusEnum.STATUS_RUNNING)
.event(EventEnum.RUNNING)
.and().withExternal().source(StatusEnum.STATUS_RUNNING).target(StatusEnum.STATUS_FINISH)
.event(EventEnum.FINISH);
}
}
使用
@Service
public class ProcessService {
@Autowired
private StatemachineService<StatusEnum, EventEnum, HeaderVO> headerStatemachineService;
@Autowired
private StatemachineService<StatusEnum, EventEnum, DetailVO> detailStatemachineService;
private void process() throws Exception {
// 业务中从当前状态处理完--》新的状态,触发事件,自动持久化更新+处理额外事项
HeaderVO headerVO = new HeaderVO();
headerVO.setStatus(StatusEnum.STATUS_CREATE.getValue());
headerStatemachineService.execute(headerVO, Constants.statusEventMap.get(StatusEnum.STATUS_CREATE.getValue()));
DetailVO detailVO = new DetailVO();
headerVO.setStatus(StatusEnum.STATUS_RUNNING.getValue());
detailStatemachineService.execute(detailVO, Constants.statusEventMap.get(StatusEnum.STATUS_RUNNING.getValue()));
}
}
监听器方式
另一种持久化方式,监听器,当触发事件后,相应状态变化,则触发对应的方法执行
import lombok.extern.slf4j.Slf4j;
import org.springframework.statemachine.annotation.*;
/**
* Description:该配置实现了com.didispace.StateMachineConfig类中定义的状态机监听器实现。
* 监听的方式触发事件进行持久化
* @author W
* @version 1.0.0
* @date 2022/9/29
*/
@WithStateMachine
@Slf4j
public class EventConfig {
@OnTransition(target = "STATUS_CREATE")
public void create() {
log.info("创建...");
// 持久化
}
@OnTransition(source = "STATUS_CREATE", target = "STATUS_RUNNING")
public void running() {
log.info("运行中...");
// 持久化
}
@OnTransitionStart(source = "STATUS_RUNNING", target = "STATUS_FINISH")
public void finish() {
log.info("结束...");
// 持久化
}
}
触发:
@Service
public class ProcessService {
@Autowired
private StateMachine<StatusEnum, EventEnum> stateMachine;
private void process() throws Exception {
stateMachine.start(); //start()创建状态,起始
stateMachine.sendEvent(EventEnum.RUNNING); //调用running方法
stateMachine.sendEvent(Events.FINISH); //调用用finish方法
}
}