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,内部流转也清晰明了,还是很值得我们学习的。
mina statemachine解读(二)的更多相关文章
- mina statemachine解读(一)
statemachine(状态机)在维护多状态数据时有非常好的作用,现在github上star排名最前的是squirrel-foundation以及spring-statemachine,而min ...
- jQuery.Callbacks 源码解读二
一.参数标记 /* * once: 确保回调列表仅只fire一次 * unique: 在执行add操作中,确保回调列表中不存在重复的回调 * stopOnFalse: 当执行回调返回值为false,则 ...
- java多线程解读二(内存篇)
线程的内存结构图 一.主内存与工作内存 1.Java内存模型的主要目标是定义程序中各个变量的访问规则.此处的变量与Java编程时所说的变量不一样,指包括了实例字段.静态字段和构成数组对象的元素,但是不 ...
- mybatis源码解读(二)——构建Configuration对象
Configuration 对象保存了所有mybatis的配置信息,主要包括: ①. mybatis-configuration.xml 基础配置文件 ②. mapper.xml 映射器配置文件 1. ...
- java8完全解读二
继续着上次的java完全解读一 继续着上次的java完全解读一1.强大的Stream API1.1什么是Stream1.2 Stream操作的三大步骤1.2.1 创建Stream1.2.2 Strea ...
- (转)go语言nsq源码解读二 nsqlookupd、nsqd与nsqadmin
转自:http://www.baiyuxiong.com/?p=886 ---------------------------------------------------------------- ...
- Mina使用总结(二)Handler
Handler的基本作用,处理接收到的客户端信息 一个简单的Handler实现如下: package com.bypay.mina.handler; import java.util.Date; im ...
- cJONS序列化工具解读二(数据解析)
cJSON数据解析 关于数据解析部分,其实这个解析就是个自动机,通过递归或者解析栈进行实现数据的解析 /* Utility to jump whitespace and cr/lf *///用于跳过a ...
- NSObject头文件解析 / 消息机制 / Runtime解读 (二)
本章接着NSObject头文件解析 / 消息机制 / Runtime解读(一)写 给类添加属性: BOOL class_addProperty(Class cls, const char *name, ...
随机推荐
- MyCP
一.作业要求 编写MyCP.java 实现类似Linux下cp XXX1 XXX2的功能,要求MyCP支持两个参数:- java MyCP -tx XXX1.txt XXX2.bin 用来把文本文 ...
- c++ primer plus 第二章 \n与endl在输出上的区别
在书上看到如下一段话: 一个差别是,endl确保程序继续运行前刷新输出(将其立即显示在屏幕上):而使用"\n"不能提供这样的保证,这意味着在有些系统中,有时可能在您 ...
- Activiti开发案例之activiti-app工作流导出图片
前言 自从 Activiti 和 JBPM4 分家以后,Activiti 目前已经发展到了版本7,本着稳定性原则我们最终选择了6,之前还有一个版本5. 问题 在开发使用的过程中发现 Activiti ...
- day11(函数参数,函数对象,打散机制,函数嵌套调用)
一,复习 # 什么是函数:具体特定功能的代码块 - 特定功能代码块作为一个整体,并给该整体命名,就是函数 # 函数的优点: # 1.减少代码的冗余 # 2.结构清晰,可读性强 # 3.具有复用性,开发 ...
- Spark报错
1. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at com.mysql.jdb ...
- Module build failed: Error: Cannot find module 'babel-runtime/core-js/get-it
npm i babel-loader@7.1.5 -D
- 红米Note 4X详细刷成开发版开启ROOT超级权限的教程
小米的手机不同手机型号正常情况下官方网站都提供两个不同的版本,大概分为稳定版和开发版,稳定版没有提供Root超级权限管理,开发版中就开启了Root超级权限,很多情况我们需要使用的一些功能强大的app, ...
- Linux 系统从入门到精通的学习大纲;
以前没有接触过Linux,生产环境需要,有时候遇到问题,百度一下,问题解决了,在遇到问题,在百度,有时候问题是如何解决的,为什么会解决有点丈二的和尚摸不着头脑, 为此,想用一段时间,系统的学习下Lin ...
- docker内安装php缺少的扩展mysql.so和mysqli.so
首先找到php.ini,放开扩展: 打开php.ini 去掉前面的分号,因为是linux环境所以扩展改为.so文件 进入容器内docker安装扩展的目录: ./docker-php-ext-insta ...
- 洛谷 P5110 块速递推
题目大意: 给定一个数列a满足递推式 \(An=233*an-1+666*an-2,a0=0,a1=1\) 求这个数列第n项模\(10^9+7\)的值,一共有T组询问 \(T<=10^7\) \ ...