上一篇文章介绍了stateless4j、spring-statemachine以及squirrel-foundation三款状态机引擎的实现原理,以及我为何选择squirrel-foundation作为解决方案。本文主要介绍一下项目中如何使用squirrel-foundation的一些细节以及如何与spring进行集成。在阅读本文前,建议先阅读官方的使用手册。
squirrel-foundation状态机的使用细节
date: 2017-06-19 15:50:18
生命周期
状态机创建过程
- 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
spring集成
从statemachine的生命流程上可以看到,StateMachineBuilder可以单例方式由spring container管理,而stateMachine的instance的生命周期伴随着请求(或业务)。
从这两点出发,集成spring需要完成两件事:
* (1).通过Spring创建StateMachineBuilder实例;
* (2).业务函数中通过(1)的StateMachineBuilder实例创建StateMachine实例,并向StateMachine暴露SpringApplicationContext;
泛型参数+覆盖默认构造函数隐藏StateMachineBuilder创建细节,实现ApplicationContextAware接口,接受applicationContext注入,并注入给stateMachine实例。
public abstract class AbstractStateMachineEngine<T extends UntypedStateMachine> implements ApplicationContextAware { protected UntypedStateMachineBuilder stateMachineBuilder = null; @SuppressWarnings("unchecked") public AbstractStateMachineEngine() { Class<T> genericType = (Class<T>)GenericTypeResolver.resolveTypeArgument(getClass(), AbstractStateMachineEngine.class); stateMachineBuilder = StateMachineBuilderFactory.create(genericType, ApplicationContext.class); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public void fire(int rmaId, State initialState, Trigger trigger, StateMachineContext context) { T stateMachine = stateMachineBuilder.newUntypedStateMachine( initialState StateMachineConfiguration.create().enableDebugMode(true).enableAutoStart(true), applicationContext); stateMachine.fire(trigger, context); } ...}@Serviceclass DiscountRefundStateMachineEngine extends AbstractStateMachineEngine<DiscountRefundStateMachine> {}@Servicepublic class ReturnGoodsStateMachineEngine extends AbstractStateMachineEngine<ReturnGoodsStateMachine> {}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
StateMachine定义,接受SpringContext注入
@StateMachineParameters(stateType = State.class, eventType = Trigger.class, 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; public DiscountRefundStateMachine(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public void doSomething(State fromState, State toState, Trigger event, StateMachineContext stateMachineContext) { DemoBean demoBean = this.applicationContext.get("demoBean"); } ...}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
状态持久化
从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; 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()); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
分布式锁+事务
由于StateMachine实例不是由Spring容器创建,所以这个过程中无法通过注解方式开启事务(Spring没有机会去创建事务代理),我采用了编程式事务,在AbstractStateMachineEngine的fire函数中隐式的实现。
AbstractStateMachineEngine#fire
public abstract class AbstractStateMachineEngine<T extends UntypedStateMachine> 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 StateMachineConfiguration.create().enableDebugMode(true).enableAutoStart(true), 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(); } } ... }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
文章转载自:http://blog.csdn.net/gkqcz/article/details/73641749