• 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。

    这里先到状态机的创建吧,再写一个状态机的内部执行流程。

  • 相关阅读:
    celery的使用
    MySQL的性能分析explain
    字典排序
    spark学习进度6——Scala中的List
    spark学习进度3——Scala方法与函数
    spark学习进度5——Scala中的数组
    spark学习进度4——Scala中的元组
    spark学习进度7——Scala中的Map集合
    我对vue3的理解
    vue2自定义指令加载指令vloading和占位图指令vshowimg
  • 原文地址:https://www.cnblogs.com/lcxdever/p/10422917.html
Copyright © 2020-2023  润新知