本文主要介绍一下状态机以及相关的一些概念。结合一个简单的订单状态流程,示例怎样在Springboot中集成Spring-statemachine。
有限状态机(英语:finite-state machine,缩写:FSM),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。应用FSM模型可以帮助对象生命周期的状态的顺序以及导致状态变化的事件进行管理。将状态和事件控制从不同的业务Service方法的if else中抽离出来。FSM的应用范围很广,对于有复杂状态流,扩展性要求比较高的场景都可以使用该模型。下面是状态机模型中的4个要素,即现态、条件、动作、次态。
如下图示例:有限的状态集是“opend”以及“closed”。如果“现态”是“opend”,当“条件”为“Close”时,执行的“动作”是“close door”,次态则为“closed”。状态机逻辑执行完毕后“closed”则变成了“现态”。
所以FSM的执行逻辑可以理解为下图,即FSM的下一个状态和输出是由输入和当前状态决定的:
对于使用Java语言的应用来说,可以选择的集成框架也比较多。如squirrel-foundation,spring-statemachine,stateless4j 。squirrel-foundation,stateless4j相对spring-statemachine更加轻量级。感兴趣的可以去看下这两个项目的源码。
Spring官网项目的QuickStart中没有对于持久化模型的举例。所以本文以一个持久化的流程——一个简单的示例订单流程进行举例。示例代码中,持久化框架使用了hibernate-jpa,请求的示范例子用了spring-web,spring-webmvc的Rest api。基于JDK8。Spring statemachine版本:1.2.7,SpringBoot 版本:1.5.3。
dependencies {
compile 'org.springframework.statemachine:spring-statemachine-core:1.2.7.RELEASE'
}
状态枚举类OrderStatus:
public enum OrderStatus {
// 待支付,待发货,待收货,订单结束
WAIT_PAYMENT, WAIT_DELIVER, WAIT_RECEIVE, FINISH;
}
事件枚举类OrderStatusChangeEvent:
public enum OrderStatusChangeEvent {
// 支付,发货,确认收货
PAYED, DELIVERY, RECEIVED
}
订单Entity Order:
@Entity
@Table(name = "order_test")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Integer id;
@NotNull
@Column(name = "order_id")
private Integer orderId;
@NotNull
@Enumerated(EnumType.ORDINAL)
@Column(name = "status")
private OrderStatus status;
public Order() {
}
public Order(Integer orderId, OrderStatus status) {
this.orderId = orderId;
this.status = status;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public OrderStatus getStatus() {
return status;
}
public void setStatus(OrderStatus status) {
this.status = status;
}
@Override
public String toString() {
return "Order{" +
"orderId=" + orderId +
", status=" + status +
'}';
}
}
Repository类 OrderRepo :
public interface OrderRepo extends JpaRepository<Order, Integer> {
Order findByOrderId(Integer order);
}
在启动springboot时,需要注入状态机的状态,事件的配置。起主要涉及到以下两个类:
@SpringBootApplication
public class TestApp {
public static void main(String[] args) {
SpringApplication.run(TestApp.class, args);
}
@Configuration
@EnableStateMachine
static class StateMachineConfig
extends StateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
// 定义初始状态
.initial(OrderStatus.WAIT_PAYMENT)
// 定义所有状态集合
.states(EnumSet.allOf(OrderStatus.class));
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source(OrderStatus.WAIT_PAYMENT).target(OrderStatus.WAIT_DELIVER)
.event(OrderStatusChangeEvent.PAYED)
.and()
.withExternal()
.source(OrderStatus.WAIT_DELIVER).target(OrderStatus.WAIT_RECEIVE)
.event(OrderStatusChangeEvent.DELIVERY)
.and()
.withExternal()
.source(OrderStatus.WAIT_RECEIVE).target(OrderStatus.FINISH)
.event(OrderStatusChangeEvent.RECEIVED);
}
}
}
状态转移过程中,可以通过监听器(Listener)来处理一些持久化或者业务监控等任务。在需要持久化的场景中,可以在状态机模式中的监听器中添加持久化的处理。其中主要涉及到:
为了方便扩展更多的Listener,以及管理Listeners和Interceptors。可以定义一个基于状态机实例的Handler: PersistStateMachineHandler,以及持久化实体的监听器OrderPersistStateChangeListener如下:
监听器的Handler以及接口定义PersistStateMachineHandler:
public class PersistStateMachineHandler extends LifecycleObjectSupport {
private final StateMachine stateMachine;
private final PersistingStateChangeInterceptor interceptor = new PersistingStateChangeInterceptor();
private final CompositePersistStateChangeListener listeners = new CompositePersistStateChangeListener();
/**
* 实例化一个新的持久化状态机Handler
*
* @param stateMachine 状态机实例
*/
public PersistStateMachineHandler(StateMachine stateMachine) {
Assert.notNull(stateMachine, "State machine must be set");
this.stateMachine = stateMachine;
}
@Override
protected void onInit() throws Exception {
stateMachine.getStateMachineAccessor().doWithAllRegions(function -> function.addStateMachineInterceptor(interceptor));
}
/**
* 处理entity的事件
*
* @param event
* @param state
* @return 如果事件被接受处理,返回true
*/
public boolean handleEventWithState(Message event, OrderStatus state) {
stateMachine.stop();
List> withAllRegions = stateMachine.getStateMachineAccessor()
.withAllRegions();
for (StateMachineAccess a : withAllRegions) {
a.resetStateMachine(new DefaultStateMachineContext<>(state, null, null, null));
}
stateMachine.start();
return stateMachine.sendEvent(event);
}
/**
* 添加listener
*
* @param listener the listener
*/
public void addPersistStateChangeListener(PersistStateChangeListener listener) {
listeners.register(listener);
}
/**
* 可以通过 addPersistStateChangeListener,增加当前Handler的PersistStateChangeListener。
* 在状态变化的持久化触发时,会调用相应的实现了PersistStateChangeListener的Listener实例。
*/
public interface PersistStateChangeListener {
/**
* 当状态被持久化,调用此方法
*
* @param state
* @param message
* @param transition
* @param stateMachine 状态机实例
*/
void onPersist(State state, Message message, Transition transition,
StateMachine stateMachine);
}
private class PersistingStateChangeInterceptor extends StateMachineInterceptorAdapter<OrderStatus, OrderStatusChangeEvent> {
// 状态预处理的拦截器方法
@Override
public void preStateChange(State state, Message message,
Transition transition, StateMachine stateMachine) {
listeners.onPersist(state, message, transition, stateMachine);
}
}
private class CompositePersistStateChangeListener extends AbstractCompositeListener<PersistStateChangeListener> implements
PersistStateChangeListener {
@Override
public void onPersist(State state, Message message,
Transition transition, StateMachine stateMachine) {
for (Iterator iterator = getListeners().reverse(); iterator.hasNext(); ) {
PersistStateChangeListener listener = iterator.next();
listener.onPersist(state, message, transition, stateMachine);
}
}
}
}
持久化状态发生变化的订单实体的Listener实现类OrderPersistStateChangeListener:
public class OrderPersistStateChangeListener implements PersistStateMachineHandler.PersistStateChangeListener {
@Autowired
private OrderRepo repo;
@Override
public void onPersist(State state, Message message,
Transition transition, StateMachine stateMachine) {
if (message != null && message.getHeaders().containsKey("order")) {
Integer order = message.getHeaders().get("order", Integer.class);
Order o = repo.findByOrderId(order);
OrderStatus status = state.getId();
o.setStatus(status);
repo.save(o);
}
}
}
Springboot注入Handler和Listener bean的Configuration类,OrderPersistHandlerConfig
@Configuration
public class OrderPersistHandlerConfig {
@Autowired
private StateMachine stateMachine;
@Bean
public OrderStateService persist() {
PersistStateMachineHandler handler = persistStateMachineHandler();
handler.addPersistStateChangeListener(persistStateChangeListener());
return new OrderStateService(handler);
}
@Bean
public PersistStateMachineHandler persistStateMachineHandler() {
return new PersistStateMachineHandler(stateMachine);
}
@Bean
public OrderPersistStateChangeListener persistStateChangeListener(){
return new OrderPersistStateChangeListener();
}
}
示例提供了两个简单的接口,一个是查看所有订单列表,一个是改变一个订单的状态。
Controller如下OrderController:
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private OrderStateService orderStateService;
/**
* 列出所有的订单列表
*
* @return
*/
@RequestMapping(method = {RequestMethod.GET})
public ResponseEntity orders() {
String orders = orderStateService.listDbEntries();
return new ResponseEntity(orders, HttpStatus.OK);
}
/**
* 通过触发一个事件,改变一个订单的状态
* @param orderId
* @param event
* @return
*/
@RequestMapping(value = "/{orderId}", method = {RequestMethod.POST})
public ResponseEntity processOrderState(@PathVariable("orderId") Integer orderId, @RequestParam("event") OrderStatusChangeEvent event) {
Boolean result = orderStateService.change(orderId, event);
return new ResponseEntity(result, HttpStatus.OK);
}
}
订单服务类OrderStateService:
@Component
public class OrderStateService {
private PersistStateMachineHandler handler;
public OrderStateService(PersistStateMachineHandler handler) {
this.handler = handler;
}
@Autowired
private OrderRepo repo;
public String listDbEntries() {
List orders = repo.findAll();
StringJoiner sj = new StringJoiner(",");
for (Order order : orders) {
sj.add(order.toString());
}
return sj.toString();
}
public boolean change(int order, OrderStatusChangeEvent event) {
Order o = repo.findByOrderId(order);
return handler.handleEventWithState(MessageBuilder.withPayload(event).setHeader("order", order).build(), o.getStatus());
}
}
添加测试数据,添加4个订单,分别是WAIT_PAYMENT状态。
request:
http://localhost:8089/orders
response:
{
"data": "Order{orderId=1, status=WAIT_PAYMENT},Order{orderId=2, status=WAIT_PAYMENT},Order{orderId=3, status=WAIT_PAYMENT},Order{orderId=4, status=WAIT_PAYMENT}",
}
request:
http://localhost:8089/orders/1?event=PAYED
http://localhost:8089/orders/2?event=PAYED
http://localhost:8089/orders/2?event=DELIVERY
http://localhost:8089/orders/2?event=DELIVERY
response:
{
"data": true
}
{
"data": true
}
{
"data": true
}
{
"data": false
}
request:
http://localhost:8089/orders
response:
{
"data": "Order{orderId=1, status=WAIT_DELIVER},Order{orderId=2, status=WAIT_RECEIVE},Order{orderId=3, status=WAIT_PAYMENT},Order{orderId=4, status=WAIT_PAYMENT}",
}
本文主要是结合一个简单示例示范Spring-statemachine的集成,在实际的业务实施中还会有更多复杂的情况,比如事务的处理,分布式事件消息,资源锁等。LZ最近也在对业务进行重构,欢迎一起交流技术。示例代码已经上传到:satate-machine-git
示例代码git
Spring-statemachine 官网