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

 // 创建对外接口对象
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. [Alpha阶段]第八次Scrum Meeting

    Scrum Meeting博客目录 [Alpha阶段]第八次Scrum Meeting 基本信息 名称 时间 地点 时长 第八次Scrum Meeting 19/04/12 新主楼F座2楼 35min ...

  2. Google Chrome即将开始警告—停止支持Flash Player

    Adobe 计划在 2020 年让 Flash Player 彻底退休,整个科技行业都在为这个关键时刻做准备,包括浏览器开发机构,Google 作为最主要的一员,试图尽可能顺利地完成 Flash Pl ...

  3. Python发送邮件脚本

    import smtplib from email.mime.text import MIMEText mailserver = "smtp.163.com" username_s ...

  4. pytorch识别CIFAR10:训练ResNet-34(微调网络,准确率提升到85%)

    版权声明:本文为博主原创文章,欢迎转载,并请注明出处.联系方式:460356155@qq.com 在前一篇中的ResNet-34残差网络,经过训练准确率只达到80%. 这里对网络做点小修改,在最开始的 ...

  5. Django(九)上:ModelForm操作

    一.内容回顾 Model - 数据库操作 - 验证 class A(MOdel): user = email = pwd = Form - class LoginForm(Form): email = ...

  6. Activiti6-FormService(学习笔记)重要

    设置流程定义文件: <?xml version="1.0" encoding="UTF-8"?> <definitions xmlns=&qu ...

  7. [ffmpeg] 滤波

    ffmpeg中有很多已经实现好的滤波器,这些滤波器的实现位于libavfilter目录之下,用户需要进行滤波时,就是是调用这些滤波器来实现的.ffmpeg对于调用滤波器有一整套的调用机制. 基本结构 ...

  8. EditTextUtil 监听输入字数

    package com.toge.ta.utils; import android.text.Editable;import android.text.Selection;import android ...

  9. [模板]fhqTreap

    用途 平衡树(可实现区间翻转) 原理 和treap一样,中序遍历表示权值的顺序,并且每个点有一个随机的附加值,形成一个堆来保证复杂度 但是不旋转,所有操作通过split和merge实现 分为两种spl ...

  10. nuxt npm run dev 报错Solution to the "Error: listen EADDRINUSE 127.0.0.1:8080"

    Solution to the "Error: listen EADDRINUSE 127.0.0.1:8080" Hello, Just sharing a solution t ...