Spring Statemachine
它的主要功能是帮助开发者简化状态机的开发过程,让状态机结构更加层次化。
特性
Spring Statemachine旨在提供以下功能:
-
对于简单的用例,易于使用平坦的一级状态机。
-
分层状态机结构,简化复杂的状态配置。
-
状态机区域提供更复杂的状态配置。
-
触发器、转换、保护和操作的使用。
-
类型安全配置适配器。
-
构建器模式,用于在Spring应用程序上下文之外方便地实例化
-
常用用例的配方
-
分布式状态机基于一个动物园管理员
-
状态机事件监听器。
-
UML Eclipse Papyrus建模。
-
将机器配置存储在持久存储中。
-
Spring IOC集成以将bean与状态机关联。
状态机很强大,因为行为总是被保证是一致的,这使得调试相对容易。
这是因为操作规则是在机器启动时写死的。
其思想是,应用程序可能以有限的状态存在,某些预定义的触发器可以将应用程序从一种状态带到另一种状态。这样的触发器可以基于事件或计时器。
在应用程序之外定义高级逻辑,然后依赖状态机来管理状态,这要容易得多。
您可以通过发送事件、侦听更改或简单地请求当前状态与状态机交互。
快速开始
项目结构
.
└── com
└── github
└── houbb
└── spring
└── boot
└── statemachine
├── Application.java
├── config
│ └── StateMachineConfig.java
└── constant
├── Events.java
└── States.java
文件配置
- pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>2.0.0.RELEASE</version>
</dependency>
</dependencies>
代码编写
- 状态定义
public enum States {
// 待支付
UNPAID,
// 待收货
WAITING_FOR_RECEIVE,
// 结束
DONE
}
- 事件定义
public enum Events {
/**
* 支付 会触发状态从待支付 UNPAID 状态到待收货 WAITING_FOR_RECEIVE 状态的迁移,
*/
PAY,
/**
* 收货 会触发状态从待收货 WAITING_FOR_RECEIVE 状态到结束 DONE 状态的迁移。
*/
RECEIVE
}
- StateMachineConfig.java
import com.github.houbb.spring.boot.statemachine.constant.Events;
import com.github.houbb.spring.boot.statemachine.constant.States;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.transition.Transition;
import java.util.EnumSet;
@Configuration
@EnableStateMachine
public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events> {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 初始化当前状态机拥有哪些状态
* @param states
* @throws Exception
*/
@Override
public void configure(StateMachineStateConfigurer<States, Events> states)
throws Exception {
states.withStates()
.initial(States.UNPAID)
.states(EnumSet.allOf(States.class));
}
/**
* 方法用来初始化当前状态机有哪些状态迁移动作,其中命名中我们很容易理解每一个迁移动作,都有来源状态source,
* 目标状态target以及触发事件event。
* @param transitions
* @throws Exception
*/
@Override
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
throws Exception {
transitions.withExternal()
.source(States.UNPAID).target(States.WAITING_FOR_RECEIVE)
.event(Events.PAY)
.and()
.withExternal()
.source(States.WAITING_FOR_RECEIVE).target(States.DONE)
.event(Events.RECEIVE);
}
/**
* 方法为当前的状态机指定了状态监听器
* @param config
* @throws Exception
*/
@Override
public void configure(StateMachineConfigurationConfigurer<States, Events> config)
throws Exception {
config.withConfiguration()
.listener(listener());
}
@Bean
public StateMachineListener<States, Events> listener() {
return new StateMachineListenerAdapter<States, Events>() {
@Override
public void transition(Transition<States, Events> transition) {
if (transition.getTarget().getId() == States.UNPAID) {
logger.info("订单创建,待支付");
return;
}
if (transition.getSource().getId() == States.UNPAID
&& transition.getTarget().getId() == States.WAITING_FOR_RECEIVE) {
logger.info("用户完成支付,待收货");
return;
}
if (transition.getSource().getId() == States.WAITING_FOR_RECEIVE
&& transition.getTarget().getId() == States.DONE) {
logger.info("用户已收货,订单完成");
return;
}
}
};
}
}
- Application.java
import com.github.houbb.spring.boot.statemachine.constant.Events;
import com.github.houbb.spring.boot.statemachine.constant.States;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.statemachine.StateMachine;
@SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Autowired
private StateMachine<States, Events> stateMachine;
@Override
public void run(String... args) throws Exception {
stateMachine.start();
stateMachine.sendEvent(Events.PAY);
stateMachine.sendEvent(Events.RECEIVE);
}
}
测试日志
...
2018-09-26 16:47:52.273 INFO 29446 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0
2018-09-26 16:47:52.284 INFO 29446 --- [ main] eConfig$$EnhancerBySpringCGLIB$$1dfef0ee : 订单创建,待支付
2018-09-26 16:47:52.287 INFO 29446 --- [ main] o.s.s.support.LifecycleObjectSupport : started org.springframework.statemachine.support.DefaultStateMachineExecutor@528c868
2018-09-26 16:47:52.288 INFO 29446 --- [ main] o.s.s.support.LifecycleObjectSupport : started UNPAID WAITING_FOR_RECEIVE DONE / UNPAID / uuid=064443bf-d8e4-44b0-9ae9-b06c57cc5620 / id=null
2018-09-26 16:47:52.293 INFO 29446 --- [ main] eConfig$$EnhancerBySpringCGLIB$$1dfef0ee : 用户完成支付,待收货
2018-09-26 16:47:52.294 INFO 29446 --- [ main] eConfig$$EnhancerBySpringCGLIB$$1dfef0ee : 用户已收货,订单完成
2018-09-26 16:47:52.296 INFO 29446 --- [ main] c.g.h.s.boot.statemachine.Application : Started Application in 11.139 seconds (JVM running for 11.67)
2018-09-26 16:47:52.297 INFO 29446 --- [ Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@4516af24: startup date [Wed Sep 26 16:47:51 CST 2018]; root of context hierarchy
2018-09-26 16:47:52.298 INFO 29446 --- [ Thread-2] o.s.c.support.DefaultLifecycleProcessor : Stopping beans in phase 0
2018-09-26 16:47:52.299 INFO 29446 --- [ Thread-2] o.s.s.support.LifecycleObjectSupport : stopped org.springframework.statemachine.support.DefaultStateMachineExecutor@528c868
2018-09-26 16:47:52.299 INFO 29446 --- [ Thread-2] o.s.s.support.LifecycleObjectSupport : stopped UNPAID WAITING_FOR_RECEIVE DONE / / uuid=064443bf-d8e4-44b0-9ae9-b06c57cc5620 / id=null
...
个人感受
这个在状态流转中比较适合使用。
可以使得代码逻辑变得清晰优雅。
参考资料
https://projects.spring.io/spring-statemachine/
http://blog.didispace.com/spring-statemachine/