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,内部流转也清晰明了,还是很值得我们学习的。

posted @ 2019-03-10 19:17  黑面馒头  阅读(348)  评论(0编辑  收藏  举报