相比Spring statemachine状态机等的复杂,功能多;我们更需要常用的功能,简单使用,所以这类就显得不简洁;再看cola-statemachine相比就是小巧、无状态、简单、轻量、性能极高的状态机DSL实现,解决业务中的状态流转问题。
github:
https://github.com/alibaba/COLA/tree/master/cola-components/cola-component-statemachine
概念:
State:状态
Event:事件,状态由事件触发,引起变化
Transition:流转,表示从一个状态到另一个状态
External Transition:外部流转,两个不同状态之间的流转
Internal Transition:内部流转,同一个状态之间的流转
Condition:条件,表示是否允许到达某个状态
Action:动作,到达某个状态之后,可以做什么
StateMachine:状态机
外部过程描述:起始状态STATE1,结束状态STATE2,当发生EVENT1时执行状态转移,当满足checkCondition()时,执行doAction,执行成功则返回状态STATE2,否则返回STATE1。
public class StateMachineTest {
static String MACHINE_ID = "TestStateMachine";
static enum States {
STATE1, STATE2, STATE3, STATE4
}
static enum Events {
EVENT1, EVENT2, EVENT3, EVENT4, INTERNAL_EVENT
}
static class Context{
String operator = "frank";
String entityId = "123465";
}
@Test
public void testExternalNormal(){
// 第一步:生成一个状态机builder
StateMachineBuilder builder = StateMachineBuilderFactory.create();
// 第二步:设置一个外部状态转移类型的builder,并设置from\to\on\when\perform
builder.externalTransition()
.from(States.STATE1)
.to(States.STATE2)
.on(Events.EVENT1)
.when(checkCondition())
.perform(doAction()); //这个action 我们可以按自己所需修改,比如这种Action service的方法Service::method
// 第三步:设置状态机的id和ready,并在StateMachineFactory中的stateMachineMap进行注册
StateMachine stateMachine = builder.build(MACHINE_ID);
// 第四步:触发状态机
States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context());
Assert.assertEquals(States.STATE2, target);
}
}
源码分析:这里只是简单介绍,整个流转的链路,因为具体的代码还是要根据自己的知识储备理解;
第一步:生成一个状态机builder
new com.alibaba.cola.statemachine.builder.StateMachineBuilderImpl
/**
* StateMap is the same with stateMachine, as the core of state machine is holding reference to states.
*/
private final Map> stateMap = new ConcurrentHashMap<>();
private final StateMachineImpl stateMachine = new StateMachineImpl<>(stateMap);
第二步:设置一个外部状态转移类型的builder,并设置from\to\on\when\perform
@Override
public ExternalTransitionBuilder externalTransition() {
return new TransitionBuilderImpl<>(stateMap, TransitionType.EXTERNAL);
}
TransitionBuilderImpl为多实现接口实现类,这个设计加上不同接口的唯一方法实现,可以让from to ,按DSL方式运行
class TransitionBuilderImpl implements ExternalTransitionBuilder, InternalTransitionBuilder, From, On, To {
上述构建就是一个状态流转的过程
class TransitionBuilderImpl implements ExternalTransitionBuilder, InternalTransitionBuilder, From, On, To {
//状态机
final Map> stateMap;
//原状态
private State source;
//目标状态
protected State target;
//该具体做事流转
private Transition transition;
//流转类型
final TransitionType transitionType;
...
// 此时只是把from的state新增到stateMap中,返回结果赋给本地变量source
@Override
public From from(S stateId) {
source = StateHelper.getState(stateMap, stateId);
return this;
}
// 此时只是把to的state新增到stateMap中,返回结果赋给本地变量target
@Override
public To to(S stateId) {
target = StateHelper.getState(stateMap, stateId);
return this;
}
//所以ON是在Form和to后面执行,因为它用本流程中的source,向来源state中加入Transition
//Transition是一个具体做事的流转,其中包含事件,目标状态和流转类型
//看下面源码就是一个事件对应Transition
@Override
public On on(E event) {
transition = source.addTransition(event, target, transitionType);
return this;
}
//该执行流转的条件
@Override
public When when(Condition condition) {
transition.setCondition(condition);
return this;
}
//设置transition的action
@Override
public void perform(Action action) {
transition.setAction(action);
}
...
}
第三步:
builder.build(MACHINE_ID) 此步就是状态机已构建完成,一般直接@Bean注入,使用时直接调用
public class StateMachineBuilderImpl implements StateMachineBuilder {
...
private final StateMachineImpl stateMachine = new StateMachineImpl<>(stateMap);
...
// 设置状态机的id和ready,并在StateMachineFactory中的stateMachineMap进行注册
@Override
public StateMachine build(String machineId) {
stateMachine.setMachineId(machineId);
stateMachine.setReady(true);
StateMachineFactory.register(stateMachine);
return stateMachine;
}
}
第四步:States target = stateMachine.fireEvent(States.STATE1, Events.EVENT1, new Context()); 业务具体使用时,这个方法入参是原状态和事件和action的入参,这个就按上面配置的状态机进行流转;from to on when perform
public class StateMachineImpl implements StateMachine {
private String machineId;
private final Map> stateMap;
private boolean ready;
public StateMachineImpl(Map> stateMap){
this.stateMap = stateMap;
}
public S fireEvent(S sourceStateId, E event, C ctx){
isReady();
State sourceState = getState(sourceStateId);
return doTransition(sourceState, event, ctx).getId();
}
private State doTransition(State sourceState, E event, C ctx) {
//根据原状态和事件获取流转transition
Optional> transition = sourceState.getTransition(event);
if(transition.isPresent()){
//ctx为执行入参
return transition.get().transit(ctx);
}
Debugger.debug("There is no Transition for " + event);
return sourceState;
Transition是具体做事的流转
/**
* TransitionImpl。
*
* This should be designed to be immutable, so that there is no thread-safe risk
*
* @author Frank Zhang
* @date 2020-02-07 10:32 PM
*/
public class TransitionImpl implements Transition {
private State source;
private State target;
private E event;
private Condition condition;
private Action action;
private TransitionType type = TransitionType.EXTERNAL;
...
@Override
public State transit(C ctx) {
Debugger.debug("Do transition: "+this);
this.verify();
if(condition == null || condition.isSatisfied(ctx)){
if(action != null){
action.execute(source.getId(), target.getId(), event, ctx);
}
return target;
}
Debugger.debug("Condition is not satisfied, stay at the "+source+" state ");
return source;
}
...
@Override
public final String toString() {
return source + "-[" + event.toString() +", "+type+"]->" + target;
}
@Override
public boolean equals(Object anObject){
if(anObject instanceof Transition){
Transition other = (Transition)anObject;
if(this.event.equals(other.getEvent())
&& this.source.equals(other.getSource())
&& this.target.equals(other.getTarget())){
return true;
}
}
return false;
}
@Override
public void verify() {
if(type== TransitionType.INTERNAL && source != target) {
throw new StateMachineException(String.format("Internal transition source state '%s' " +
"and target state '%s' must be same.", source, target));
}
}
State是整个流转中状态类
public interface State extends Visitable{
/**
* Gets the state identifier.
*
* @return the state identifiers
*/
S getId();
/**
* Add transition to the state
* @param event the event of the Transition
* @param target the target of the transition
* @return
*/
Transition addTransition(E event, State target, TransitionType transitionType);
Optional> getTransition(E event);
Collection> getTransitions();
}
public class StateImpl implements State {
protected final S stateId;
private HashMap> transitions = new HashMap<>();
StateImpl(S stateId){
this.stateId = stateId;
}
@Override
public Transition addTransition(E event, State target, TransitionType transitionType) {
Transition newTransition = new TransitionImpl<>();
newTransition.setSource(this);
newTransition.setTarget(target);
newTransition.setEvent(event);
newTransition.setType(transitionType);
Debugger.debug("Begin to add new transition: "+ newTransition);
verify(event, newTransition);
transitions.put(event, newTransition);
return newTransition;
}
/**
* @param event
* @param newTransition
*/
private void verify(E event, Transition newTransition) {
Transition existingTransition = transitions.get(event);
if(existingTransition != null){
if(existingTransition.equals(newTransition)){
throw new StateMachineException(existingTransition+" already Exist, you can not add another one");
}
}
}
最后用层级展示下源码的设计:存储下来类似树形分支
第一维度:状态
第二维度:原状态 对应 事件
第三维度:事件对应具体流转