Java 编程语言中,状态机主要有这几个 spring statemachine 、squirrel-foundation 、COLA-component-statemachine 调研后发现前两个都是有状态的,需要持久化,考虑到目前项目的复杂性选择了阿里开源的无状态状态机组件,下面是cola-component-statemachine 实战。
状态机主要解决问题是:
- 多段的if else if...
- 同一个方法体内长篇审批逻辑...非常不便于维护
引入依赖
maven 参考:
<dependency>
<groupId>com.alibaba.cola</groupId>
<artifactId>cola-component-statemachine</artifactId>
<version>4.3.2</version>
</dependency>
案例
使用状态机管理一个简单的审批流程状态。
首先定义了一个状态机基础抽象,用于后续规范其他状态机
/**
* 通用基础设定及方法
* @param <S> 状态枚举/类
* @param <E> 事件枚举/类
* @param <C> 上下文数据对象
*/
public interface BaseMachine<S,E,C> {
/**
* 获取状态机id
*/
String getMachineId();
/**
* 激活某个事件
*
* @param status 状态枚举
* @param event 事件
* @param context 上下文参数
*/
default S fire(S status, E event, C context) {
String machineId = getMachineId();
StateMachine<S, E, C> objectObjectObjectStateMachine = StateMachineFactory.get(machineId);
return objectObjectObjectStateMachine.fireEvent(status, event, context);
}
}
然后定义审批的状态和事件
需要注意的是状态不等于事件,但是名称又往往相似
状态:
@Getter
public enum ComStates {
PENDING_SUBMIT(0,"待提审"),
AUDITING(2,"审核中(单/多人)"),
SUCCESS(3,"已通过"),
REJECT(4,"已驳回"),
CANCEL(5,"已取消"),
;
private final Integer value;
private final String name;
ComStates(Integer value,String name){
this.value = value;
this.name = name;
}
public static ComStates getByValue(Integer value){
return new ArrayList<>(Arrays.asList(ComStates.values())).stream().filter(o-> Objects.equals(o.value,value)).findFirst().orElse(null);
}
}
事件:
@Getter
public enum ComEvents {
SUBMIT(1,"提交审核"),
CANCEL(2,"撤销审核"),
PASS_AUDIT(4,"通过审核"),
REJECT_AUDIT(5,"驳回审核")
;
private final Integer value;
private final String name;
ComEvents(Integer value,String name){
this.value = value;
this.name = name;
}
}
审批事件流程参考
一共五个事件,五个简易流程
演示项目结构参考:

测试接口:
@GetMapping("/com/get")
public ComContext get(String dataId){
return dbExample.get(dataId);
}
@GetMapping("/com/submit")
public Object submit(String dataId){
ComContext context = dbExample.get(dataId);
ComContext ctx=new ComContext();
BeanUtils.copyProperties(context,ctx);
return comAuditMachine.fire(ComStates.getByValue(context.getCurrentState()), ComEvents.SUBMIT, ctx);
}
@GetMapping("/com/cancel")
public Object cancel(String dataId){
ComContext context = dbExample.get(dataId);
ComContext ctx=new ComContext();
BeanUtils.copyProperties(context,ctx);
return comAuditMachine.fire(ComStates.getByValue(context.getCurrentState()), ComEvents.CANCEL, ctx);
}
@GetMapping("/com/audit")
public Object audit(String dataId,Long suid,Integer state,String msg){
ComContext context = dbExample.get(dataId);
ComContext ctx=new ComContext();
BeanUtils.copyProperties(context,ctx);
ctx.setSessionUserId(suid);
ctx.setCurrentNodeAuditState(state);
ctx.setCurrentNodeReason(msg);
ComEvents comEvents;
if (Objects.equals(1,state)){
comEvents = ComEvents.PASS_AUDIT;
}else{
comEvents = ComEvents.REJECT_AUDIT;
}
return comAuditMachine.fire(ComStates.getByValue(context.getCurrentState()),comEvents,ctx);
}
主要包含,提交审批/取消审批/审批。另外一个get获取当前审核数据信息
下面模拟流程: 提交审批->取消审批
上面操作,一切都按预计执行。
下面执行以下流程:提交审批->1号审核人员通过->2号人员驳回
按预计执行完毕。
其他流程这里就不展开描述了,感兴趣的可以下载demo源码尝试
demo-boot-statemachine.zip (访问密码: 9987)
相关操作接口:
查看审核信息 http://localhost:8080/com/get?dataId=1 提交审核 http://localhost:8080/com/submit?dataId=1 取消审核 http://localhost:8080/com/cancel?dataId=1 首次审核不通过 http://localhost:8080/com/audit?dataId=1&suid=1&state=2&msg=notfond 1号人审核通过 http://localhost:8080/com/audit?dataId=1&suid=1&state=1 2号不通过 http://localhost:8080/com/audit?dataId=1&suid=2&state=2&msg=2notfond 2号通过 http://localhost:8080/com/audit?dataId=1&suid=2&state=1
需要注意的是引用状态机模式,并不能减少业务代码的编写,只是优化的整个编码的细节,如COLA的下图一样形象的讲解了这个
更多参考连接:
- 官方库COLA/cola-components/cola-component-statemachine at master · alibaba/COLA · GitHub
- 事务失效解决:COLA-statemachine事务失效踩坑-阿里云开发者社区 (aliyun.com)
https://blog.xqlee.com/article/2403281832589381.html
评论