statemachine(状态机)在维护多状态数据时有非常好的作用,现在github上star排名最前的是squirrel-foundation以及spring-statemachine,而mina的statemachine好像并没有对外提供,多用于mina的高级特性里面。

了解了下spring-statemachine,提供特别完善的扩展方式,interceptor,listener,甚至支持分布式,但是上手使用还是有一定的难度,代码也比较复杂,状态机的实例比较重。没有看到较好的现实应用实例,如对一个任务的管理可能是需要根据ID从数据库中获取状态再根据当前状态,事件去决定transition,看到spring中是使用PersistStateMachineHandler来处理类似的情况,需要停止状态机,重置状态机为相应状态再触发,感觉不是很优雅,也可能是我看的不够深入没有理解其精髓,如果有别的实现方式欢迎留言告知。

squirrel-foundation相比较spring上手就容易很多了,有很完善的帮助示例,应用也比较符合对状态机的认知,同时也提供了完善的linstener等支持,是比较好的状态机选择。

这里主要介绍mina statemachine,相比前两个可以说寥寥无名,但是在用起来的时候还是很爽的,比较符合现实业务对状态机的要求。核心代码也就几百行,实现逻辑足够清晰,看完源码半个小时就够了,也可以根据自己的业务需求进行修改定制。

mina statemachine的guide:http://mina.apache.org/mina-project/userguide/ch14-state-machine/ch14-state-machine.html,有对应的maven gav。

先上示例吧

/**
* 任务实体
* @author 鱼蛮 on 2019/2/23
**/
@Getter
@Setter
public class Task {
/**任务ID*/
private Integer id;
/**任务名称*/
private String name;
/**任务状态*/
private String state;
} /**
* @author 鱼蛮 on 2019/2/23
**/
public interface TaskWork {
/**
* 任务领取
* @param taskId
* @param userName
*/
void take(Integer taskId, String userName); /**
* 任务提交
* @param taskId
*/
void submit(Integer taskId); /**
* 任务审核
* @param taskId
* @param auditor
*/
void audit(Integer taskId, String auditor);
} /**
* @author 鱼蛮 on 2019/2/23
**/
@Slf4j
public class TaskHandler {
@State public static final String CREATED = "Created";
@State public static final String TOOK = "Took";
@State public static final String SUBMITTED = "Submitted";
@State public static final String AUDITED = "Audited"; @Transition(on = "take", in = CREATED, next = TOOK)
public void takeTask(StateContext context, String userName) {
Task task = (Task)context.getAttribute("task");
log.info("use:{},take task, taskId:{}", userName, task.getId());
} @Transition(on = "submit", in = {TOOK}, next = SUBMITTED)
public void submitTask(StateContext context) {
Task task = (Task)context.getAttribute("task");
log.info("taskId:{}, submitted", task.getId());
} @Transition(on = "audit", in = SUBMITTED, next = AUDITED)
public void auditTask(StateContext context, String auditor) {
Task task = (Task)context.getAttribute("task");
log.info("auditor:{}, audit task {}", auditor, task.getId());
}
} /**
* @author 鱼蛮 on 2019/2/23
**/
public class TaskSmTest {
public static void main(String[] args) {
// 新建handler
TaskHandler taskHandler = new TaskHandler();
// 构建状态机
StateMachine sm = StateMachineFactory.getInstance(Transition.class).create(TaskHandler.CREATED, taskHandler);
// 创建对外接口对象
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); taskWork.take(123, "Jack");
taskWork.submit(124);
taskWork.audit(125, "Andy"); 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);
     taskWork.submit(123);
} }

输出结果为:

2019-02-23 15:50:54,570  INFO [main] (TaskHandler.java:22) - use:Jack,take task, taskId:123
2019-02-23 15:50:54,574 INFO [main] (TaskHandler.java:28) - taskId:124, submitted
2019-02-23 15:50:54,574 INFO [main] (TaskHandler.java:34) - auditor:Andy, audit task 125
2019-02-23 15:50:54,575 INFO [main] (TaskHandler.java:22) - use:Jack,take task, taskId:null
Exception in thread "main" org.apache.mina.statemachine.event.UnhandledEventException: Unhandled event: Event[id=submit,context=StateContext[currentState=State[id=Created],attributes={task=com.blackbread.statemachine.mina.Task@4f8e5cde}],arguments={123}]
at org.apache.mina.statemachine.StateMachine.handle(StateMachine.java:275)
at org.apache.mina.statemachine.StateMachine.processEvents(StateMachine.java:170)
at org.apache.mina.statemachine.StateMachine.handle(StateMachine.java:158)
at org.apache.mina.statemachine.StateMachineProxyBuilder$MethodInvocationHandler.invoke(StateMachineProxyBuilder.java:261)
at com.sun.proxy.$Proxy5.submit(Unknown Source)
at com.blackbread.statemachine.mina.TaskSmTest.main(TaskSmTest.java:55)

来分析下状态机的创建以及执行流程,首先看这一行是状态机的创建,初始化

        StateMachine sm = StateMachineFactory.getInstance(Transition.class).create(TaskHandler.CREATED, taskHandler);
getInstance方法中获取传入的transitionAnnotation指定的TransitionAnnotation.class,以此作为查找trastition的注解,同时创建StateMachineFactory对象
create方法执行StateMachinne的创建
    public StateMachine create(String start, Object handler, Object... handlers) {

        Map<String, State> states = new HashMap<>();
List<Object> handlersList = new ArrayList<>(1 + handlers.length);
handlersList.add(handler);
handlersList.addAll(Arrays.asList(handlers));      // 从handler中获取带State注解的状态集合,这里必须是String类型的,如果想用其他类型的需要自己修改源码加以支持    
LinkedList<Field> fields = new LinkedList<>();
for (Object h : handlersList) {
fields.addAll(getFields(h instanceof Class ? (Class<?>) h : h.getClass()));
}
     // 根据field创建State对象
for (State state : createStates(fields)) {
states.put(state.getId(), state);
} if (!states.containsKey(start)) {
throw new StateMachineCreationException("Start state '" + start + "' not found.");
}
     // 执行transition与State的绑定
setupTransitions(transitionAnnotation, transitionsAnnotation, entrySelfTransitionsAnnotation,
exitSelfTransitionsAnnotation, states, handlersList); return new StateMachine(states.values(), start);
}
private static void setupTransitions(Class<? extends Annotation> transitionAnnotation,
Class<? extends Annotation> transitionsAnnotation,
Class<? extends Annotation> onEntrySelfTransitionAnnotation,
Class<? extends Annotation> onExitSelfTransitionAnnotation, Map<String, State> states, Object handler) { Method[] methods = handler.getClass().getDeclaredMethods();
Arrays.sort(methods, new Comparator<Method>() {
@Override
public int compare(Method m1, Method m2) {
return m1.toString().compareTo(m2.toString());
}
}); for (Method m : methods) {
       // 做State与OnEntry,OnExit注解标注的方法进行绑定,在进入transition以及退出时候调用
setupSelfTransitions(m, onEntrySelfTransitionAnnotation, onExitSelfTransitionAnnotation, states, handler); List<TransitionWrapper> transitionAnnotations = new ArrayList<>();
// 这里是找带有指定的transitionAnnotation注解的方法,如果是将其包装成TransitionWrapper进行保存,在下一步处理中好获取相应数据
if (m.isAnnotationPresent(transitionAnnotation)) {
transitionAnnotations.add(new TransitionWrapper(transitionAnnotation, m
.getAnnotation(transitionAnnotation)));
}
// 处理多个注解的
if (m.isAnnotationPresent(transitionsAnnotation)) {
transitionAnnotations.addAll(Arrays.asList(new TransitionsWrapper(transitionAnnotation,
transitionsAnnotation, m.getAnnotation(transitionsAnnotation)).value()));
} if (transitionAnnotations.isEmpty()) {
continue;
} for (TransitionWrapper annotation : transitionAnnotations) {
Object[] eventIds = annotation.on(); if (eventIds.length == 0) {
throw new StateMachineCreationException("Error encountered when processing method " + m
+ ". No event ids specified.");
} if (annotation.in().length == 0) {
throw new StateMachineCreationException("Error encountered when processing method " + m
+ ". No states specified.");
} State next = null; if (!annotation.next().equals(Transition.SELF)) {
next = states.get(annotation.next()); if (next == null) {
throw new StateMachineCreationException("Error encountered when processing method " + m
+ ". Unknown next state: " + annotation.next() + ".");
}
} for (Object event : eventIds) {
if (event == null) {
event = Event.WILDCARD_EVENT_ID;
} if (!(event instanceof String)) {
event = event.toString();
} for (String in : annotation.in()) {
State state = states.get(in); if (state == null) {
throw new StateMachineCreationException("Error encountered when processing method "
+ m + ". Unknown state: " + in + ".");
}
              // 这里就是执行State与Transition的绑定,定义了如下的关系:state执行了event,使用什么样的transition进行处理
state.addTransition(new MethodTransition(event, next, m, handler), annotation.weight());
}
}
}
}
}

这段代码就是创建状态机的核心代码了,其实主要了就是解析State状态集合,解析Transition标签,做State与Event,Transition的绑定,状态机在使用的时候其实就是先获取State然后获取State上绑定的Transition执行传入的Event。

这里先到状态机的创建吧,再写一个状态机的内部执行流程。

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

  1. mina statemachine解读(二)

    这里主要讲下对外接口暴露的处理. // 创建对外接口对象 TaskWork taskWork = new StateMachineProxyBuilder().setStateContextLooku ...

  2. Mina源码阅读笔记(一)-整体解读

    今天的这一节,将从整体上对mina的源代码进行把握,网上已经有好多关于mina源码的阅读笔记,但好多都是列举了一下每个接口或者类的方法.我倒是想从mina源码的结构和功能上对这个框架进行剖析.源码的阅 ...

  3. MINA系列学习-mina整体介绍

    今天的这一节,将从整体上对mina的源代码进行把握,网上已经有好多关于mina源码的阅读笔记,但好多都是列举了一下每个接口或者类的方法.我倒是想从mina源码的结构和功能上对这个框架进行剖析.源码的阅 ...

  4. 《Netty5.0架构剖析和源码解读》【PDF】下载

    <Netty5.0架构剖析和源码解读>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062545 内容简介 Netty 是个异步的 ...

  5. JVM源码分析之堆外内存完全解读

    JVM源码分析之堆外内存完全解读   寒泉子 2016-01-15 17:26:16 浏览6837 评论0 阿里技术协会 摘要: 概述 广义的堆外内存 说到堆外内存,那大家肯定想到堆内内存,这也是我们 ...

  6. Mina学习之---mina整体流程介绍

    现在公司使用的NIO框架一直时候Mina,当然这也的框架还有Netty.虽然一直在用,但只是简单的停留在业务层面,最近面试的时候有问Mina相关的东西.在之前的博客中已经对BIO,NIO,AIO这三种 ...

  7. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  8. SDWebImage源码解读 之 NSData+ImageContentType

    第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...

  9. SDWebImage源码解读 之 UIImage+GIF

    第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...

随机推荐

  1. BAT面试题:请使用递归构建N叉树

    题目要求: 现在我们拥有全国的省.市.县.镇的行政信息,比如 浙江省 -> 杭州市 -> 西湖区 --> xx街道,请将这些信息构建成一棵树,根节点为全国,叶子节点为镇. 我的误解: ...

  2. iic接口介绍

    最近遇到一个BUG,跟IIC通信有关,所以借这个机会总结一下IIC总线协议 1.引脚接口介绍 1.A0,A1,A2为24LC64的片选信号,IIC总线最多可以挂载8个IIC接口器件,通过对A0,A1, ...

  3. Django中使用极验Geetest滑动验证码

    一,环境部署 1.创建一个django测试项目 此处省略... 二,文档部署 1.下载安装python对应的SDK 使用命令从Github导入完整项目:git clone https://github ...

  4. 踩坑之mongodb配置文件修改

    一.说明 本文档是在mongodb为3.4下编写的,仅作为参考 配置mongodb有两种方式,一种是通过mongod和mongos两个命令:另外一种方式就是配置文件的方式.因为更容易去管理,所以后者更 ...

  5. 《为大量出现的KPI流快速部署异常检测模型》 笔记

    以下我为这篇<Rapid Deployment of Anomaly Detection Models for Large Number of Emerging KPI Streams>做 ...

  6. Keil MDK5的ITM调试

    https://blog.csdn.net/burgesskzg/article/details/77100453

  7. Vue.js 2.x笔记:路由Vue Router(6)

    1. Vue Router简介与安装 1.1 Vue Router简介 Vue Router 是 Vue.js 官方的路由管理器.它和 Vue.js 的核心深度集成,构建单页面应用. Vue Rout ...

  8. [2019.03.21]LF, CR, CRLF and LFCR(?)

    开玩笑的啦,没有LFCR这种沙雕东西 为什么突然想起来写这个呢,是因为先前照着shell画llehs的时候,总报错,改正了以后又因为看不见而在上一篇博客上没有写明,所以过来好好写一写咯. 可以看出报错 ...

  9. App自动化(2)--Python&Appium实现安卓手机九宫格解锁

    九宫格作为常见的手势密码,我们在使用的时候,是从起点开始,按住不放,然后滑动手指,直到最后一个点松开手指,如果与设置的手势密码匹配,则解锁成功. 现在大多数九宫格作为一个元素存在,很难定位到每一个点. ...

  10. python学习day21 面向对象(三)嵌套/特殊方法

    1.嵌套 类/方法/对象都可以当做变量或嵌套到其他类型中. 函数的参数可以是任意类型. 可哈希(不可变)数据类型可以做字典的key. 类和对象可以做字典的key. 2.特殊方法(8) __init__ ...