zoukankan      html  css  js  c++  java
  • mina statemachine解读(二)

    这里主要讲下对外接口暴露的处理。

     // 创建对外接口对象
            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);

    这里主要看create方法,其实就是通过代理模式创建了一个代理类。

        public Object create(Class<?>[] ifaces, StateMachine sm) {
            ClassLoader cl = defaultCl;
            if (cl == null) {
                cl = Thread.currentThread().getContextClassLoader();
            }
    
            InvocationHandler handler = new MethodInvocationHandler(sm, contextLookup, interceptor, eventFactory,
                    ignoreUnhandledEvents, ignoreStateContextLookupFailure, name);
    
            return Proxy.newProxyInstance(cl, ifaces, handler);
        }
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if ("hashCode".equals(method.getName()) && args == null) {
                    return Integer.valueOf(System.identityHashCode(proxy));
                }
                
                if ("equals".equals(method.getName()) && args.length == 1) {
                    return Boolean.valueOf(proxy == args[0]);
                }
                
                if ("toString".equals(method.getName()) && args == null) {
                    return (name != null ? name : proxy.getClass().getName()) + "@"
                            + Integer.toHexString(System.identityHashCode(proxy));
                }
    
                if (log.isDebugEnabled()) {
                    log.debug("Method invoked: " + method);
                }
    
                args = args == null ? EMPTY_ARGUMENTS : args;
                // 拦截器处理可以对输入参数做一些处理
                if (interceptor != null) {
                    args = interceptor.modify(args);
                }
                // lookup方法去加载context
                StateContext context = contextLookup.lookup(args);
    
                if (context == null) {
                    if (ignoreStateContextLookupFailure) {
                        return null;
                    }
                    
                    throw new IllegalStateException("Cannot determine state context for method invocation: " + method);
                }
                // 事件工厂去创建事件,statematchine其实最终还是由事件去驱动的
                Event event = eventFactory.create(context, method, args);
    
                try {
               // statemachine处理传入时间,触发状态处理
                    sm.handle(event);
                } catch (UnhandledEventException uee) {
                    if (!ignoreUnhandledEvents) {
                        throw uee;
                    }
                }
    
                return null;
            }
        }

    这里可以看到主要是会由StateContextLookup去根据传入参数查找相应的StateContext,然后由EventFactory去创建一个Event,然后stateMachine去处理这个事件来完成状态机的调用,内部状态的轮转,状态机最终还是由事件去驱动的。

    
    
        private void handle(State state, Event event) {
            StateContext context = event.getContext();
            // 获取state上面绑定的transitions 
            for (Transition t : state.getTransitions()) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Trying transition {}", t);
                }
    
                try {
                    // transition实际执行相关的事件
                    if (t.execute(event)) {
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("Transition {} executed successfully.", t);
                        }
                        // 执行成功执行onExits,OnEntries设置相应的状态
                        setCurrentState(context, t.getNextState());
    
                        return;
                    }
                } 
                ... ...
    
            /*
             * No transition could handle the event. Try with the parent state if
             * there is one.
             */
             // 如果没有当前state没有绑定transition,或者没有符合条件的transition,去找parent state,如果还没有就是说明是不支持的,抛出没有处理的Envet异常
            if (state.getParent() != null) {
                handle(state.getParent(), event);
            } else {
                throw new UnhandledEventException(event);
            }
        }

    事件最终的执行还是有transition去执行,这里获取了state上的transition然后去处理event,然后通过执行的结果,以及实际方法处理中抛出的:BreakAndContinueException,BreakAndGotoException,BreakAndCallException,BreakAndReturnException异常来进行流程控制。

    看一下实际的transition处理类,MethodTransition

        public boolean doExecute(Event event) {
            Class<?>[] types = method.getParameterTypes();
    
            if (types.length == 0) {
                invokeMethod(EMPTY_ARGUMENTS);
                
                return true;
            }
            // 如果参数长度大于2+原始参数失败
            if (types.length > 2 + event.getArguments().length) {
                return false;
            }
    
            Object[] args = new Object[types.length];
    
            int i = 0;
            // 如果第一个参数是Event,则将event对象放入参数列表
            if (match(types[i], event, Event.class)) {
                args[i++] = event;
            }
            // 如果第二个参数是StateContext则将context对象放入参数列表
            if (i < args.length && match(types[i], event.getContext(), StateContext.class)) {
                args[i++] = event.getContext();
            }
            
            Object[] eventArgs = event.getArguments();
            // 判定剩余参数类型是否匹配,如果不匹配则执行失败
            for (int j = 0; i < args.length && j < eventArgs.length; j++) {
                if (match(types[i], eventArgs[j], Object.class)) {
                    args[i++] = eventArgs[j];
                }
            }
    
            if (args.length > i) {
                return false;
            }
            // 执行method
            invokeMethod(args);
    
            return true;
        }

    这里主要做了参数的校验与绑定,对实际处理方法中如果加了Event或者是StateContext也把相应的数据塞到参数列表里面,实际执行时候大概率也会用到StateContext。

        private void invokeMethod(Object[] arguments) {
            try {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Executing method " + method + " with arguments " + Arrays.asList(arguments));
                }
                
                method.invoke(target, arguments);
            } catch (InvocationTargetException ite) {
                if (ite.getCause() instanceof RuntimeException) {
                    throw (RuntimeException) ite.getCause();
                }
                
                throw new MethodInvocationException(method, ite);
            } catch (IllegalAccessException iae) {
                throw new MethodInvocationException(method, iae);
            }
        }
    invokeMethod是实际的方法执行类,这里会对Exception区别处理,对RuntimeException直接抛出,这里处理BreakException是不更好点。

    这里整个调用过程也分析完了,可以看到状态机的流转主要是由Event驱动,获取State绑定的transition来执行处理Event,StateMachineProxyBuilder就是用代理的方式提供了方便的对外接口类。
    如果不使用这个也照样可以玩转状态机,如前面这段示例程序:
    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);

    可以看到整个状态机的设计还是很清晰、巧妙的。运用了工厂模式、代理模式等设计模式,对外提供简单易懂的API,内部流转也清晰明了,还是很值得我们学习的。

  • 相关阅读:
    JDK8 直接定义接口中静态方法
    Spring+Netty+WebSocket实例
    基于Maven,Spring+ActiveMQ实现,贴近实际
    Netty+WebSocket简单实现网页聊天
    ActiveMQ简单入门实例
    WCF 的日志配置
    AutoMapper使用笔记
    博客园现代化建设——AutoMapper
    jqplot formatString 日期格式化列表
    MongoDB实战开发 【零基础学习,附完整Asp.net示例】
  • 原文地址:https://www.cnblogs.com/lcxdever/p/10506533.html
Copyright © 2011-2022 走看看