Mastering State Patterns with Spring State Machine: From Theory to Order Workflow
This article explains the classic State design pattern, demonstrates a traffic‑light example, compares Spring State Machine with COLA, and walks through building a complete order‑status workflow in Spring Boot using enums, configuration, listeners, services, and a test controller, while highlighting trade‑offs and alternatives.
The article begins by recalling the State design pattern, which encapsulates an object's behavior in separate state classes so that the object's actions change automatically when its internal state changes. It defines the pattern, lists its three roles—Context, State, and ConcreteState—and shows a UML class diagram (illustrated by an image).
To illustrate the pattern, a simple traffic‑light example is provided. The abstract state class MyState declares an abstract handler() method. Two concrete states, RedLightState and GreenLightState, implement handler() to print "红灯停" and "绿灯行" respectively. A MyContext holds a MyState instance and delegates handler() calls to the current state. The test class creates a context, switches between red and green states, and prints the expected output, demonstrating how the pattern eliminates explicit if‑else logic.
After establishing the pattern, the article introduces the concept of a state machine (a mathematical model of states and transitions) and explains that Spring State Machine is an implementation of the State pattern with richer features. It outlines the essential elements of a state machine: current state, trigger event, response function, and target state.
A comparison table (described in text) contrasts Spring State Machine with COLA State Machine, noting that Spring offers a richer ecosystem (284 interfaces/classes, reactive API) but is harder to customize, whereas COLA is simpler with fewer interfaces (36) and easier customization.
Next, the article walks through building an order‑status workflow using Spring State Machine:
1. Dependency
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>${springboot.version}</version>
</dependency>2. Domain Model
public class Order {
private Long orderId;
private OrderStatusEnum orderStatus;
}
public enum OrderStatusEnum { WAIT_PAYMENT, WAIT_DELIVER, WAIT_RECEIVE, FINISH }
public enum OrderStatusChangeEventEnum { PAYED, DELIVERY, RECEIVED }3. State Machine Configuration
@Configuration
@EnableStateMachine
public class OrderStatusMachineConfig 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);
}
}4. Listener
@Component
@WithStateMachine
@Transactional
public class OrderStatusListener {
@OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
public boolean payTransition(Message message) {
Order order = (Order) message.getHeaders().get("order");
order.setOrderStatus(OrderStatusEnum.WAIT_DELIVER);
System.out.println("支付,状态机反馈信息:" + message.getHeaders().toString());
return true;
}
// similar methods for deliverTransition and receiveTransition omitted for brevity
}5. Service Layer
@Service
public class OrderServiceImpl implements OrderService {
@Resource
private StateMachine<OrderStatusEnum, OrderStatusChangeEventEnum> orderStateMachine;
private long id = 1L;
private Map<Long, Order> orders = Maps.newConcurrentMap();
public Order create() {
Order order = new Order();
order.setOrderStatus(OrderStatusEnum.WAIT_PAYMENT);
order.setOrderId(id++);
orders.put(order.getOrderId(), order);
System.out.println("订单创建成功:" + order);
return order;
}
public Order pay(long orderId) {
Order order = orders.get(orderId);
Message<OrderStatusChangeEventEnum> message = MessageBuilder.withPayload(OrderStatusChangeEventEnum.PAYED)
.setHeader("order", order).build();
if (!sendEvent(message)) {
System.out.println("支付失败, 状态异常,订单号:" + orderId);
}
return orders.get(orderId);
}
// deliver and receive methods follow the same pattern
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)) {
Order order = (Order) message.getHeaders().get("order");
if (Objects.nonNull(order) && Objects.equals(order.getOrderStatus(), OrderStatusEnum.FINISH)) {
orderStateMachine.stop();
}
}
}
return result;
}
}6. Controller (Test Entry)
@RestController
public class OrderController {
@Resource
private OrderService orderService;
@RequestMapping("/testOrderStatusChange")
public String testOrderStatusChange() {
orderService.create();
orderService.create();
orderService.pay(1L);
orderService.deliver(1L);
orderService.receive(1L);
orderService.pay(2L);
orderService.deliver(2L);
orderService.receive(2L);
System.out.println("全部订单状态:" + orderService.getOrders());
return "success";
}
}The execution results (shown in two screenshots) confirm that the state machine correctly drives the order through WAIT_PAYMENT → WAIT_DELIVER → WAIT_RECEIVE → FINISH, and that invalid transitions are rejected.
7. Reflection and Alternatives
The author reflects on the characteristics of state machines and suggests three alternative implementation ideas: using a message‑queue to publish events, a scheduled job that polls orders and advances them based on business rules, and a rule‑engine that encodes state‑transition logic.
Finally, the article summarizes that the State pattern provides a clean way to encapsulate state‑dependent behavior, Spring State Machine builds on this pattern with a rich API, and the presented order‑workflow example demonstrates a practical application. Readers are invited to comment on other possible approaches.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
