Spring StateMachine實(shí)現(xiàn)狀態(tài)機(jī)使用示例詳解
什么是狀態(tài)機(jī)
狀態(tài)機(jī)是計(jì)算機(jī)科學(xué)中的??核心建模工具??,用于描述對(duì)象在其生命周期內(nèi)狀態(tài)變化的邏輯模型。它通過定義??有限狀態(tài)集合??、??狀態(tài)轉(zhuǎn)移規(guī)則??和??觸發(fā)事件??,精確控制系統(tǒng)的行為邏輯。
本文主要講Spring StateMachine實(shí)現(xiàn)的狀態(tài)機(jī)
使用示例
導(dǎo)入maven依賴
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>4.0.0</version>
</dependency>創(chuàng)建訂單狀態(tài)枚舉類
public enum OrderStatusEnum {
WAITING_PAYMENT,//待支付
WAITING_RECEIVE,//待取貨
FINISHED,//已完成
CANCELED;//已取消
}創(chuàng)建訂單轉(zhuǎn)悠事件枚舉類
public enum OrderEvent {
CREATE_ORDER,//創(chuàng)建訂單
PAY_ORDER,//支付訂單
RECEIVE_ORDER,//取貨
CANCEL_ORDER,//取消訂單
FINISH_ORDER//完成訂單
}添加狀態(tài)機(jī)實(shí)例并添加狀態(tài)轉(zhuǎn)移規(guī)則
@Configuration
@EnableStateMachine(name="orderStateMachine")
public class OrderStateMachineConfig {
@Bean
public StateMachine<OrderStatusEnum, OrderEvent> orderStateMachine() throws Exception {
StateMachineBuilder.Builder<OrderStatusEnum, OrderEvent> builder = StateMachineBuilder.builder();
// 配置狀態(tài)
builder.configureStates()
.withStates()
.initial(OrderStatusEnum.WAITING_PAYMENT) // 初始狀態(tài)
.states(EnumSet.allOf(OrderStatusEnum.class));// 配置所有狀態(tài)
// 配置狀態(tài)轉(zhuǎn)換 規(guī)則
builder.configureTransitions()
// 訂單待支付 -> 待取貨
.withExternal()
.source(OrderStatusEnum.WAITING_PAYMENT).target(OrderStatusEnum.WAITING_RECEIVE)
.event(OrderEvent.PAY_ORDER)
.and()
// 訂單待取貨 -> 已完成
.withExternal()
.source(OrderStatusEnum.WAITING_RECEIVE).target(OrderStatusEnum.FINISHED)
.event(OrderEvent.FINISH_ORDER)
.and()
// 訂單待支付 -> 已取消
.withExternal()
.source(OrderStatusEnum.WAITING_PAYMENT).target(OrderStatusEnum.CANCELED)
.event(OrderEvent.CANCEL_ORDER);
return builder.build();
}
}注入上下文bean,方法作用后面會(huì)說(shuō)
@Bean
public DefaultStateMachinePersister persister(){
return new DefaultStateMachinePersister<>(new StateMachinePersist<Object, Object, Order>() {
@Override
public void write(StateMachineContext<Object, Object> context, Order order) throws Exception {
//此處并沒有進(jìn)行持久化操作
System.out.println("訂單狀態(tài)持久化:" + context.getState());
}
@Override
public StateMachineContext<Object, Object> read(Order order) throws Exception {
//此處直接獲取order中的狀態(tài),其實(shí)并沒有進(jìn)行持久化讀取操作
return new DefaultStateMachineContext(order.getStatus(), null, null, null);
}
});
}創(chuàng)建調(diào)用示例服務(wù)
@Service
@RequiredArgsConstructor
public class OrderService {
private final StateMachine<OrderStatusEnum, OrderEvent> orderStateMachine;
private final DefaultStateMachinePersister persister;
// 1、事件: 支付
// 2、狀態(tài)轉(zhuǎn)換:待支付 → 待發(fā)貨\
public Order pay() {
Order order = Order
.builder()
.orderId("1")
.status(OrderStatusEnum.WAITING_RECEIVE)
.build();
System.out.println("線程名稱:" + Thread.currentThread().getName() + " 嘗試支付,訂單號(hào):" + order.getOrderId());
Message message = MessageBuilder.withPayload(OrderEvent.PAY_ORDER).setHeader("order", order).build();
if (!sendEvent(message, order)) {
System.out.println("線程名稱:" + Thread.currentThread().getName() + " 支付失敗, 狀態(tài)異常,訂單號(hào):" + order.getOrderId());
}
System.out.println("線程名稱:" + Thread.currentThread().getName() + " 支付成功,訂單號(hào):" + order.getOrderId());
return order;
}
/**
* 發(fā)送訂單狀態(tài)轉(zhuǎn)換事件
*
* @param message
* @param order
* @return
*/
private synchronized boolean sendEvent(Message<OrderEvent> message, Order order) {
boolean result = false;
try {
orderStateMachine.start();
//嘗試恢復(fù)狀態(tài)機(jī)狀態(tài)
persister.restore(orderStateMachine, order);
//添加延遲用于線程安全測(cè)試
Thread.sleep(1000);
result = orderStateMachine.sendEvent(message);
//持久化狀態(tài)機(jī)狀態(tài)
persister.persist(orderStateMachine, order);
} catch (Exception e) {
e.printStackTrace();
} finally {
orderStateMachine.stop();
}
return result;
}
}代碼解析:
創(chuàng)建訂單示例模擬數(shù)據(jù)庫(kù)實(shí)際訂單信息,基于當(dāng)前訂單狀態(tài)變換的流轉(zhuǎn)與事件創(chuàng)建對(duì)應(yīng)message消息,同時(shí)添加head信息。調(diào)用sendEvent方法使用狀態(tài)機(jī)判斷。
public Order pay() {
Order order = Order
.builder()
.orderId("1")
.status(OrderStatusEnum.WAITING_RECEIVE)
.build();
System.out.println("線程名稱:" + Thread.currentThread().getName() + " 嘗試支付,訂單號(hào):" + order.getOrderId());
Message message = MessageBuilder.withPayload(OrderEvent.PAY_ORDER).setHeader("order", order).build();
if (!sendEvent(message, order)) {
System.out.println("線程名稱:" + Thread.currentThread().getName() + " 支付失敗, 狀態(tài)異常,訂單號(hào):" + order.getOrderId());
}
System.out.println("線程名稱:" + Thread.currentThread().getName() + " 支付成功,訂單號(hào):" + order.getOrderId());
return order;
}判斷邏輯很簡(jiǎn)單,
.start()啟動(dòng)狀態(tài)機(jī),
.restore()方法跟蹤發(fā)現(xiàn)實(shí)際調(diào)用的是上面上下文bean中的read()方法,作用是恢復(fù)狀態(tài)機(jī)的上下文,結(jié)果一番搜索后,博主終于明白這步的作用。首先我們要知道Spring StateMachine狀態(tài)機(jī)是無(wú)狀態(tài)的,每次啟動(dòng)都會(huì)是默認(rèn)的狀態(tài),而他狀態(tài)判斷邏輯不會(huì)從所有規(guī)則中尋找符合的規(guī)則,而是基于狀態(tài)機(jī)當(dāng)前的狀態(tài)與傳入的事件進(jìn)行判斷狀態(tài)流轉(zhuǎn)是否正確,因此需要將當(dāng)前狀態(tài)與狀態(tài)機(jī)中的狀態(tài)進(jìn)行同步
.sendEvent()方法是實(shí)際調(diào)用狀態(tài)機(jī)內(nèi)部的進(jìn)行狀態(tài)流轉(zhuǎn)規(guī)則的判斷,符合狀態(tài)流轉(zhuǎn)規(guī)則時(shí)對(duì)狀態(tài)進(jìn)行變換,同時(shí)符合規(guī)則轉(zhuǎn)換成功時(shí)返回true,否則返回false
通過dubug追蹤看到,狀態(tài)變換成功,同時(shí)返回true


.persist()方法內(nèi)部實(shí)際調(diào)用上下文中的write方法,可以在此進(jìn)行實(shí)際的數(shù)據(jù)持久化
接著在finally中停止?fàn)顟B(tài)機(jī)
private synchronized boolean sendEvent(Message<OrderEvent> message, Order order) {
boolean result = false;
try {
orderStateMachine.start();
//嘗試恢復(fù)狀態(tài)機(jī)狀態(tài)
persister.restore(orderStateMachine, order);
//添加延遲用于線程安全測(cè)試
Thread.sleep(1000);
result = orderStateMachine.sendEvent(message);
//持久化狀態(tài)機(jī)狀態(tài)
persister.persist(orderStateMachine, order);
} catch (Exception e) {
e.printStackTrace();
} finally {
orderStateMachine.stop();
}
return result;
}至此,基本的狀態(tài)機(jī)就順利實(shí)現(xiàn)
到此這篇關(guān)于Spring StateMachine實(shí)現(xiàn)狀態(tài)機(jī)使用示例詳解的文章就介紹到這了,更多相關(guān)Spring StateMachine狀態(tài)機(jī)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談一下數(shù)據(jù)庫(kù)連接池Druid德魯伊
數(shù)據(jù)庫(kù)連接池就是一個(gè)容器持有多個(gè)數(shù)據(jù)庫(kù)連接,當(dāng)程序需要操作數(shù)據(jù)庫(kù)的時(shí)候直接從池中取出連接,使用完之后再還回去,和線程池一個(gè)道理,需要的朋友可以參考下2023-05-05
實(shí)戰(zhàn)指南:Java編寫Flink?SQL解決難題
想知道如何利用Java編寫Flink?SQL解決難題嗎?本指南將為您揭示最實(shí)用的技巧和策略,讓您輕松應(yīng)對(duì)挑戰(zhàn),跟著我們一起探索,讓Java和Flink?SQL成為您問題解決的得力助手!2023-12-12
基于java.lang.IllegalArgumentException異常報(bào)錯(cuò)問題及解決
這篇文章主要介紹了基于java.lang.IllegalArgumentException異常報(bào)錯(cuò)問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
IDEA?code?template配置和參數(shù)方式
這篇文章主要介紹了IDEA?code?template配置和參數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教<BR>2024-01-01
使用BitSet位集合,一個(gè)重復(fù)校驗(yàn)工具
這篇文章主要介紹了使用BitSet位集合,一個(gè)重復(fù)校驗(yàn)工具,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
使用Mybatis-plus策略自動(dòng)更新數(shù)據(jù)庫(kù)時(shí)間失敗問題解決
這篇文章主要介紹了使用Mybatis-plus策略自動(dòng)更新數(shù)據(jù)庫(kù)時(shí)間失敗問題解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10

