Spring Statemachine for Order State Management with Persistence, Exception Handling, and AOP Logging
This article explains the fundamentals of finite state machines, introduces Spring Statemachine, demonstrates how to model order lifecycle states, configure persistence with memory and Redis, handle exceptions during state transitions, and use AOP to log transition results, providing complete Java code examples.
The article begins by defining a finite state machine (FSM) and its four core concepts—State, Event, Action, and Transition—using an automatic door example to illustrate how real‑world objects can be modeled as state machines.
State Machine Diagram
It describes how to draw a state‑transition diagram for an order process, showing the elements needed (initial state, target state, action, condition) and provides a sample diagram for moving from "Pending Payment" to "Pending Shipment".
Spring Statemachine Overview
Spring Statemachine is introduced as a framework that brings state‑machine concepts into Spring applications. Its main features include simple flat state machines, hierarchical state configurations, region support, triggers, guards, actions, type‑safe configuration adapters, Zookeeper‑based distributed state machines, event listeners, UML modeling, persistence, and Spring IoC integration.
Quick Start
A complete example is given that models order states (WAIT_PAYMENT, WAIT_DELIVER, WAIT_RECEIVE, FINISH) and events (PAYED, DELIVERY, RECEIVED). The article provides the SQL table definition for orders, the Java enum definitions for statuses and events, and the Spring configuration for the state machine.
CREATE TABLE `tb_order` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`order_code` varchar(128) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '订单编码',
`status` smallint(3) DEFAULT NULL COMMENT '订单状态',
`name` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '订单名称',
`price` decimal(12,2) DEFAULT NULL COMMENT '价格',
`delete_flag` tinyint(2) NOT NULL DEFAULT '0' COMMENT '删除标记,0未删除 1已删除',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '更新时间',
`create_user_code` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '创建人',
`update_user_code` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '更新人',
`version` int(11) NOT NULL DEFAULT '0' COMMENT '版本号',
`remark` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';The Java enums for order status and events are shown, followed by the state‑machine configuration class that defines the initial state, all possible states, and the external transitions for payment, delivery, and receipt.
public enum OrderStatus {
WAIT_PAYMENT(1, "待支付"),
WAIT_DELIVER(2, "待发货"),
WAIT_RECEIVE(3, "待收货"),
FINISH(4, "已完成");
// getters and utility methods omitted for brevity
}
public enum OrderStatusChangeEvent { PAYED, DELIVERY, RECEIVED }
@Configuration
@EnableStateMachine(name = "orderStateMachine")
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter
{
@Override
public void configure(StateMachineStateConfigurer
states) throws Exception {
states.withStates().initial(OrderStatus.WAIT_PAYMENT).states(EnumSet.allOf(OrderStatus.class));
}
@Override
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);
}
}Persistence
Two persistence strategies are demonstrated: an in‑memory map persister and a Redis‑based persister. The in‑memory persister stores the StateMachineContext in a local HashMap , while the Redis persister uses RedisStateMachineContextRepository and RepositoryStateMachinePersist to store state across distributed instances.
Controller and Service Layer
REST endpoints for creating orders, paying, delivering, and confirming receipt are provided. The service layer contains methods that start the state machine, restore its previous state, send events, and persist the new state. Detailed code shows how to inject the state machine and persisters, and how to log actions.
Exception Handling in Transitions
The article points out that state‑machine listeners swallow exceptions, causing sendEvent to always return true . It proposes storing the result of each transition in the state machine’s extended state (variables map) and checking this flag before persisting.
AOP Logging of Transition Results
An annotation @LogResult and an aspect LogResultAspect are introduced. The aspect intercepts methods annotated with @LogResult , executes the original method, and records 1 for success or 0 for failure in the extended state, allowing the sendEvent method to decide whether to persist.
@Retention(RetentionPolicy.RUNTIME)
public @interface LogResult { String key(); }
@Aspect
@Component
public class LogResultAspect {
@Around("@annotation(com.example.LogResult)")
public Object logResultAround(ProceedingJoinPoint pjp) throws Throwable {
// extract message and order, invoke method, store result in extended state
}
}Finally, the article lists remaining issues such as handling non‑payment events, duplicate code in listeners, and suggests further refactoring with AOP.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
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.