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