第 2 篇:SSM 核心概念与源码剖析:状态、事件与转换的底层逻辑
在 SSM(Spring StateMachine)系列博客的第一篇中,我们初步了解了 SSM 的应用场景与基础搭建,而要真正掌握 SSM的设计思想与实战能力,必须深入其核心概念与底层源码。本文将从 状态、事件、转换 三大核心要素切入,结合源码剖析与电商订单场景的实战案例,拆解 SSM 状态流转引擎的运行逻辑,并详解动作(Action)与守卫(Guard)的进阶用法,帮你彻底摆脱“知其然不知其所以然”的困境。
一、SSM 6 大核心概念精讲(附源码 + 实战)
SSM 的核心能力围绕“状态管理”展开,而理解 状态(State)、事件(Event)、转换(Transition)、动作(Action)、守卫(Guard)、上下文(Context) 这 6 大概念,是掌握 SSM 的第一步。本节先聚焦前三者,结合源码与电商订单场景实战,帮你建立直观认知。
1. 状态(State):状态机的“节点”
状态是状态机的核心载体,代表业务对象在某个阶段的“静态特征”(如订单的“已创建”“已支付”)。SSM 按功能将状态分为三类,且所有状态都基于抽象类 AbstractState 实现。
(1)状态的分类
- 简单状态(SimpleState):原子化的基础状态,无子状态嵌套,是业务中最常用的类型。例如电商订单的“CREATED(已创建)”“PAID(已支付)”,这类状态不包含更细粒度的子状态。
- 复合状态(CompositeState):包含子状态的“容器型状态”,适用于复杂业务场景。例如“SHIPPED(已发货)”可拆分为“IN_TRANSIT(运输中)”“OUT_FOR_DELIVERY(派送中)”两个子状态,此时“SHIPPED”就是复合状态。
- 初始/终结状态(InitialState/FinalState):状态机的“入口”与“出口”。初始状态(InitialState)是状态机启动时默认进入的第一个状态(每个状态机/复合状态仅一个);终结状态(FinalState)是状态机停止的标志(进入后状态机不再处理事件)。
(2)源码剖析:AbstractState 的核心属性
所有状态类(SimpleState、CompositeState 等)都继承自 org.springframework.statemachine.state.AbstractState,其核心属性定义了状态的“身份”与“行为”:
public abstract class AbstractState<S, E> implements State<S, E> {// 1. 状态唯一标识(如订单状态的 "CREATED")private final S id;// 2. 进入状态时执行的动作列表(进入动作)private final List<Action<S, E>> entryActions;// 3. 离开状态时执行的动作列表(离开动作)private final List<Action<S, E>> exitActions;// 4. 子状态(仅复合状态有值,简单状态为 null)private final Collection<State<S, E>> substates;// 5. 是否为初始状态private final boolean initial;// 6. 是否为终结状态private final boolean end;// 构造器与 getter 方法省略...
}
id:状态的唯一标识,通常与业务枚举(如OrderState.CREATED)绑定,确保状态在状态机中不重复。entryActions/exitActions:动作列表(List<Action>),分别在“进入状态”和“离开状态”时触发(例如进入“CANCEL(已取消)”状态时执行退款动作,离开“CREATED”状态时释放库存)。initial/end:布尔值标记状态类型,InitialState的initial=true,FinalState的end=true,简单/复合状态的这两个属性均为false。
(3)实战:电商订单状态定义与配置
以电商订单为例,我们用枚举定义核心状态,并配置初始/终结状态:
步骤 1:定义订单状态枚举
// 订单状态枚举(包含简单状态与终结状态)
public enum OrderState {CREATED("已创建"), // 初始状态PAID("已支付"), // 简单状态SHIPPED("已发货"), // 简单状态DELIVERED("已送达"), // 终结状态CANCELLED("已取消"); // 终结状态
}
步骤 2:配置状态机的初始与状态集合
通过 SSM 的配置类 StateMachineConfigurerAdapter,指定状态机的状态集合、初始状态与终结状态:
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig extends StateMachineConfigurerAdapter<OrderState, OrderEvent> {// 配置状态与初始/终结状态@Overridepublic void configure(StateMachineStateConfigurer<OrderState, OrderEvent> states) throws Exception {states.withStates()// 1. 指定状态机的所有状态.states(EnumSet.allOf(OrderState.class))// 2. 配置初始状态(启动时进入).initial(OrderState.CREATED)// 3. 配置终结状态(进入后状态机停止).end(OrderState.DELIVERED).end(OrderState.CANCELLED);}// 转换配置后续讲解...
}
2. 事件(Event):状态转换的“触发信号”
事件是触发状态转换的“信号载体”——没有事件,状态机的状态永远不会变化。例如“支付”事件(PAY)触发订单从“CREATED”转为“PAID”,“发货”事件(SHIP)触发从“PAID”转为“SHIPPED”。
(1)事件的本质:信号 + 数据
SSM 中的事件不仅是“触发信号”,还能携带业务数据,其核心组成包括:
payload(事件载荷):事件关联的业务数据,例如“支付事件”的payload可包含订单 ID、支付金额、支付方式等。headers(事件头):事件的扩展信息,非业务核心但需传递的内容,例如事件发送时间、发送者 ID、重试次数等。
(2)源码剖析:MessageEvent 与事件发布流程
SSM 中的事件本质是 org.springframework.messaging.Message 的封装,核心实现类为 org.springframework.statemachine.event.MessageEvent,其源码简化如下:
public class MessageEvent extends StateMachineEvent {// 封装的 Message 对象(包含 payload 和 headers)private final Message<?> message;public MessageEvent(Object source, Message<?> message) {super(source);this.message = message;}// 获取事件 payloadpublic Object getPayload() {return message.getPayload();}// 获取事件 headerspublic MessageHeaders getHeaders() {return message.getHeaders();}
}
事件的发布依赖 org.springframework.statemachine.event.StateMachineEventPublisher 接口,其核心方法 publishEvent(StateMachineEvent event) 负责将事件广播给状态机。完整发布流程如下:
- 业务代码通过
StateMachine.sendEvent(Message<?> message)发送事件; - 状态机内部将
Message封装为MessageEvent; StateMachineEventPublisher调用ApplicationEventPublisher(Spring 事件机制)发布MessageEvent;- 状态机监听该事件,触发后续的状态转换逻辑。
(3)实战:电商订单事件定义与发送
步骤 1:定义订单事件枚举
// 订单事件枚举(触发状态转换的信号)
public enum OrderEvent {PAY("支付"), // 触发 CREATED → PAIDSHIP("发货"), // 触发 PAID → SHIPPEDDELIVER("确认收货"),// 触发 SHIPPED → DELIVEREDCANCEL("取消订单"); // 触发 CREATED → CANCELLED
}
步骤 2:定义事件载荷(业务数据)
// 支付事件的载荷(包含订单ID、支付金额、支付方式)
public class OrderPayPayload {private Long orderId; // 订单IDprivate BigDecimal amount; // 支付金额private String payType; // 支付方式(ALIPAY/WECHAT)// getter/setter 省略...
}
步骤 3:发送事件(携带数据)
通过 StateMachine 的 sendEvent 方法发送事件,结合 MessageBuilder 封装 payload 和 headers:
@Service
public class OrderStateService {@Autowiredprivate StateMachine<OrderState, OrderEvent> stateMachine;// 发送支付事件(触发订单支付)public void sendPayEvent(Long orderId, BigDecimal amount, String payType) {// 1. 构建事件载荷OrderPayPayload payload = new OrderPayPayload();payload.setOrderId(orderId);payload.setAmount(amount)