mina statemachine解读(一)
statemachine(状态机)在维护多状态数据时有非常好的作用,现在github上star排名最前的是squirrel-foundation以及spring-statemachine,而mina的statemachine好像并没有对外提供,多用于mina的高级特性里面。
了解了下spring-statemachine,提供特别完善的扩展方式,interceptor,listener,甚至支持分布式,但是上手使用还是有一定的难度,代码也比较复杂,状态机的实例比较重。没有看到较好的现实应用实例,如对一个任务的管理可能是需要根据ID从数据库中获取状态再根据当前状态,事件去决定transition,看到spring中是使用PersistStateMachineHandler来处理类似的情况,需要停止状态机,重置状态机为相应状态再触发,感觉不是很优雅,也可能是我看的不够深入没有理解其精髓,如果有别的实现方式欢迎留言告知。
squirrel-foundation相比较spring上手就容易很多了,有很完善的帮助示例,应用也比较符合对状态机的认知,同时也提供了完善的linstener等支持,是比较好的状态机选择。
这里主要介绍mina statemachine,相比前两个可以说寥寥无名,但是在用起来的时候还是很爽的,比较符合现实业务对状态机的要求。核心代码也就几百行,实现逻辑足够清晰,看完源码半个小时就够了,也可以根据自己的业务需求进行修改定制。
mina statemachine的guide:http://mina.apache.org/mina-project/userguide/ch14-state-machine/ch14-state-machine.html,有对应的maven gav。
先上示例吧
/** * 任务实体 * @author 鱼蛮 on 2019/2/23 **/ @Getter @Setter public class Task { /**任务ID*/ private Integer id; /**任务名称*/ private String name; /**任务状态*/ private String state; } /** * @author 鱼蛮 on 2019/2/23 **/ public interface TaskWork { /** * 任务领取 * @param taskId * @param userName */ void take(Integer taskId, String userName); /** * 任务提交 * @param taskId */ void submit(Integer taskId); /** * 任务审核 * @param taskId * @param auditor */ void audit(Integer taskId, String auditor); } /** * @author 鱼蛮 on 2019/2/23 **/ @Slf4j public class TaskHandler { @State public static final String CREATED = "Created"; @State public static final String TOOK = "Took"; @State public static final String SUBMITTED = "Submitted"; @State public static final String AUDITED = "Audited"; @Transition(on = "take", in = CREATED, next = TOOK) public void takeTask(StateContext context, String userName) { Task task = (Task)context.getAttribute("task"); log.info("use:{},take task, taskId:{}", userName, task.getId()); } @Transition(on = "submit", in = {TOOK}, next = SUBMITTED) public void submitTask(StateContext context) { Task task = (Task)context.getAttribute("task"); log.info("taskId:{}, submitted", task.getId()); } @Transition(on = "audit", in = SUBMITTED, next = AUDITED) public void auditTask(StateContext context, String auditor) { Task task = (Task)context.getAttribute("task"); log.info("auditor:{}, audit task {}", auditor, task.getId()); } } /** * @author 鱼蛮 on 2019/2/23 **/ public class TaskSmTest { public static void main(String[] args) { // 新建handler TaskHandler taskHandler = new TaskHandler(); // 构建状态机 StateMachine sm = StateMachineFactory.getInstance(Transition.class).create(TaskHandler.CREATED, taskHandler); // 创建对外接口对象 TaskWork taskWork = new StateMachineProxyBuilder().setStateContextLookup(new StateContextLookup() { @Override public StateContext lookup(Object[] objects) { Integer taskId = (Integer)objects[0]; // 这里应该是根据Id去数据库查询 Task task = new Task(); task.setId(taskId); StateContext context = new DefaultStateContext(); if (taskId == 123) { task.setState(TaskHandler.CREATED); } else if (taskId == 124) { task.setState(TaskHandler.TOOK); } else if (taskId == 125) { task.setState(TaskHandler.SUBMITTED); } context.setCurrentState(sm.getState(task.getState())); context.setAttribute("task", task); return context; } }).create(TaskWork.class, sm); taskWork.take(123, "Jack"); taskWork.submit(124); taskWork.audit(125, "Andy"); StateContext context = new DefaultStateContext(); context.setCurrentState(sm.getState(TaskHandler.CREATED)); context.setAttribute("task", new Task()); Event event = new Event("take", context, new Object[]{123, "Jack"}); sm.handle(event);
taskWork.submit(123);
} }
输出结果为:
2019-02-23 15:50:54,570 INFO [main] (TaskHandler.java:22) - use:Jack,take task, taskId:123 2019-02-23 15:50:54,574 INFO [main] (TaskHandler.java:28) - taskId:124, submitted 2019-02-23 15:50:54,574 INFO [main] (TaskHandler.java:34) - auditor:Andy, audit task 125 2019-02-23 15:50:54,575 INFO [main] (TaskHandler.java:22) - use:Jack,take task, taskId:null Exception in thread "main" org.apache.mina.statemachine.event.UnhandledEventException: Unhandled event: Event[id=submit,context=StateContext[currentState=State[id=Created],attributes={task=com.blackbread.statemachine.mina.Task@4f8e5cde}],arguments={123}] at org.apache.mina.statemachine.StateMachine.handle(StateMachine.java:275) at org.apache.mina.statemachine.StateMachine.processEvents(StateMachine.java:170) at org.apache.mina.statemachine.StateMachine.handle(StateMachine.java:158) at org.apache.mina.statemachine.StateMachineProxyBuilder$MethodInvocationHandler.invoke(StateMachineProxyBuilder.java:261) at com.sun.proxy.$Proxy5.submit(Unknown Source) at com.blackbread.statemachine.mina.TaskSmTest.main(TaskSmTest.java:55)
来分析下状态机的创建以及执行流程,首先看这一行是状态机的创建,初始化
StateMachine sm = StateMachineFactory.getInstance(Transition.class).create(TaskHandler.CREATED, taskHandler);
getInstance方法中获取传入的transitionAnnotation指定的TransitionAnnotation.class,以此作为查找trastition的注解,同时创建StateMachineFactory对象
create方法执行StateMachinne的创建
public StateMachine create(String start, Object handler, Object... handlers) { Map<String, State> states = new HashMap<>(); List<Object> handlersList = new ArrayList<>(1 + handlers.length); handlersList.add(handler); handlersList.addAll(Arrays.asList(handlers));
// 从handler中获取带State注解的状态集合,这里必须是String类型的,如果想用其他类型的需要自己修改源码加以支持 LinkedList<Field> fields = new LinkedList<>(); for (Object h : handlersList) { fields.addAll(getFields(h instanceof Class ? (Class<?>) h : h.getClass())); }
// 根据field创建State对象 for (State state : createStates(fields)) { states.put(state.getId(), state); } if (!states.containsKey(start)) { throw new StateMachineCreationException("Start state '" + start + "' not found."); } // 执行transition与State的绑定 setupTransitions(transitionAnnotation, transitionsAnnotation, entrySelfTransitionsAnnotation, exitSelfTransitionsAnnotation, states, handlersList); return new StateMachine(states.values(), start); }
private static void setupTransitions(Class<? extends Annotation> transitionAnnotation, Class<? extends Annotation> transitionsAnnotation, Class<? extends Annotation> onEntrySelfTransitionAnnotation, Class<? extends Annotation> onExitSelfTransitionAnnotation, Map<String, State> states, Object handler) { Method[] methods = handler.getClass().getDeclaredMethods(); Arrays.sort(methods, new Comparator<Method>() { @Override public int compare(Method m1, Method m2) { return m1.toString().compareTo(m2.toString()); } }); for (Method m : methods) {
// 做State与OnEntry,OnExit注解标注的方法进行绑定,在进入transition以及退出时候调用 setupSelfTransitions(m, onEntrySelfTransitionAnnotation, onExitSelfTransitionAnnotation, states, handler); List<TransitionWrapper> transitionAnnotations = new ArrayList<>(); // 这里是找带有指定的transitionAnnotation注解的方法,如果是将其包装成TransitionWrapper进行保存,在下一步处理中好获取相应数据 if (m.isAnnotationPresent(transitionAnnotation)) { transitionAnnotations.add(new TransitionWrapper(transitionAnnotation, m .getAnnotation(transitionAnnotation))); } // 处理多个注解的 if (m.isAnnotationPresent(transitionsAnnotation)) { transitionAnnotations.addAll(Arrays.asList(new TransitionsWrapper(transitionAnnotation, transitionsAnnotation, m.getAnnotation(transitionsAnnotation)).value())); } if (transitionAnnotations.isEmpty()) { continue; } for (TransitionWrapper annotation : transitionAnnotations) { Object[] eventIds = annotation.on(); if (eventIds.length == 0) { throw new StateMachineCreationException("Error encountered when processing method " + m + ". No event ids specified."); } if (annotation.in().length == 0) { throw new StateMachineCreationException("Error encountered when processing method " + m + ". No states specified."); } State next = null; if (!annotation.next().equals(Transition.SELF)) { next = states.get(annotation.next()); if (next == null) { throw new StateMachineCreationException("Error encountered when processing method " + m + ". Unknown next state: " + annotation.next() + "."); } } for (Object event : eventIds) { if (event == null) { event = Event.WILDCARD_EVENT_ID; } if (!(event instanceof String)) { event = event.toString(); } for (String in : annotation.in()) { State state = states.get(in); if (state == null) { throw new StateMachineCreationException("Error encountered when processing method " + m + ". Unknown state: " + in + "."); } // 这里就是执行State与Transition的绑定,定义了如下的关系:state执行了event,使用什么样的transition进行处理 state.addTransition(new MethodTransition(event, next, m, handler), annotation.weight()); } } } } }
这段代码就是创建状态机的核心代码了,其实主要了就是解析State状态集合,解析Transition标签,做State与Event,Transition的绑定,状态机在使用的时候其实就是先获取State然后获取State上绑定的Transition执行传入的Event。
这里先到状态机的创建吧,再写一个状态机的内部执行流程。