springboot 有限状态机入门指南
为什么要使用状态机?
如果你写过很复杂的流程系统,流程系统中涉及多步操作,流程达到不同的状态需要有不同的处理,同时状态间的转换也是有特定逻辑的。如果不使用状态机,那么你的代码我估计会有大量的if判断语句,你得判断某个操作指令过来了,当前这个状态是否能执行该指令。当程序需要异常终止,你需要在本不该对数据库进行操作的状态对数据库进行操作,进而导致到处都在操作数据库,一旦数据库出现数据异常,很难确定时哪里出现了问题。给修改bug带来了极大的困难。如果你有上面的困惑,那么我建议你修改代码模式,如果你的代码中存在大量的if elseif,你可以用反射,也可以用策略模式或者状态模式。如果你的系统中很明显就是一个状态流传的逻辑,那么我强烈建议你使用有限状态机重构自己的代码。有限状态机可以解决上面的问题。
什么是状态机?
从我的实际经验来讲,有限状态机可以看作是有向有环图。有向,因为状态一般是以一个状态到另一个状态;有环,因为流程很有可能出现失败然后重新定位到前面某个状态节点。如下所示:
为什么说有限状态机能解决去掉大量if判断的问题?
在有限自动机中,状态的转换被预先设定好。以上图为例,按照流程化的编写代码,原本T1状态是只能转移到T2状态的,如果此时来了一个Command要求当前状态转为S2状态,传统的写法只能是加判断条件,判断该指令是否合法,满足我业务流程,不满足就抛弃。这样就使得代码中会嵌入大量的状态-指令契合的条件判断语句。如果使用有限状态机,预先定义好状态之间的转化流程,当一个非法命令来到某个状态的执行部分,当前状态并没有为该命令定义处理方法,那么该命令自然就被舍弃掉了。在流程比较复杂的系统中,这样的优势更为明显。
有限状态机的实现原理是什么呢?
可以利用栈这种数据结构手写一个有限状态机。通过把状态的入栈和出栈操作定义为状态的转化过程,栈顶状态为当前自动机状态流程的状态,通过压栈和出栈的方式实现有限自动机的流程控制。不过,手写的有限状态机要上生产线还是比较麻烦,例如如何解决状态持久化问题等等。
我使用的是springboot,有现成的框架供我使用吗?
答案是肯定的。springboot为spring使用开发人员提供了StateMachine这个有限状态自动机,官方文档传送门
接下来让我详细为大家介绍一下如何简单上手StateMachine(详细使用参照官方文档)
引入maven依赖
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-starter</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
定义状态和事件的枚举类型
public enum Events {
EVENT1, EVENT2
}
public enum States {
SI, STATE1, STATE2
}
自定义配置文件
@Configuration
@EnableStateMachine
@Slf4j
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events> {
@Bean
public StateMachineListener<States,Events> listener() {
return new StateMachineListenerAdapter<States,Events>(){
@Override
public void stateChanged(State<States,Events> from,State<States,Events> to){
log.info("State change to {}",to.getId());
}
};
}
@Override
public void configure(StateMachineConfigurationConfigurer<States,Events> config) throws Exception {
config.withConfiguration()
.autoStartup(true)
.listener(listener());
}
@Override
public void configure(StateMachineStateConfigurer<States,Events> states) throws Exception {
states.withStates()
.initial(States.SI)
.states(EnumSet.allOf(States.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<States,Events> transitions) throws Exception {
transitions
.withExternal().source(States.SI).target(States.STATE1).event(Events.EVENT1).action(action())
.and()
.withExternal().source(States.STATE1).target(States.STATE2).event(Events.EVENT2).action(action());
}
@Bean
public Action<States,Events> action(){
return new Action<States, Events>() {
@Override
public void execute(StateContext<States, Events> stateContext) {
log.info("from {} to {}",stateContext.getSource().getId(),stateContext.getTarget().getId());
}
};
}
}
修改Application主类,实现CommandLineRunner接口
@EnableAutoConfiguration
@Configuration
@ComponentScan({"com.chaojilaji.workorderservice"})
public class WorkorderserviceApplication implements CommandLineRunner {
@Autowired
private StateMachine<States, Events> stateMachine;
public static void main(String[] args) {
SpringApplication.run(WorkorderserviceApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
stateMachine.sendEvent(Events.EVENT1);
stateMachine.sendEvent(Events.EVENT2);
}
}
运行代码,可以看见如下输出:
官方文档关于这些配置的因果有详细的描述,再次给出官方文档地址:
https://docs.spring.io/spring-statemachine/docs/2.2.x-SNAPSHOT/reference/
推荐阅读
-
SpringBoot集成easypoi快速入门(Excel导入导出工具)
-
【转】Thrift入门试用 博客分类: 开源组件的应用Apache开源组件研究企业中间件J2EE开发技术指南 apchethriftsocket
-
jna使用入门 博客分类: 企业应用面临的问题企业中间件J2EE开发技术指南 JNA
-
springboot 有限状态机入门指南
-
算法竞赛入门指南第八章
-
Helm快速入门指南
-
阿里云Kubernetes服务 - Service Broker快速入门指南 集群控制台
-
java的if else语句入门指南(推荐)
-
SpringMvc入门指南(必看)
-
java的if else语句入门指南(推荐)