彻底搞懂Spring状态机原理,实现订单与物流解耦

1 状态模式的UML类图

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

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 { @Override public void favorite() {  System.out.println("收藏成功!"); } @Override public void comment(String comment) {  System.out.println(comment); }}

创建未登录状态UnloginState类。

public class UnLoginState extends UserState { @Override public void favorite() {  this.switch2Login();  this.context.getState().favorite(); } @Override public 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状态机原理,实现订单与物流解耦_第1张图片

3 使用状态机实现订单状态流转控制

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

3.1 添加依赖。

 org.springframework.statemachine spring-statemachine-core 2.0.1.RELEASE

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; } @Override public 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 {  /**  * 配置状态  * @param states  * @throws Exception  */ public void configure(StateMachineStateConfigurer 	states) throws Exception {  states    .withStates()    .initial(OrderStatus.WAIT_PAYMENT)    .states(EnumSet.allOf(OrderStatus.class)); }  /**  * 配置状态转换事件关系  * @param transitions  * @throws Exception  */ 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); }  /**  * 持久化配置  * 在实际使用中,可以配合Redis等进行持久化操作  * @return  */ @Bean public DefaultStateMachinePersister persister(){  return new DefaultStateMachinePersister<>(new StateMachinePersist() {   @Override   public void write(StateMachineContext context, Order order) throws 			Exception {    //此处并没有进行持久化操作   }    @Override   public StateMachineContext read(Order order) throws Exception {    //此处直接获取Order中的状态,其实并没有进行持久化读取操作    return new DefaultStateMachineContext(order.getStatus(), null, null, null);   }  }); }}

3.5 添加订单状态**。

@Component("orderStateListener")@WithStateMachine(name = "orderStateMachine")public class OrderStateListenerImpl{  @OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER") public boolean payTransition(Message 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 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 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 getOrders();}

3.7 在Service业务逻辑中应用。

@Service("orderService")public class OrderServiceImpl implements IOrderService { @Autowired private StateMachine orderStateMachine;  @Autowired private StateMachinePersister persister;  private int id = 1; private Map 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 getOrders() {  return orders; }  /**  * 发送订单状态转换事件  *  * @param message  * @param order  * @return  */ private synchronized boolean sendEvent(Message 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()); }}

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

 
小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏+评论转发+关注我,后面会有很多干货。
我有一些面试题、架构、设计类资料可以说是程序员面试必备!所有资料都整理到网盘了,需要的话欢迎下载!私信我回复【07】即可免费获取

彻底搞懂Spring状态机原理,实现订单与物流解耦_第2张图片

 

原文出处:www.shaoqun.com/a/1240421.html

你可能感兴趣的:(框架,java,后端,spring,java,后端)