Spring StateMachine實(shí)現(xiàn)狀態(tài)機(jī)使用示例詳解
什么是狀態(tài)機(jī)
狀態(tài)機(jī)是計(jì)算機(jī)科學(xué)中的??核心建模工具??,用于描述對(duì)象在其生命周期內(nèi)狀態(tài)變化的邏輯模型。它通過(guò)定義??有限狀態(tài)集合??、??狀態(tài)轉(zhuǎn)移規(guī)則??和??觸發(fā)事件??,精確控制系統(tǒng)的行為邏輯。
本文主要講Spring StateMachine實(shí)現(xiàn)的狀態(tài)機(jī)
使用示例
導(dǎo)入maven依賴(lài)
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>4.0.0</version>
</dependency>創(chuàng)建訂單狀態(tài)枚舉類(lèi)
public enum OrderStatusEnum {
WAITING_PAYMENT,//待支付
WAITING_RECEIVE,//待取貨
FINISHED,//已完成
CANCELED;//已取消
}創(chuàng)建訂單轉(zhuǎn)悠事件枚舉類(lèi)
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 {
//此處并沒(méi)有進(jìn)行持久化操作
System.out.println("訂單狀態(tài)持久化:" + context.getState());
}
@Override
public StateMachineContext<Object, Object> read(Order order) throws Exception {
//此處直接獲取order中的狀態(tài),其實(shí)并沒(méi)有進(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("線程名稱(chēng):" + 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("線程名稱(chēng):" + Thread.currentThread().getName() + " 支付失敗, 狀態(tài)異常,訂單號(hào):" + order.getOrderId());
}
System.out.println("線程名稱(chēng):" + 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("線程名稱(chēng):" + 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("線程名稱(chēng):" + Thread.currentThread().getName() + " 支付失敗, 狀態(tài)異常,訂單號(hào):" + order.getOrderId());
}
System.out.println("線程名稱(chēng):" + 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
通過(guò)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)文章
MybatisPlus3.3.0沒(méi)有MybatisPlusInterceptor類(lèi)問(wèn)題的解決方法
項(xiàng)目使用的是mybatis-plus-extension3.3.0依賴(lài),然后在我使用分頁(yè)插件的時(shí)候,發(fā)現(xiàn)無(wú)法導(dǎo)入MybatisPlusInterceptor類(lèi)所以本文給大家介紹了MybatisPlus3.3.0沒(méi)有MybatisPlusInterceptor類(lèi)問(wèn)題的解決方法,需要的朋友可以參考下2023-12-12
SpringBoot Actuator埋點(diǎn)和監(jiān)控及簡(jiǎn)單使用
最近做的項(xiàng)目涉及到埋點(diǎn)監(jiān)控、報(bào)表、日志分析的相關(guān)知識(shí),于是搗鼓的一番,下面把涉及的知識(shí)點(diǎn)及SpringBoot Actuator埋點(diǎn)和監(jiān)控的簡(jiǎn)單用法,給大家分享下,感興趣的朋友一起看看吧2021-11-11
SpringBoot、mybatis返回樹(shù)結(jié)構(gòu)的數(shù)據(jù)實(shí)現(xiàn)
本文主要介紹了SpringBoot、mybatis返回樹(shù)結(jié)構(gòu)的數(shù)據(jù)實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
Java定時(shí)調(diào)用.ktr文件的示例代碼(解決方案)
這篇文章主要介紹了Java定時(shí)調(diào)用.ktr文件的示例代碼,本文給大家分享遇到問(wèn)題及解決方法,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04
教你用Java GUI實(shí)現(xiàn)文本文件的讀寫(xiě)
今天帶大家來(lái)學(xué)習(xí)怎么用JavaSwing實(shí)現(xiàn)實(shí)現(xiàn)文本文件讀寫(xiě),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很好的幫助,需要的朋友可以參考下2021-05-05
Mybatis中實(shí)體類(lèi)屬性與數(shù)據(jù)列表間映射方法介紹
這篇文章主要介紹了Mybatis中實(shí)體類(lèi)屬性與數(shù)據(jù)列表間映射方法介紹,一共四中方法,供大家參考。2017-10-10
Java中對(duì)List去重 Stream去重的解決方法
這篇文章主要介紹了Java中對(duì)List去重, Stream去重的問(wèn)題解答,文中給大家介紹了Java中List集合去除重復(fù)數(shù)據(jù)的方法,需要的朋友可以參考下2018-04-04

