有限状态机简称就是状态机,因为一般的状态机的状态都是离散和可举的,即为有限,所以后面的介绍都不加有限二字。状态机表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。通俗的描述状态机就是定义了一套状态変更的流程:状态机包含一个状态集合,定义当状态机处于某一个状态的时候它所能接收的事件以及可执行的行为,执行完成后,状态机所处的状态。所以状态机会包含以下几个重要的元素:
- State:状态。一个标准的状态机最少包含两个状态:初始和终态。初态是状态机初始化后所处的状态,而终态顾名思义就是状态机结束时所处的状态。其他的状态都是一些流转中停留的状态。标准的状态机还会涉及到一些中间态,存在中间态的状态机流程就会比较复杂(用处也不是特别大,而且可以通过其他方式实现),所以在目标实现的状态机里不会引入这个概念。
- Event:事件。还有中描述叫Trigger,表达的意思都一样,就是要执行某个操作的触发器或口令:当状态机处于某个状态时,只有外界告诉状态机要干什么事情的时候,状态机才会去执行具体的行为,来完成外界想要它完成的操作。比如出去吃饭,说“点菜”,服务员才会拿着小本过来记录你要吃的菜,说的那句“点菜”,就相当于Event。
- Action:行为。状态变更索要执行的具体行为。还是拿上面点菜的例子,服务员拿小本记录你定的菜的过程就是Action
- Transition:变更。一个状态接收一个事件执行了某些行为到达了另外一个状态的过程就是一个Transition。定义Transition就是在定义状态机的运转流程
如果你写过很复杂的流程系统,流程系统中涉及多步操作,流程达到不同的状态需要有不同的处理,同时状态间的转换也是有特定逻辑的。如果不使用状态机,那么你的代码我估计会有大量的if判断语句,你得判断某个操作指令过来了,当前这个状态是否能执行该指令。
Spring中有集成了一套状态机框架,我们可以使用该框架进行开发。假设我们使用的Springboot为2.1.9
pom
代码语言:javascript复制<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-starter</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
我们定义两个枚举,一个为订单状态,一个为订单事件。
代码语言:javascript复制public enum OrderStatus {
CREATED,
WAITINF_FOR_RECEIVE,
FINISHED
}
代码语言:javascript复制public enum OrderEvents {
PAY,
RECEIVE
}
设置一个状态机配置bean
代码语言:javascript复制@Configuration
@EnableStateMachine
@Slf4j
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderStatus,OrderEvents> {
/**
* 状态机侦听器的bean
* @return
*/
@Bean
public StateMachineListener<OrderStatus,OrderEvents> listener() {
return new StateMachineListenerAdapter<OrderStatus,OrderEvents>() {
@Override
public void stateChanged(State<OrderStatus, OrderEvents> from, State<OrderStatus, OrderEvents> to) {
log.info("State change to {}",to.getId());
}
};
}
/**
* 状态机执行器的bean
* @return
*/
@Bean
public Action<OrderStatus,OrderEvents> action() {
return new Action<OrderStatus, OrderEvents>() {
@Override
public void execute(StateContext<OrderStatus, OrderEvents> stateContext) {
log.info("from {} to {}",stateContext.getSource().getId(),stateContext.getTarget().getId());
}
};
}
/**
* 注册侦听器
* @param config
* @throws Exception
*/
@Override
public void configure(StateMachineConfigurationConfigurer<OrderStatus, OrderEvents> config) throws Exception {
config.withConfiguration()
.autoStartup(true)
.listener(listener());
}
/**
* 初始化订单状态
* @param states
* @throws Exception
*/
@Override
public void configure(StateMachineStateConfigurer<OrderStatus, OrderEvents> states) throws Exception {
states.withStates()
.initial(OrderStatus.CREATED)
.states(EnumSet.allOf(OrderStatus.class));
}
/**
* 订单状态转变时对应的事件和执行器
* @param transitions
* @throws Exception
*/
@Override
public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvents> transitions) throws Exception {
transitions.withExternal().source(OrderStatus.CREATED).target(OrderStatus.WAITINF_FOR_RECEIVE)
.event(OrderEvents.PAY).action(action())
.and()
.withExternal().source(OrderStatus.WAITINF_FOR_RECEIVE).target(OrderStatus.FINISHED)
.event(OrderEvents.RECEIVE).action(action());
}
}
Springboot引导类
代码语言:javascript复制@SpringBootApplication
public class CalculateApplication implements CommandLineRunner{
@Autowired
private StateMachine<OrderStatus,OrderEvents> stateMachine;
public static void main(String[] args) {
SpringApplication.run(CalculateApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
stateMachine.sendEvent(OrderEvents.PAY);
stateMachine.sendEvent(OrderEvents.RECEIVE);
}
}
运行可见如下日志
代码语言:javascript复制2020-06-02 03:41:20.007 INFO 22287 --- [ main] c.g.calculate.state.StateMachineConfig : from CREATED to WAITINF_FOR_RECEIVE
2020-06-02 03:41:20.008 INFO 22287 --- [ main] c.g.calculate.state.StateMachineConfig : State change to WAITINF_FOR_RECEIVE
2020-06-02 03:41:20.008 INFO 22287 --- [ main] c.g.calculate.state.StateMachineConfig : from WAITINF_FOR_RECEIVE to FINISHED
2020-06-02 03:41:20.009 INFO 22287 --- [ main] c.g.calculate.state.StateMachineConfig : State change to FINISHED