在企业开发中,数据在不同的业务间传输是最常见的工作,所以虽然我们的主架构是用的状态机,也就是从流程状态的角度来看待这个项目,但在具体业务中,每个状态的转变中会牵涉到各类业务,这些业务有些需要收到状态机变化的通知,需要把状态值传递给业务类和业务方法,同样的,在处理状态变化是,也需要获取业务数据,方便不同的业务在同一个状态变化环节做各自的业务,下面我们就讲下这个数据在 spring statemachine 里面的传递。
这次我们的顺序变一下,由外部传入一个订单号到 controller 开始:
@RequestMapping(“/testOrderState”)
public void testOrderState(String orderId) throws Exception {StateMachine<OrderStates, OrderEvents> stateMachine = orderStateMachineBuilder.build(beanFactory);
System.out.println(stateMachine.getId());
// 创建流程
stateMachine.start();
// 触发 PAY 事件
stateMachine.sendEvent(OrderEvents.PAY);
// 触发 RECEIVE 事件
Order order = new Order(orderId, "547568678", "广东省深圳市", "13435465465", "RECEIVE");
Message<OrderEvents> message = MessageBuilder.withPayload(OrderEvents.RECEIVE).setHeader("order", order).build();
stateMachine.sendEvent(message);
// 获取最终状态
System.out.println("最终状态:" + stateMachine.getState().getId());
}
controller 收到 request 请求的参数,orderId,然后状态机依次触发事件,到触发 RECEIVE 事件的时候,我们新建了一个 Order,并把 orderId 塞进去了,其实更多的情况应该是我们拿到 orderId,然后查询数据库,得到 order 数据对象,这里为了简化代码,就新建一个啦。
然后就是真正的主角登场了,Message。它其实不是 spirng statemachine 专属的,它是 spring 里面通用的一种消息工具,看它的源代码:
package org.springframework.messaging;
public interface Message<T> {
/**
* Return the message payload.
*/
T getPayload();
/**
* Return message headers for the message (never {@code null} but may be empty).
*/
MessageHeaders getHeaders();
}
它由两个部分组成,看图就知道了,和代码里面是一致的
在 spring statemachine 里面,我们把状态塞到 message 的 payload 里面,然后把需要传递的业务数据(例子里面就是 order 对象)塞到 header 里面。创建 message 用的是 messagebuilder,看它的名字就知道是专门创建 message 的。
Message<OrderEvents> message = MessageBuilder.withPayload(OrderEvents.RECEIVE).setHeader(“order”, order).build();
stateMachine.sendEvent(message);
创建了 message 后,状态机 sendEvent 就可以不只是传一个 event,可以组合 event(OrderEvents.RECEIVE)和数据内容(order)一起发送给状态机变化的处理类 eventconfig 了。让我们看 eventConfig 的处理:
/**
* WAITING_FOR_RECEIVE->DONE 执行的动作
*/
@OnTransition(source = "WAITING_FOR_RECEIVE", target = "DONE")
public void receive(Message<OrderEvents> message) {System.out.println("传递的参数:" + message.getHeaders().get("order"));
logger.info("--- 用户已收货,订单完成 ---");
}
首先,receive 方法的参数由之前的为空:
public void receive() {
logger.info("--- 用户已收货,订单完成 ---");
}
改成了 Message<OrderEvents> message,这样就能从 message 的 getHeaders 里面取到传递过来的数据对象了。
另外如果我们需要传递多个数据对象怎么办呢,比如我们在实际业务中,除了传订单数据,可能还需要把商品数据,或者支付结果数据也传过来,那么也容易,我们还是从 controller 里面开始:
Message<OrderEvents> message = MessageBuilder.withPayload(OrderEvents.RECEIVE).setHeader(“order”, order).setHeader(“otherobj”, “otherobjvalue”).build();
在后面继续 setHeader 就好了,然后到 eventConfig 里面:
System.out.println(“ 传递的参数:” + message.getHeaders().get(“order”));
System.out.println(“ 传递的参数:” + message.getHeaders().get(“otherObj”));
运行后看日志:
传递的参数:Order [id=null, userId=547568678, address= 广东省深圳市, phoneNum=13435465465, state=RECEIVE]
传递的参数:otherObjValue
可知两个的数据都传递到了 eventConfig 里面了,这个就实现了多个数据对象的同时传递。
到这里为止,状态机通过 message 对象就和其他的业务代码做到了数据连接。其实这个很关键,只有做到和其他业务的数据传递,才能算的上真正的可用。
下一章我们继续讲状态机的持久化问题和怎么在非起始状态开始创建状态机
码云配套代码地址