彻底搞懂海运航线 彻底搞懂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);}}最后编写客户端测试代码 。
【彻底搞懂海运航线 彻底搞懂Spring状态机原理,实现订单与物流解耦】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) throwsException {//此处并没有进行持久化操作}@Overridepublic StateMachineContext<Object, Object> 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<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("客户线程"){@Overridepublic void run() {orderService.deliver(1);orderService.receive(1);}}.start();orderService.pay(2);orderService.deliver(2);orderService.receive(2);System.out.println("全部订单状态:" + orderService.getOrders());}}通过这个真实的业务案例,相信小伙伴们已经对状态模式有了一个非常深刻的理解 。
关注微信公众号『 Tom弹架构 』回复“设计模式”可获取完整源码 。
【推荐】Tom弹架构:30个设计模式真实案例(附源码),挑战年薪60W不是梦
本文为“Tom弹架构”原创,转载请注明出处 。技术在于分享,我分享我快乐!
如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力 。关注微信公众号『 Tom弹架构 』可获取更多技术干货!