1、概念
状态机是用于实现工作流控制和状态自动转换的程序。
(1)工作流是独立于业务部分外的内容,
(2)调用对应的业务触发事件,就会执行对应的工作流的信息。
2、实现逻辑
通过配置
① 状态机的状态 和 状态变更触发事件
② 状态机内 业务监听事件 触发逻辑
③ 实体类的状态属性(建议用枚举类)
④ 状态变化触发事件属性(同 ③~)
⑤ 业务逻辑中 API 设为:
当业务状态变更时,会传递变更的状态值到 状态机中,
状态机会调用它的内置接口 发送业务状态到监听器,
当监听器的配置信息中 关于事件状态 有对应的状态转换事务,
就会再触发 状态机监听器中的 对应 事务状态转换接口,
内部代码编写修改 为新的状态值
实现事件触发状态变更机制。
3、业务场景示例
以 订单状态变更为例:
订单有
4种状态:①等待支付 ②等待发货 ③等待收货 ④完成
3种行为(状态变更触发事件):①支付 ②发货 ③收货
4、环境
①SpringBoot
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
②SpringJUnit5(SB2.2之后内置,可以不显式写)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
③Lombok
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
④SpringStateMachine
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>3.2.1</version>
</dependency>
5、实现代码
① 实体类
@Slf4j
@Data
@TableComment("订单表")
@TableName("t_order")
public class OrderDTO {
@TableId(value="id",type = IdType.ASSIGN_ID)
@Column(comment = "主键_订单编号",length = 20,isKey = true,type = MySqlTypeConstant.BIGINT,isAutoIncrement = true)
private Long id;
@Column(comment = "订单状态")
private OrderStatusEnum orderStatus;
}
② 状态枚举类
public enum OrderStatusEnum {
WAIT_PAYMENT,
WAIT_DELIVER,
WAIT_RECEIVE,
FINISH;
}
③ 状态变更触发事件枚举类
public enum OrderStatusChangeEventEnum {
PAYED,
DELIVERY,
RECEIVED;
}
④ SpringStateMachine状态机配置类
@Configuration
@EnableStateMachine
public class OrderStatemachineConfig extends StateMachineConfigurerAdapter<OrderStatusEnum, OrderStatusChangeEventEnum> {
@Override
public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderStatusChangeEventEnum> states) throws Exception {
states.withStates()
.initial(OrderStatusEnum.WAIT_PAYMENT)
.end(OrderStatusEnum.FINISH)
.states(EnumSet.allOf(OrderStatusEnum.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderStatusChangeEventEnum> transitions) throws Exception {
transitions.withExternal().source(OrderStatusEnum.WAIT_PAYMENT).target(OrderStatusEnum.WAIT_DELIVER)
.event(OrderStatusChangeEventEnum.PAYED)
.and()
.withExternal().source(OrderStatusEnum.WAIT_DELIVER).target(OrderStatusEnum.WAIT_RECEIVE)
.event(OrderStatusChangeEventEnum.DELIVERY)
.and()
.withExternal().source(OrderStatusEnum.WAIT_RECEIVE).target(OrderStatusEnum.FINISH)
.event(OrderStatusChangeEventEnum.RECEIVED);
}
}
⑤ 状态机事件监听器
@Component
@WithStateMachine
@Transactional
public class OrderStatemachineListener {
@OnTransition(source = "WAIT_PAYMENT",target="WAIT_DELIVER")
public boolean payTransaction(Message message){
OrderDTO order = (OrderDTO) message.getHeaders().get("order");
order.setOrderStatus(OrderStatusEnum.WAIT_DELIVER);
System.out.println("已支付,状态机反馈信息:"+message.getHeaders().toString());
return true;
}
@OnTransition(source = "WAIT_DELIVER",target="WAIT_RECEIVE")
public boolean deliveryTransaction(Message message){
OrderDTO order = (OrderDTO) message.getHeaders().get("order");
order.setOrderStatus(OrderStatusEnum.WAIT_RECEIVE);
System.out.println("已发货,状态机反馈信息:"+message.getHeaders().toString());
return true;
}
@OnTransition(source = "WAIT_RECEIVE",target="FINISH")
public boolean receiveTransaction(Message message){
OrderDTO order = (OrderDTO) message.getHeaders().get("order");
order.setOrderStatus(OrderStatusEnum.FINISH);
System.out.println("已收货,状态机反馈信息:"+message.getHeaders().toString());
return true;
}
}
⑥ 业务状态变更事件API
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, OrderDTO> implements OrderService{
@Resource
private StateMachine<OrderStatusEnum, OrderStatusChangeEventEnum> orderStateMachine;
private long id=1L;
private Map<Long,OrderDTO> orders= Maps.newConcurrentMap();
@Override
public OrderDTO create() {
OrderDTO orderDTO = new OrderDTO();
orderDTO.setOrderStatus(OrderStatusEnum.WAIT_PAYMENT);
orderDTO.setId(id++);
orders.put(orderDTO.getId(),orderDTO);
System.out.println("订单创建成功"+orderDTO.toString());
return orderDTO;
}
@Override
public Map<Long, OrderDTO> getOrders() {
return orders;
}
private synchronized boolean sendEvent(Message<OrderStatusChangeEventEnum> message){
boolean result=false;
try {
orderStateMachine.start();
result = orderStateMachine.sendEvent(message);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(Objects.nonNull(message)){
OrderDTO order = (OrderDTO) message.getHeaders().get("order");
if(Objects.nonNull(order)&&Objects.equals(order.getOrderStatus(),OrderStatusEnum.FINISH)){
orderStateMachine.stop();
}
}
}
return result;
}
@Override
public OrderDTO pay(long id) {
OrderDTO orderDTO = orders.get(id);
Message message =
MessageBuilder.withPayload(OrderStatusChangeEventEnum.PAYED).
setHeader("order", orderDTO).build();
if(!sendEvent(message)){
System.out.println("订单支付失败");
}
return orderDTO;
}
@Override
public OrderDTO deliver(long id) {
OrderDTO orderDTO = orders.get(id);
Message<OrderStatusChangeEventEnum> message =
MessageBuilder.withPayload(OrderStatusChangeEventEnum.DELIVERY).
setHeader("order", orderDTO).build();
if(!sendEvent(message)){
System.out.println("订单发货失败");
}
return orderDTO;
}
@Override
public OrderDTO receive(long id) {
OrderDTO orderDTO = orders.get(id);
Message<OrderStatusChangeEventEnum> message =
MessageBuilder.withPayload(OrderStatusChangeEventEnum.RECEIVED).
setHeader("order", orderDTO).build();
if(!sendEvent(message)){
System.out.println("订单收货失败");
}
return orderDTO;
}
}
⑦ 单元测试
@SpringBootTest
class GenerateApplicationTests {
@Resource
private OrderService orderService;
@Test
void contextLoads() {
}
@Test
public void testOrder(){
orderService.create();
orderService.pay(1L);
orderService.deliver(1L);
orderService.receive(1L);
System.out.println("所有订单状态:"+orderService.getOrders());
}
}
6、测试结果
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/374ed0733ab44096727fe10beef26ff1.png)
7、调用逻辑分析图(未完待续)