zoukankan      html  css  js  c++  java
  • 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。

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

  • 相关阅读:
    java操作生成jar包 和写入jar包
    jboss配置jndi连接池
    windows 域的LDAP查询相关举例
    LDAP error Code 及解决方法
    HDU 6417
    CF1299D Around the World
    codechef Chef and The Colored Grid
    Educational Codeforces Round 82 (Rated for Div. 2)
    CF1237F Balanced Domino Placements
    CF1254E Send Tree to Charlie
  • 原文地址:https://www.cnblogs.com/lcxdever/p/10422917.html
Copyright © 2011-2022 走看看