摘要:狀態機的使用細節上一篇文章介紹了以及三款狀態機引擎的實現原理,以及我為何選擇作為解決方案。被代理的默認實現為,內部描述了狀態機實例創建細節包括類型信息等,同時也包含了的一些全局共享資源包括等。
squirrel-foundation狀態機的使用細節
date: 2017-06-19 15:50:18
上一篇文章介紹了stateless4j、spring-statemachine以及squirrel-foundation三款狀態機引擎的實現原理,以及我為何選擇squirrel-foundation作為解決方案。本文主要介紹一下項目中如何使用squirrel-foundation的一些細節以及如何與spring進行集成。在閱讀本文前,建議先閱讀官方的使用手冊。
生命周期 狀態機創建過程StateMachine: StateMachine實例由StateMachineBuilder創建不被共享,對于使用annotation方式(或fluent api)定義的StateMachine,StateMachine實例即根據此定義創建,相應的action也由本實例執行,與spring的集成最終要的就是講spring的bean實例注入給由builder創建的狀態機實例;
StateMachineBuilder: 本質上是由StateMachineBuilderFactory創建的動態代理。被代理的StateMachineBuilder默認實現為StateMachineBuilderImpl,內部描述了狀態機實例創建細節包括State、Event、Context類型信息、constructor等,同時也包含了StateMachine的一些全局共享資源包括StateConverter、EventConverter、MvelScriptManager等。StateMachineBuilder可被復用,使用中可被實現為singleton;
StateMachineBuilderFactory: 為StateMachineBuilder創建的動態代理實例;
事件處理過程狀態正常遷移
TransitionBegin--(exit->transition->entry)-->TransitionComplete-->TransitionEnd
狀態遷移異常
TransitionBegin--(exit->transition->entry)-->TransitionException-->TransitionEnd
狀態遷移事件拒絕
TransitionBegin-->TransitionDeclined-->TransitionEnd
從statemachine的生命流程上可以看到,StateMachineBuilder可以單例方式由spring container管理,而stateMachine的instance的生命周期伴隨著請求(或業務)。
從這兩點出發,集成spring需要完成兩件事:
(1).通過Spring創建StateMachineBuilder實例;
(2).業務函數中通過(1)的StateMachineBuilder實例創建StateMachine實例,并向StateMachine暴露SpringApplicationContext;
泛型參數+覆蓋默認構造函數隱藏StateMachineBuilder創建細節,實現ApplicationContextAware接口,接受applicationContext注入,并注入給stateMachine實例。
public abstract class AbstractStateMachineEngineimplements ApplicationContextAware { protected UntypedStateMachineBuilder stateMachineBuilder = null; @SuppressWarnings("unchecked") public AbstractStateMachineEngine() { //識別泛型參數 Class genericType = (Class )GenericTypeResolver.resolveTypeArgument(getClass(), AbstractStateMachineEngine.class); stateMachineBuilder = StateMachineBuilderFactory.create(genericType, ApplicationContext.class); } //注入applicationContext,并在創建StateMachine實例時注入 @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } //delegate fire public void fire(int rmaId, State initialState, Trigger trigger, StateMachineContext context) { T stateMachine = stateMachineBuilder.newUntypedStateMachine( initialState //暫時開啟debug進行日志trace StateMachineConfiguration.create().enableDebugMode(true).enableAutoStart(true), //注入applicationContext applicationContext); stateMachine.fire(trigger, context); } ... } @Service class DiscountRefundStateMachineEngine extends AbstractStateMachineEngine { } @Service public class ReturnGoodsStateMachineEngine extends AbstractStateMachineEngine { }
StateMachine定義,接受SpringContext注入
@StateMachineParameters(stateType = State.class, eventType = Trigger.class, //StateMachineContext 自定義上下文,用來傳遞數據 contextType = StateMachineContext.class) @States({ @State(name = "PENDING", initialState = true), @State(name = "CONFIRMING"), @State(name = "REJECTED"), @State(name = "REFUND_APPROVING"), @State(name = "REFUND_APPROVED"), @State(name = "REFUND_FINISHED") }) @Transitions({ @Transit(from = "PENDING", to = "CONFIRMING", on = "APPLY_CONFIRM", callMethod = "doSomething"), @Transit(from = "CONFIRMING", to = "REJECTED", on = "REJECT"), @Transit(from = "CONFIRMING", to = "REFUND_APPROVING", on = "APPLY_APPROVED"), @Transit(from = "REFUND_APPROVING", to = "REFUND_APPROVED", on = "REFUND_APPROVED"), @Transit(from = "REFUND_APPROVED", to = "REFUND_FINISHED", on = "REFUND_FINISH_CONFIRM") }) public class DiscountRefundStateMachine extends AbstractUntypedStateMachine { protected ApplicationContext applicationContext; //定義構造函數接受ApplicationContext注入([參看New State Machine Instance](http://hekailiang.github.io/squirrel/)) public DiscountRefundStateMachine(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public void doSomething(State fromState, State toState, Trigger event, StateMachineContext stateMachineContext) { DemoBean demoBean = this.applicationContext.get("demoBean"); //do something } ... }狀態持久化
從StateMachine的事件響應流程中可以看到,TransitionBegin--(exit->transition->entry)-->TransitionComplete-->TransitionEnd,在TransitionComplete發生一個狀態已從source遷移到了target狀態,所以我選擇了在這個時間點進行了狀態的持久化(沒有選擇TransitionEnd做持久化,因為某些場景在持久化完成后還會存在一些外部動作的觸發,例如通知第三方系統當前狀態已完成變更)。
public class DiscountRefundStateMachine extends AbstractUntypedStateMachine { .. @Override protected void afterTransitionCompleted(Object fromState, Object toState, Object event, Object context) { if (context instanceof StateMachineContext && toState instanceof State) { StateMachineContext stateMachineContext = (StateMachineContext)context; //從上下文中獲取需要持久化的數據,例如訂單ID等 Rma rma = stateMachineContext.get(MessageKeyEnum.RMA); //持久化 rma.setStatus((State)toState); this.applicationContext.get("rmaRepository").updateRma(rma); } else { throw new Exception("type not support, context expect " + StateMachineContext.class.getSimpleName() + ", actually " + context.getClass().getSimpleName() + ", state expect " + State.class.getSimpleName() + ", actually " + toState.getClass().getSimpleName()); } } }分布式鎖+事務
由于StateMachine實例不是由Spring容器創建,所以這個過程中無法通過注解方式開啟事務(Spring沒有機會去創建事務代理),我采用了編程式事務,在AbstractStateMachineEngine的fire函數中隱式的實現。
AbstractStateMachineEngine#fire
public abstract class AbstractStateMachineEngine使用graphviz生成狀態拓撲圖implements ApplicationContextAware { ... public void fire(int rmaId, State initialState, Trigger trigger, StateMachineContext context) { JedisLock jedisLock = jedisLockFactory.buildLock(rmaId); //爭用分布式鎖 if (jedisLock.tryLock()) { try { T stateMachine = stateMachineBuilder.newUntypedStateMachine( initialState //暫時開啟debug進行日志trace StateMachineConfiguration.create().enableDebugMode(true).enableAutoStart(true), //注入applicationContext applicationContext); DataSourceTransactionManager transactionManager = applicationContext.get("transactionManager") DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = transactionManager.getTransaction(def); try { stateMachine.fire(trigger, context) transactionManager.commit(status); } catch (Exception ex) { transactionManager.rollback(status); throw ex; } } finally { jedisLock.release(); } } ... } }
squirrel statemachine提供了DotVisitor、SCXMLVisitor兩種實現方式用于生成狀態機描述文件,項目里我選擇了graphviz用來做狀態拓撲
graphviz gui工具下載
PS:由于squirrel默認的DotVisitorImpl對帶中文描述屬性的State/Event枚舉不友好,我在原有代碼上做了一些調整,有類似需求的可以看這里
更多文章請訪問我的博客
轉載請注明出處
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67214.html
地址的識別和分析是本地搜索必不可少的技術,盡管有許多識別和分析地址的方法,最有效的是有限狀態機。一個有限狀態機是一個特殊的有向圖(參見有關圖論的系列),它包括一些狀態(節點)和連接這些狀態的有向弧。下圖是一個識別中國地址的有限狀態機的簡單的例子。每一個有限狀態機都有一個啟始狀態和一個終止狀態和若干中間狀態。每一條弧上帶有從一個狀態進入下一個狀態的條件。比如,在上圖中,當前的狀態是省,如果遇到一個詞...
摘要:和的云計算功能特點對比正是這個戰爭或者說趨勢的一個生動寫照。在設計方面稍占優勢,這源于它優秀的文檔資料以及便捷易用的部署和管理接口。總而言之,目前調度器將只會對部署虛擬機環節有影響。 在云計算生態系統中,有兩種類型的用戶需要使用云計算資源:傳統型(Traditional IT applications)和在互聯網大潮下逐漸崛起云計算應用型(Cloud-aware applications)。...
閱讀 712·2021-11-22 13:52
閱讀 1518·2021-09-27 13:36
閱讀 2818·2021-09-24 09:47
閱讀 2172·2021-09-22 15:48
閱讀 3600·2021-09-22 15:39
閱讀 1463·2019-08-30 12:43
閱讀 2918·2019-08-29 18:39
閱讀 3181·2019-08-29 12:51