写点什么

一文让你彻底搞懂 Spring 状态及原理,实现订单与物流解耦

  • 2021 年 11 月 18 日
  • 本文字数:4776 字

    阅读完需:约 16 分钟

一文让你彻底搞懂Spring状态及原理,实现订单与物流解耦

1 状态模式的 UML 类图 #状态模式的 UML 类图如下图所示。


一文让你彻底搞懂 Spring 状态及原理,实现订单与物流解耦 2 使用状态模式实现登录状态自由切换 #当我们在社区阅读文章时,如果觉得文章写得很好,我们就会评论、收藏两连发。如果处于登录情况下,则可以直接做评论、收藏这些行为。否则,跳转到登录界面,登录后再继续执行先前的动作。这里涉及的状态有两种:登录与未登录;行为有两种:评论和收藏。下面使用状态模式来实现这个逻辑,代码如下。首先创建抽象状态角色 UserState 类。


public abstract class UserState {protected AppContext context;


public void setContext(AppContext context) {    this.context = context;}
public abstract void favorite();
public abstract void comment(String comment);
复制代码


}


然后创建登录状态 LogInState 类。


public class LoginInState extends UserState {@Overridepublic void favorite() {System.out.println("收藏成功!");}


@Overridepublic void comment(String comment) {    System.out.println(comment);}
复制代码


}


创建未登录状态 UnloginState 类。


public class UnLoginState extends UserState {@Overridepublic void favorite() {this.switch2Login();this.context.getState().favorite();}


@Overridepublic void comment(String comment) {    this.switch2Login();    this.context.getState().comment(comment);}
private void switch2Login() { System.out.println("跳转到登录页面!"); this.context.setState(this.context.STATE_LOGIN);}
复制代码


}


创建上下文角色 AppContext 类。


public class AppContext {public static final UserState STATE_LOGIN = new LoginInState();public static final UserState STATE_UNLOGIN = new UnLoginState();private UserState currentState = STATE_UNLOGIN;{STATE_LOGIN.setContext(this);STATE_UNLOGIN.setContext(this);}


public void setState(UserState state) {    this.currentState = state;    this.currentState.setContext(this);}
public UserState getState() { return this.currentState;}
public void favorite() { this.currentState.favorite();}
public void comment(String comment) { this.currentState.comment(comment);}
复制代码


}


最后编写客户端测试代码。


public static void main(String[] args) {AppContext context = new AppContext();context.favorite();context.comment("评论: 好文章,360 个赞!");}


运行结果如下图所示。


一文让你彻底搞懂 Spring 状态及原理,实现订单与物流解耦 3 使用状态机实现订单状态流转控制 #状态机是状态模式的一种应用,相当于上下文角色的一个升级版。在工作流或游戏等各种系统中有大量使用,如各种工作流引擎,它几乎是状态机的子集和实现,封装状态的变化规则。Spring 也提供了一个很好的解决方案。Spring 中的组件名称就叫作状态机(StateMachine)。状态机帮助开发者简化状态控制的开发过程,让状态机结构更加层次化。下面用 Spring 状态机模拟一个订单状态流转的过程。


3.1 添加依赖。#


<dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-core</artifactId><version>2.0.1.RELEASE</version></dependency>


3.2 创建订单实体 Order 类。#


public class Order {private int id;private OrderStatus status;public void setStatus(OrderStatus status) {this.status = status;}


public OrderStatus getStatus() {    return status;}
public void setId(int id) { this.id = id;}
public int getId() { return id;}
@Overridepublic String toString() { return "订单号:" + id + ", 订单状态:" + status;}
复制代码


}


3.3 创建订单状态枚举类和状态转换枚举类。#


/**


  • 订单状态*/public enum OrderStatus {//待支付,待发货,待收货,订单结束 WAIT_PAYMENT, WAIT_DELIVER, WAIT_RECEIVE, FINISH;}


/**


  • 订单状态改变事件*/public enum OrderStatusChangeEvent {//支付,发货,确认收货 PAYED, DELIVERY, RECEIVED;}


3.4 添加状态流转配置。#


/**


  • 订单状态机配置*/@Configuration@EnableStateMachine(name = "orderStateMachine")public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> {

  • /**

  • 配置状态

  • @param states

  • @throws Exception*/public void configure(StateMachineStateConfigurer<OrderStatus, OrderStatusChangeEvent> states) throws Exception {states.withStates().initial(OrderStatus.WAIT_PAYMENT).states(EnumSet.allOf(OrderStatus.class));}

  • /**

  • 配置状态转换事件关系

  • @param transitions

  • @throws Exception*/public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderStatusChangeEvent> 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);}

  • /**

  • 持久化配置

  • 在实际使用中,可以配合 Redis 等进行持久化操作

  • @return*/@Beanpublic DefaultStateMachinePersister persister(){return new DefaultStateMachinePersister<>(new StateMachinePersist<Object, Object, Order>() {@Overridepublic void write(StateMachineContext<Object, Object> context, Order order) throws Exception {//此处并没有进行持久化操作}

  • });}}


3.5 添加订单状态监听器。#


@Component("orderStateListener")@WithStateMachine(name = "orderStateMachine")public class OrderStateListenerImpl{


@OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")public boolean payTransition(Message<OrderStatusChangeEvent> message) {    Order order = (Order) message.getHeaders().get("order");    order.setStatus(OrderStatus.WAIT_DELIVER);    System.out.println("支付,状态机反馈信息:" + message.getHeaders().toString());    return true;}
@OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")public boolean deliverTransition(Message<OrderStatusChangeEvent> message) { Order order = (Order) message.getHeaders().get("order"); order.setStatus(OrderStatus.WAIT_RECEIVE); System.out.println("发货,状态机反馈信息:" + message.getHeaders().toString()); return true;}
@OnTransition(source = "WAIT_RECEIVE", target = "FINISH")public boolean receiveTransition(Message<OrderStatusChangeEvent> message){ Order order = (Order) message.getHeaders().get("order"); order.setStatus(OrderStatus.FINISH); System.out.println("收货,状态机反馈信息:" + message.getHeaders().toString()); return true;}
复制代码


}


3.6 创建 IOrderService 接口。#


public interface IOrderService {//创建新订单 Order create();//发起支付 Order pay(int id);//订单发货 Order deliver(int id);//订单收货 Order receive(int id);//获取所有订单信息 Map<Integer, Order> getOrders();}


3.7 在 Service 业务逻辑中应用。#


@Service("orderService")public class OrderServiceImpl implements IOrderService {


@Autowiredprivate StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
@Autowiredprivate StateMachinePersister<OrderStatus, OrderStatusChangeEvent, Order> persister;
private int id = 1;private Map<Integer, Order> orders = new HashMap<>();
public Order create() { Order order = new Order(); order.setStatus(OrderStatus.WAIT_PAYMENT); order.setId(id++); orders.put(order.getId(), order); return order;}
public Order pay(int id) { Order order = orders.get(id); System.out.println("线程名称:" + Thread.currentThread().getName() + " 尝试支付,订单号:" + id); Message message = MessageBuilder.withPayload(OrderStatusChangeEvent.PAYED).
复制代码


setHeader("order", order).build();if (!sendEvent(message, order)) {System.out.println("线程名称:" + Thread.currentThread().getName() + " 支付失败, 状态异常,订单号:" + id);}return orders.get(id);}


public Order deliver(int id) {    Order order = orders.get(id);    System.out.println("线程名称:" + Thread.currentThread().getName() + " 尝试发货,订单号:" + id);    if (!sendEvent(MessageBuilder.withPayload(OrderStatusChangeEvent.DELIVERY)
复制代码


.setHeader("order", order).build(), orders.get(id))) {System.out.println("线程名称:" + Thread.currentThread().getName() + " 发货失败,状态异常,订单号:" + id);}return orders.get(id);}


public Order receive(int id) {    Order order = orders.get(id);    System.out.println("线程名称:" + Thread.currentThread().getName() + " 尝试收货,订单号:" + id);    if (!sendEvent(MessageBuilder.withPayload(OrderStatusChangeEvent.RECEIVED)
复制代码


.setHeader("order", order).build(), orders.get(id))) {System.out.println("线程名称:" + Thread.currentThread().getName() + " 收货失败,状态异常,订单号:" + id);}return orders.get(id);}


public Map<Integer, Order> getOrders() {    return orders;}

/** * 发送订单状态转换事件 * * @param message * @param order * @return */private synchronized boolean sendEvent(Message<OrderStatusChangeEvent> message, Order order) { boolean result = false; try { orderStateMachine.start(); //尝试恢复状态机状态 persister.restore(orderStateMachine, order); //添加延迟用于线程安全测试 Thread.sleep(1000); result = orderStateMachine.sendEvent(message); //持久化状态机状态 persister.persist(orderStateMachine, order); } catch (Exception e) { e.printStackTrace(); } finally { orderStateMachine.stop(); } return result;}
复制代码


}


3.8 编写客户端测试代码。#


@SpringBootApplicationpublic class Test {public static void main(String[] args) {


    Thread.currentThread().setName("主线程");
ConfigurableApplicationContext context = SpringApplication.run(Test.class,args);
IOrderService orderService = (IOrderService)context.getBean("orderService");
orderService.create(); orderService.create();
orderService.pay(1);
new Thread("客户线程"){ @Override public void run() { orderService.deliver(1); orderService.receive(1); } }.start();
orderService.pay(2); orderService.deliver(2); orderService.receive(2);
System.out.println("全部订单状态:" + orderService.getOrders());
}
复制代码


}


通过这个真实的业务案例,相信小伙伴们已经对状态模式有了一个非常深刻的理解。

用户头像

还未添加个人签名 2021.10.14 加入

还未添加个人简介

评论

发布
暂无评论
一文让你彻底搞懂Spring状态及原理,实现订单与物流解耦