状态机(State Machine)是一种用于描述系统行为的数学模型,广泛应用于计算机科学、工程和自动化等领域。它通过定义系统的状态、事件和转移来模拟系统的动态行为。
基本概念
-
状态(State):系统在某一时刻的特定条件或模式。
-
事件(Event):触发状态转移的外部输入或条件变化。
-
转移(Transition):状态因事件而发生的改变。
-
动作(Action):在状态转移或处于某一状态时执行的操作。
简单点来说就是某一个流程当前状态A->事件1->状态B, 或者状态A->事件2->C的一个流程, 实现起来也是比较简单的
自定义状态机
基本原理就是根据状态机的概念简单实现一下, 根据读取json文件来配置整个流程
状态机配置
@Data
public class StateMachineConfig {
/**
* 状态机名称(Bean名称)
*/
private String name;
/**
* 状态机配置
*/
private List<Transition> transitions;
@Data
public static class Transition {
/**
* 当前状态
*/
private String from;
/**
* 事件
*/
private String event;
/**
* 下一个状态
*/
private String to;
}
}
{
"name": "orderProcess",
"transitions": [
{
"from": "A",
"event": "event1",
"to": "B"
},
{
"from": "A",
"event": "event2",
"to": "C"
},
{
"from": "B",
"event": "event3",
"to": "C"
},
{
"from": "C",
"event": "event1",
"to": "D"
}
]
}
状态机实例
这个类主要是状态机的实例, 也是实现状态机的核心类, 主要实现为Table类, 简单点说就是两个key确定一个value
/**
* 状态机实例类
*
* @author zzt
* @version 1.0.0
*/
@Slf4j
public class SimpleStateMachine {
/**
* 状态转换关系
*/
private final Table<String, String, String> table = HashBasedTable.create();
/**
* 添加状态转换关系
* 该方法用于在状态机中添加从当前状态到新状态的转换关系。转换关系由当前状态、事件和新状态组成。
*
* @param fromState 当前状态,表示转换的起始状态。
* @param event 触发状态转换的事件。
* @param toState 转换后的新状态。
*/
public void addTransition(String fromState, String event, String toState) {
table.put(fromState, event, toState);
}
/**
* 发送事件以触发状态转换
* 此方法根据当前状态和接收到的事件来确定是否需要进行状态转换
* 如果当前状态和事件的组合在转换表中定义了状态转换,则执行转换
* 否则,输出没有可用转换的消息
*
* @param currentState 当前状态
* @param event 触发状态转换的事件
* @return 返回新状态
*/
public String sendEvent(String currentState, String event) {
//获取下一个状态
String to = table.get(currentState, event);
if (StringUtils.isBlank(to)) {
log.error("没有可用转换, 请检查配置文件,currentState: [{}], event: [{}]", currentState, event);
throw new BusinessException("获取状态异常!");
}
return to;
}
}
状态机工厂
状态机工厂主要就是用来生产状态机实例的, 在实际项目中可能会配置多个流程,因此需要生成多个状态机实例, 当时用时只需要从容器中获取一个实例即可
/**
* 状态机实例工厂,负责创建状态机并将其注册到 Spring IOC 容器中
*
* @author zzt
* @version 1.0.0
*/
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "state-machine")
public class StateMachineFactory {
/**
* 是否启用状态机
*/
private Boolean enable = false;
/**
* 状态机配置文件路径(resources目录下的包)
*/
private String path = "";
private final ApplicationContext applicationContext;
/**
* 初始化所有状态机并注册到 Spring IOC 容器中
*/
@Bean
public void registerStateMachines() {
if (enable) {
// 获取所有状态机配置
List<StateMachineConfig> stateMachineConfigs = StateMachineJsonUtil.readAllJsonFiles(path);
// 获取状态机配置名称
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) applicationContext.getAutowireCapableBeanFactory();
for (StateMachineConfig config : stateMachineConfigs) {
//判断是否已经存在Bean名称
if (!registry.containsBeanDefinition(config.getName())) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(SimpleStateMachine.class);
beanDefinition.setInstanceSupplier(() -> createStateMachine(config));
// 使用状态机名称作为 Bean 名称
registry.registerBeanDefinition(config.getName(), beanDefinition);
} else {
log.error("状态机名称:[{}]重复,请检查配置文件!", config.getName());
throw new SpringBootPlusException("状态机名称重复,请检查配置文件!");
}
}
}
}
/**
* 创建并配置状态机实例
*
* @param stateMachineConfig 状态机配置
* @return 配置好的状态机实例
*/
private SimpleStateMachine createStateMachine(StateMachineConfig stateMachineConfig) {
// 创建状态机并配置
SimpleStateMachine stateMachine = new SimpleStateMachine();
// 配置状态机的转换规则
for (StateMachineConfig.Transition transition : stateMachineConfig.getTransitions()) {
stateMachine.addTransition(transition.getFrom(), transition.getEvent(), transition.getTo());
}
return stateMachine;
}
}
读取json文件
/**
* 读取 json 文件
*
* @author zzt
* @version 1.0.0
*/
@Slf4j
public class StateMachineJsonUtil {
/**
* 读取 resources/path 目录下的所有 .json 文件,并将其解析为 StateMachineConfig 对象的列表。
*
* @param path json文件路径
* @return 包含所有解析后的 StateMachineConfig 对象的列表
*/
public static List<StateMachineConfig> readAllJsonFiles(String path) {
ObjectMapper objectMapper = MapperUtils.getInstance();
List<StateMachineConfig> stateMachineConfigs = new ArrayList<>();
try {
ClassPathResource resource = new ClassPathResource(path);
File[] files = resource.getFile().listFiles();
if (files != null) {
// 遍历每个资源文件,将其解析为 StateMachineConfig 对象并添加到列表中
for (File file : files) {
if (file.exists()) {
FileInputStream inputStream = new FileInputStream(file);
StateMachineConfig stateMachineConfig = objectMapper.readValue(inputStream, StateMachineConfig.class);
stateMachineConfigs.add(stateMachineConfig);
}
}
}
} catch (Exception e) {
log.error("读取json文件失败,[{}]", e.getMessage(), e);
throw new SpringBootPlusException("读取json文件失败");
}
return stateMachineConfigs;
}
}