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

 // 创建对外接口对象
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,内部流转也清晰明了,还是很值得我们学习的。

mina statemachine解读(二)的更多相关文章

  1. mina statemachine解读(一)

      statemachine(状态机)在维护多状态数据时有非常好的作用,现在github上star排名最前的是squirrel-foundation以及spring-statemachine,而min ...

  2. jQuery.Callbacks 源码解读二

    一.参数标记 /* * once: 确保回调列表仅只fire一次 * unique: 在执行add操作中,确保回调列表中不存在重复的回调 * stopOnFalse: 当执行回调返回值为false,则 ...

  3. java多线程解读二(内存篇)

    线程的内存结构图 一.主内存与工作内存 1.Java内存模型的主要目标是定义程序中各个变量的访问规则.此处的变量与Java编程时所说的变量不一样,指包括了实例字段.静态字段和构成数组对象的元素,但是不 ...

  4. mybatis源码解读(二)——构建Configuration对象

    Configuration 对象保存了所有mybatis的配置信息,主要包括: ①. mybatis-configuration.xml 基础配置文件 ②. mapper.xml 映射器配置文件 1. ...

  5. java8完全解读二

    继续着上次的java完全解读一 继续着上次的java完全解读一1.强大的Stream API1.1什么是Stream1.2 Stream操作的三大步骤1.2.1 创建Stream1.2.2 Strea ...

  6. (转)go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin

    转自:http://www.baiyuxiong.com/?p=886 ---------------------------------------------------------------- ...

  7. Mina使用总结(二)Handler

    Handler的基本作用,处理接收到的客户端信息 一个简单的Handler实现如下: package com.bypay.mina.handler; import java.util.Date; im ...

  8. cJONS序列化工具解读二(数据解析)

    cJSON数据解析 关于数据解析部分,其实这个解析就是个自动机,通过递归或者解析栈进行实现数据的解析 /* Utility to jump whitespace and cr/lf *///用于跳过a ...

  9. NSObject头文件解析 / 消息机制 / Runtime解读 (二)

    本章接着NSObject头文件解析 / 消息机制 / Runtime解读(一)写 给类添加属性: BOOL class_addProperty(Class cls, const char *name, ...

随机推荐

  1. Web 项目系列之浏览器机制(一)

    目录: ——初步认识浏览器 ——浏览器的渲染机制   ——浏览器的缓存机制 正文: 初步认识浏览器 想来任何一位读者,对浏览器都不会陌生.除开IT相关人员常用的Chrome(谷歌,Google).Fi ...

  2. C语言之将弧度值转换为角度值

    #include<stdio.h> #include<stdlib.h> #define pi 3.141592 void conver_radian(float radian ...

  3. 团队开发项目--NABCD模型

    N(need)需求: 鉴于在学校中的大部分爱学习的学生平时都去拍空教室的占有情况,我们发现有的时候太多,导致同学们们拍照会浪费很长的时间,而且空教室的显示不是一下子全出来,有的时候还会出现无法显示的情 ...

  4. Android中自定义IP控件

    最近在搞Android项目,之前并没有系统的去学过这方面的编程,只能边看书边撸代码.在项目的开发的过程中,需要一个IP控件,后面了解到Android中并没有这样的控件,于是网上搜索,发现得到的结果并不 ...

  5. 使用eclipse启动tomcat里的项目时报错:java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener

    1.这种错:java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener刚开始看的时候 ...

  6. Acitiviti数据库表设计(学习笔记)

    ACT_ID_*:与权限,用户与用户组,以及用户与用户组关系相关的表 ACT_RU_*:代表了流程引擎运行时的库表,RU表示Runtime ACT_HI_*:HI表示History当流程完成了节点以后 ...

  7. syntax error near unexpected token `$'\r''

    这种情况发生的原因是因为你所处理的文件换行符是dos格式的"\r\n"可以使用cat -v 文件名 来查看换行符是否是,如果是上述的,则行结尾会是^m需要转换成linux/unix ...

  8. Excel提取字符串示例

    1.提取两个字符中间的字

  9. LODOP打印当前日期时间的方法

    JS方法直接获取.之前有个详细介绍的博文:LODOP打印用JS获取的当前日期本文也再演示一下,详细介绍见上面链接的博文,该方法此文不做详细介绍. 本文有三段:1.JS获取日期,2,.LODOP的FOR ...

  10. CSS绝对定位元素居中的几种方法

    转载自-CSS居中绝对https://www.cnblogs.com/skura23/p/6530556.html 作者:PajamaCat 1,div宽度未知1 <body> <d ...