Understanding State Machines and Implementing Spring Statemachine for Order Processing

This article explains the fundamentals of finite state machines, introduces the four core concepts of state, event, action, and transition, and provides a detailed guide on using Spring Statemachine with Java—including configuration, persistence, service and controller implementations, testing, and advanced troubleshooting techniques.

Architecture Digest
Architecture Digest
Architecture Digest
Understanding State Machines and Implementing Spring Statemachine for Order Processing

The article begins by defining a state (e.g., an automatic door can be open or closed) and explaining that a state machine (finite‑state machine, FSM) is a mathematical model representing a limited set of states and the transitions between them.

Four essential concepts are introduced:

State – at least two states are required (e.g., open and closed).

Event – the trigger that causes a transition (e.g., pressing the open button).

Action – the operation performed when an event occurs (typically a function call).

Transition – the change from one state to another (e.g., the "open" process).

The article then describes FSM as an algorithmic idea consisting of a set of states, an initial state, inputs, and a transition function that determines the next state.

Next, the focus shifts to Spring Statemachine , a framework that brings state‑machine concepts into Spring applications. Its main features include simple flat state machines, hierarchical structures, region support, triggers, guards, actions, security adapters, generator mode, Zookeeper‑based distributed state machines, event listeners, UML modeling, and persistence.

A quick‑start example uses an order‑processing scenario. The SQL table definition for tb_order is provided:

CREATE TABLE `tb_order` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `order_code` varchar(128) DEFAULT NULL COMMENT '订单编码',
  `status` smallint(3) DEFAULT NULL COMMENT '订单状态',
  `name` varchar(64) 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) DEFAULT NULL COMMENT '创建人',
  `update_user_code` varchar(32) DEFAULT NULL COMMENT '更新人',
  `version` int(11) NOT NULL DEFAULT '0' COMMENT '版本号',
  `remark` varchar(64) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COMMENT='订单表';

Dependencies for Maven/Gradle are shown:

<!-- redis持久化状态机 -->
<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-redis</artifactId>
    <version>1.2.9.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.statemachine</groupId>
    <artifactId>spring-statemachine-starter</artifactId>
    <version>2.0.1.RELEASE</version>
</dependency>

Enum definitions for states and events:

public enum OrderStatus {
    WAIT_PAYMENT(1, "待支付"),
    WAIT_DELIVER(2, "待发货"),
    WAIT_RECEIVE(3, "待收货"),
    FINISH(4, "已完成");
    private Integer key;
    private String desc;
    // constructor, getters, and lookup method omitted for brevity
}

public enum OrderStatusChangeEvent {
    PAYED, DELIVERY, RECEIVED;
}

The state‑machine configuration class registers the states and external transitions:

@Configuration
@EnableStateMachine(name = "orderStateMachine")
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderStatus, OrderStatusChangeEvent> {
    @Override
    public void configure(StateMachineStateConfigurer<OrderStatus, OrderStatusChangeEvent> states) throws Exception {
        states.withStates()
              .initial(OrderStatus.WAIT_PAYMENT)
              .states(EnumSet.allOf(OrderStatus.class));
    }
    @Override
    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);
    }
}

Persistence is demonstrated using an in‑memory map and a Redis‑backed persister. The in‑memory persister bean:

@Bean(name = "stateMachineMemPersister")
public static StateMachinePersister getPersister() {
    return new DefaultStateMachinePersister(new StateMachinePersist() {
        @Override
        public void write(StateMachineContext context, Object contextObj) throws Exception {
            map.put(contextObj, context);
        }
        @Override
        public StateMachineContext read(Object contextObj) throws Exception {
            return (StateMachineContext) map.get(contextObj);
        }
        private Map map = new HashMap();
    });
}

Redis persister configuration uses RedisStateMachineContextRepository and RedisStateMachinePersister.

Controller and service layers expose REST endpoints ( /order/create, /order/pay, etc.) and use the state machine to drive order state transitions. The service method sendEvent starts the machine, restores persisted state, sends the event, and persists the new state if the transition succeeds.

Listeners annotated with @OnTransition update the order record and demonstrate how exceptions inside transition methods are swallowed by the state machine, leading to false‑positive success results. The article proposes storing execution results in the machine’s ExtendedState variables and checking them after the event.

Advanced solutions include a custom @LogResult annotation and an AOP aspect that records success (1) or failure (0) in ExtendedState, allowing the sendEvent method to decide whether to persist the state.

Finally, the article lists testing steps, common pitfalls (e.g., state machine swallowing exceptions), and a set of related reading links.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

workflowstate machine
Architecture Digest
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.