Spring狀態(tài)機(jī)的使用解讀
Spring狀態(tài)機(jī)深度解析:從入門到生產(chǎn)實(shí)戰(zhàn)
Spring State Machine是Spring生態(tài)系統(tǒng)中一個(gè)強(qiáng)大的狀態(tài)機(jī)框架,它讓復(fù)雜的狀態(tài)流轉(zhuǎn)變得優(yōu)雅而簡單。本文將帶你從基礎(chǔ)概念出發(fā),逐步深入理解并掌握Spring狀態(tài)機(jī)在實(shí)際生產(chǎn)環(huán)境中的應(yīng)用。
一、狀態(tài)機(jī)是什么?為什么要用它?
想象一下訂單系統(tǒng):用戶下單后,訂單會(huì)經(jīng)歷"待支付→已支付→待發(fā)貨→已發(fā)貨→已完成"等一系列狀態(tài)變化。如果在代碼里用if-else來處理這些狀態(tài)流轉(zhuǎn),很快就會(huì)變成一團(tuán)亂麻。
狀態(tài)機(jī)(State Machine)就是解決這類問題的利器!它明確定義了:
- 狀態(tài)(State):系統(tǒng)可能處于的狀態(tài)
- 事件(Event):觸發(fā)狀態(tài)變化的動(dòng)作
- 轉(zhuǎn)換(Transition):狀態(tài)之間的流轉(zhuǎn)規(guī)則

二、Spring狀態(tài)機(jī)核心概念
2.1 三大核心組件
// 1. 定義狀態(tài)枚舉
public enum OrderStatus {
WAIT_PAYMENT, // 待支付
PAID, // 已支付
WAIT_DELIVER, // 待發(fā)貨
DELIVERED, // 已發(fā)貨
COMPLETED, // 已完成
CANCELLED // 已取消
}
// 2. 定義事件枚舉
public enum OrderEvent {
PAY, // 支付
DELIVER, // 發(fā)貨
RECEIVE, // 收貨
CANCEL // 取消
}
// 3. 配置狀態(tài)機(jī)
@Configuration
@EnableStateMachine
public class OrderStateMachineConfig
extends StateMachineConfigurerAdapter<OrderStatus, OrderEvent> {
@Override
public void configure(StateMachineStateConfigurer<OrderStatus, OrderEvent> states)
throws Exception {
states.withStates()
.initial(OrderStatus.WAIT_PAYMENT)
.states(EnumSet.allOf(OrderStatus.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvent> transitions)
throws Exception {
transitions
.withExternal()
.source(OrderStatus.WAIT_PAYMENT)
.target(OrderStatus.PAID)
.event(OrderEvent.PAY)
.and()
.withExternal()
.source(OrderStatus.PAID)
.target(OrderStatus.WAIT_DELIVER)
.event(OrderEvent.DELIVER)
.and()
.withExternal()
.source(OrderStatus.WAIT_DELIVER)
.target(OrderStatus.DELIVERED)
.event(OrderEvent.RECEIVE)
.and()
.withExternal()
.source(OrderStatus.DELIVERED)
.target(OrderStatus.COMPLETED)
.event(OrderEvent.RECEIVE);
}
}

2.2 狀態(tài)持久化
生產(chǎn)環(huán)境中,狀態(tài)必須持久化。Spring狀態(tài)機(jī)支持多種持久化方式:
@Service
@RequiredArgsConstructor
public class OrderService {
private final StateMachineFactory<OrderStatus, OrderEvent> factory;
private final StateMachinePersist<OrderStatus, OrderEvent, String> persist;
public boolean pay(String orderId) {
StateMachine<OrderStatus, OrderEvent> sm = restoreStateMachine(orderId);
boolean result = sm.sendEvent(OrderEvent.PAY);
if (result) {
persistStateMachine(orderId, sm);
// 發(fā)送支付成功消息
publishPaymentSuccessEvent(orderId);
}
return result;
}
private StateMachine<OrderStatus, OrderEvent> restoreStateMachine(String orderId) {
try {
return persist.restore(factory.getStateMachine(), orderId);
} catch (Exception e) {
throw new RuntimeException("恢復(fù)狀態(tài)機(jī)失敗", e);
}
}
private void persistStateMachine(String orderId, StateMachine<OrderStatus, OrderEvent> sm) {
try {
persist.persist(sm, orderId);
} catch (Exception e) {
throw new RuntimeException("保存狀態(tài)機(jī)失敗", e);
}
}
}

三、生產(chǎn)實(shí)戰(zhàn):工作流引擎
讓我們看一個(gè)更復(fù)雜的例子——審批工作流系統(tǒng):
// 支持并行審批的復(fù)雜狀態(tài)機(jī)
@Configuration
@EnableStateMachine(name = "workflowStateMachine")
public class WorkflowStateMachineConfig
extends EnumStateMachineConfigurerAdapter<WorkflowState, WorkflowEvent> {
@Override
public void configure(StateMachineStateConfigurer<WorkflowState, WorkflowEvent> states)
throws Exception {
states
.withStates()
.initial(WorkflowState.DRAFT)
.fork(WorkflowState.FORK)
.join(WorkflowState.JOIN)
.state(WorkflowState.FINISHED)
.and()
.withStates()
.parent(WorkflowState.FORK)
.initial(WorkflowState.DEPT_APPROVAL)
.state(WorkflowState.DEPT_APPROVED)
.and()
.withStates()
.parent(WorkflowState.FORK)
.initial(WorkflowState.FINANCE_APPROVAL)
.state(WorkflowState.FINANCE_APPROVED);
}
@Override
public void configure(StateMachineTransitionConfigurer<WorkflowState, WorkflowEvent> transitions)
throws Exception {
transitions
// 提交到并行審批
.withExternal()
.source(WorkflowState.DRAFT)
.target(WorkflowState.FORK)
.event(WorkflowEvent.SUBMIT)
// 部門審批分支
.and()
.withExternal()
.source(WorkflowState.DEPT_APPROVAL)
.target(WorkflowState.DEPT_APPROVED)
.event(WorkflowEvent.DEPT_APPROVE)
// 財(cái)務(wù)審批分支
.and()
.withExternal()
.source(WorkflowState.FINANCE_APPROVAL)
.target(WorkflowState.FINANCE_APPROVED)
.event(WorkflowEvent.FINANCE_APPROVE)
// 合并后完成
.and()
.withExternal()
.source(WorkflowState.JOIN)
.target(WorkflowState.FINISHED)
.event(WorkflowEvent.COMPLETE);
}
}

四、狀態(tài)監(jiān)聽器:記錄每一次變化

@WithStateMachine
public class OrderStateListener {
private static final Logger log = LoggerFactory.getLogger(OrderStateListener.class);
@OnTransition(target = "PAID")
public void onPay(Message<OrderEvent> message) {
String orderId = getHeader(message, "orderId");
log.info("訂單{}支付成功,狀態(tài)流轉(zhuǎn)到已支付", orderId);
// 觸發(fā)后續(xù)業(yè)務(wù)邏輯
paymentSuccessHandler.handle(orderId);
}
@OnTransition(target = "DELIVERED")
public void onDeliver(Message<OrderEvent> message) {
String orderId = getHeader(message, "orderId");
log.info("訂單{}已發(fā)貨,狀態(tài)流轉(zhuǎn)到已發(fā)貨", orderId);
// 發(fā)送短信通知
smsService.sendDeliverySms(orderId);
}
@OnTransitionEnd
public void onTransitionEnd(StateContext<OrderStatus, OrderEvent> context) {
log.info("狀態(tài)轉(zhuǎn)換完成:{} -> {}, 事件:{}",
context.getSource().getId(),
context.getTarget().getId(),
context.getEvent()
);
// 持久化狀態(tài)轉(zhuǎn)換記錄
transitionLogService.log(context);
}
private String getHeader(Message<OrderEvent> message, String headerName) {
return message.getHeaders().get(headerName, String.class);
}
}
五、Guards:智能的狀態(tài)轉(zhuǎn)換守衛(wèi)

@Component
public class OrderGuard {
@Bean
public Guard<OrderStatus, OrderEvent> payGuard() {
return context -> {
String orderId = context.getMessageHeader("orderId");
BigDecimal amount = orderService.getOrderAmount(orderId);
// 檢查訂單金額
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
log.warn("訂單{}支付失?。航痤~為0", orderId);
return false;
}
// 檢查庫存
boolean hasStock = inventoryService.checkStock(orderId);
if (!hasStock) {
log.warn("訂單{}支付失?。簬齑娌蛔?, orderId);
return false;
}
return true;
};
}
}
// 在狀態(tài)機(jī)配置中使用guard
@Override
public void configure(StateMachineTransitionConfigurer<OrderStatus, OrderEvent> transitions)
throws Exception {
transitions
.withExternal()
.source(OrderStatus.WAIT_PAYMENT)
.target(OrderStatus.PAID)
.event(OrderEvent.PAY)
.guard(payGuard()); // 添加守衛(wèi)條件
}
六、實(shí)戰(zhàn)技巧與最佳實(shí)踐
6.1 狀態(tài)機(jī)可視化
@RequestMapping("/state-machine")
public class StateMachineVisualController {
@GetMapping("/diagram/{orderId}")
public ResponseEntity<String> getStateDiagram(@PathVariable String orderId) {
// 獲取當(dāng)前狀態(tài)
OrderStatus currentStatus = orderService.getOrderStatus(orderId);
// 生成PlantUML格式的狀態(tài)圖
String diagram = generatePlantUMLDiagram(currentStatus);
return ResponseEntity.ok()
.contentType(MediaType.TEXT_PLAIN)
.body(diagram);
}
private String generatePlantUMLDiagram(OrderStatus currentStatus) {
StringBuilder sb = new StringBuilder();
sb.append("@startuml\n");
sb.append("[*] --> WAIT_PAYMENT\n");
sb.append("WAIT_PAYMENT --> PAID : PAY\n");
sb.append("PAID --> WAIT_DELIVER : DELIVER\n");
sb.append("WAIT_DELIVER --> DELIVERED : RECEIVE\n");
sb.append("DELIVERED --> COMPLETED : RECEIVE\n");
// 高亮當(dāng)前狀態(tài)
sb.append("skinparam state {\n");
sb.append(" BackgroundColor<<Current>> LightBlue\n");
sb.append("}\n");
sb.append("state ").append(currentStatus).append(" <<Current>>\n");
sb.append("@enduml\n");
return sb.toString();
}
}
6.2 分布式狀態(tài)一致性

// 使用分布式鎖確保狀態(tài)轉(zhuǎn)換的原子性
@Service
public class DistributedOrderService {
private final RedissonClient redisson;
private final StateMachineFactory<OrderStatus, OrderEvent> factory;
public boolean transition(String orderId, OrderEvent event) {
RLock lock = redisson.getLock("order:state:" + orderId);
try {
// 最多等待3秒,持鎖10秒
if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
// 恢復(fù)狀態(tài)機(jī)
StateMachine<OrderStatus, OrderEvent> sm = restoreStateMachine(orderId);
// 發(fā)送事件并處理結(jié)果
boolean result = sm.sendEvent(event);
if (result) {
// 持久化新狀態(tài)
persistStateMachine(orderId, sm);
// 發(fā)布領(lǐng)域事件
publishDomainEvent(orderId, event, sm.getState().getId());
}
return result;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("狀態(tài)轉(zhuǎn)換被中斷", e);
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
return false;
}
}

七、性能優(yōu)化建議
- 狀態(tài)機(jī)緩存:頻繁使用的狀態(tài)機(jī)實(shí)例可以緩存,避免重復(fù)創(chuàng)建
- 異步事件處理:使用Spring的事件驅(qū)動(dòng)模型異步處理狀態(tài)變化
- 批量持久化:多個(gè)狀態(tài)變化可以合并為一次數(shù)據(jù)庫操作
- 讀寫分離:狀態(tài)查詢走從庫,狀態(tài)更新走主庫
八、總結(jié)
Spring狀態(tài)機(jī)的優(yōu)勢(shì)在于:
- 代碼清晰:將復(fù)雜的狀態(tài)流轉(zhuǎn)從業(yè)務(wù)代碼中分離
- 易于維護(hù):狀態(tài)轉(zhuǎn)換規(guī)則集中管理
- 可測(cè)試性強(qiáng):可以單獨(dú)測(cè)試狀態(tài)機(jī)邏輯
- 生產(chǎn)就緒:支持持久化、監(jiān)聽、分布式等高級(jí)特性
當(dāng)你的業(yè)務(wù)涉及復(fù)雜的狀態(tài)流轉(zhuǎn)時(shí),Spring狀態(tài)機(jī)絕對(duì)是你的得力助手。它讓狀態(tài)管理變得優(yōu)雅,讓代碼更容易理解和維護(hù)。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Boot文件上傳原理與實(shí)現(xiàn)詳解
這篇文章主要介紹了Spring Boot 文件上傳原理與實(shí)現(xiàn)詳解,前端文件上傳是面向多用戶的,多用戶之間可能存在上傳同一個(gè)名稱、類型的文件;為了避免文件沖突導(dǎo)致的覆蓋問題這些應(yīng)該在后臺(tái)進(jìn)行解決,需要的朋友可以參考下2024-01-01
Java微信公眾平臺(tái)開發(fā)(11) 微信三大平臺(tái)的關(guān)聯(lián)
這篇文章主要介紹了Java微信公眾平臺(tái)開發(fā)第十一步,微信開發(fā)中微信公眾平臺(tái)、開放平臺(tái)和商戶平臺(tái)的關(guān)聯(lián),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
springboot如何通過@PropertySource加載自定義yml文件
這篇文章主要介紹了springboot如何通過@PropertySource加載自定義yml文件,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
kafka 啟動(dòng)報(bào)錯(cuò) missingTopicsFatal is true的解決
這篇文章主要介紹了kafka 啟動(dòng)報(bào)錯(cuò) missingTopicsFatal is true的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
java實(shí)現(xiàn)把一個(gè)List集合拆分成多個(gè)的操作
這篇文章主要介紹了java實(shí)現(xiàn)把一個(gè)List集合拆分成多個(gè)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-08-08
Java方法遞歸的形式和常見遞歸算法(方法遞歸結(jié)合File類查找文件)
方法遞歸方法直接調(diào)用自己或者間接調(diào)用自己的形式稱為方法遞歸( recursion),遞歸做為一種算法在程序設(shè)計(jì)語言中廣泛應(yīng)用,這篇文章主要介紹了Java方法遞歸的形式和常見遞歸算法-方法遞歸結(jié)合File類查找文件,需要的朋友可以參考下2023-02-02

