ACTIVITI 是一个优秀开源软件,通过阅读源码,我们不但可以了解工作流引擎执行的原理还可以增加个人的编码功力。

ACTIVITI 所有执行过程都是采用命令模式进行执行。

本文主要描述流程引擎数据保存的过程。

流程引擎所有的操作都采用命令模式,使用命令执行器进行执行,命令执行器是一个采用拦截器链式执行模式。

1.命令执行器。

代码为org.activiti.engine.impl.interceptor.CommandExecutor.

命令执行器的构造代码如下:

1.获取拦截器列表。

1.获取客户自定义前置拦截器。

这个需要实现CommandInterceptor接口,并配置到流程定义配置文件中。

2.获取默认的拦截器。

 protected Collection< ? extends CommandInterceptor> getDefaultCommandInterceptors() {
List<CommandInterceptor> interceptors = new ArrayList<CommandInterceptor>();
interceptors.add(new LogInterceptor()); CommandInterceptor transactionInterceptor = createTransactionInterceptor();
if (transactionInterceptor != null) {
interceptors.add(transactionInterceptor);
} interceptors.add(new CommandContextInterceptor(commandContextFactory, this));
return interceptors;
}

这个是获取默认的拦截器。

这里有四个拦截器。

1.LogInterceptor日志拦截器,拦截器打印执行的日志。

2.事务拦截器。

3.CommandContextInterceptor 拦截器。

这个拦截器功能如下:

1.流程定义。

2.注入命令上下文,命令上下文包括数据只有代码。

3.调用命令上下文close方法,执行数据保存。

4.命令执行拦截器,CommandInvoker 这个是拦截器最后的一个,为调用具体的命令。

3.获取后置拦截器。

这个需要实现CommandInterceptor接口,并配置到流程定义配置文件中。

2.构建拦截器链。

  基础拦截器代码:

public abstract class AbstractCommandInterceptor implements CommandInterceptor {

  /** will be initialized by the {@link org.activiti.engine.ProcessEngineConfiguration ProcessEngineConfiguration} */
protected CommandInterceptor next; @Override
public CommandInterceptor getNext() {
return next;
} @Override
public void setNext(CommandInterceptor next) {
this.next = next;
}
}

  拦截器调用setNext方法设置下一个拦截器。

 protected CommandInterceptor initInterceptorChain(List<CommandInterceptor> chain) {
if (chain==null || chain.isEmpty()) {
throw new ActivitiException("invalid command interceptor chain configuration: "+chain);
}
for (int i = 0; i < chain.size()-1; i++) {
chain.get(i).setNext( chain.get(i+1) );
}
return chain.get(0);
}

这里将拦截器列表构建成链的模式。

2.现在介绍一下命令的执行模式顺序。

比如我们设置流程变量,参考代码如下:

RuntimeService setVariable(String executionId, String variableName, Object value);

RuntimeServiceImpl 实现代码:

Map<String, Object> variables = new HashMap<String, Object>();
variables.put(variableName, value);
commandExecutor.execute(new SetExecutionVariablesCmd(executionId, variables, false));

这个commandExecutor到底是如何注入的。

我们可以看到这个RuntimeServiceImpl 扩展了类ServiceImpl。

我们在ProcessEngineConfigurationImpl 中看到如下代码:

protected void initServices() {
initService(repositoryService);
initService(runtimeService);
initService(historyService);
initService(identityService);
initService(taskService);
initService(formService);
initService(managementService);
} protected void initService(Object service) {
if (service instanceof ServiceImpl) {
((ServiceImpl)service).setCommandExecutor(commandExecutor);
}
}

这里注入了commandExecutor。

执行顺序为:

1.执行前置拦截器,如果存在。

2.日志执行。

public class LogInterceptor extends AbstractCommandInterceptor {

  private static Logger log = LoggerFactory.getLogger(LogInterceptor.class);

  public <T> T execute(CommandConfig config, Command<T> command) {
if (!log.isDebugEnabled()) {
// do nothing here if we cannot log
return next.execute(config, command);
}
log.debug(" ");
log.debug("--- starting {} --------------------------------------------------------", command.getClass().getSimpleName());
try { return next.execute(config, command); } finally {
log.debug("--- {} finished --------------------------------------------------------", command.getClass().getSimpleName());
log.debug(" ");
}
}
}

参考日志代码记录日志,调用下一个拦截器(next.execute(config, command);),最后记录日志。

3.执行事务。

3.执行CommandContext拦截器。

这个拦截器执行数据库持久化。

try {
// Push on stack
Context.setCommandContext(context);
Context.setProcessEngineConfiguration(processEngineConfiguration); return next.execute(config, command); } catch (Exception e) { context.exception(e); } finally {
try {
if (!contextReused) {
context.close();
}
} finally {
// Pop from stack
Context.removeCommandContext();
Context.removeProcessEngineConfiguration();
}
}

在命令中并不执行数据库持久化,持久化在此拦截器中调用context.close();执行。

4.执行后置拦截器,如果存在。

5.调用命令拦截器执行命令。

ACTIVITI 源码研究之命令模式执行的更多相关文章

  1. Appium Android Bootstrap源码分析之命令解析执行

    通过上一篇文章<Appium Android Bootstrap源码分析之控件AndroidElement>我们知道了Appium从pc端发送过来的命令如果是控件相关的话,最终目标控件在b ...

  2. activiti源码分析学习

    activiti源码分析学习导语 目前项目中用activiti来做工作流的相关工作,最近遇到一些情况下,公司二次开发的流程图渲染出现了问题,会造成流程图出不来的情况.初步分析数据库中记录以及简单的代码 ...

  3. Chrome自带恐龙小游戏的源码研究(七)

    在上一篇<Chrome自带恐龙小游戏的源码研究(六)>中研究了恐龙的跳跃过程,这一篇研究恐龙与障碍物之间的碰撞检测. 碰撞盒子 游戏中采用的是矩形(非旋转矩形)碰撞.这类碰撞优点是计算比较 ...

  4. Chrome自带恐龙小游戏的源码研究(三)

    在上一篇<Chrome自带恐龙小游戏的源码研究(二)>中实现了云朵的绘制和移动,这一篇主要研究如何让游戏实现昼夜交替. 昼夜交替的效果主要是通过样式来完成,但改变样式的时机则由脚本控制. ...

  5. 阿里sentinel源码研究深入

    1. 阿里sentinel源码研究深入 1.1. 前言 昨天已经把sentinel成功部署到线上环境,可参考我上篇博文,该走的坑也都走了一遍,已经可以初步使用它的限流和降级功能,根据我目前的实践,限流 ...

  6. 从源码研究如何不重启Springboot项目实现redis配置动态切换

    上一篇Websocket的续篇暂时还没有动手写,这篇算是插播吧.今天讲讲不重启项目动态切换redis服务. 背景 多个项目或微服务场景下,各个项目都需要配置redis数据源.但是,每当运维搞事时(修改 ...

  7. java io 源码研究记录(一)

    Java IO 源码研究: 一.输入流 1  基类 InputStream 简介: 这是Java中所有输入流的基类,它是一个抽象类,下面我们简单来了解一下它的基本方法和抽象方法. 基本方法: publ ...

  8. maven 下载 源码和javadoc 命令

    摘要:我们在写代码时候,往往是想查看一下源码,看看源码的一些细节内容.一般情况下,在IDE(如eclipse)中近仅仅只需按住ctrl+ 点击对应的方法即可进入对应的源码部分.但是有些时候很多依赖项并 ...

  9. Android开源项目 Universal imageloader 源码研究之Lru算法

    https://github.com/nostra13/Android-Universal-Image-Loader universal imageloader 源码研究之Lru算法 LRU - Le ...

随机推荐

  1. git学习笔记12-标签管理-版本

    发布一个版本时,我们通常先在版本库中打一个标签(tag),这样,就唯一确定了打标签时刻的版本.将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来.所以,标签也是版本库的一个快照 ...

  2. 从POI到O2O 看百度地图如何走出未来之路

    近期O2O的烧钱融资大战如火如荼,有人已经把O2O大战,用乌合之众的群体心理失控来形容.其实厂商都不傻,O2O烧钱大家都知道,但是大家还知道O2O背后这块大蛋糕价值"万亿级". 有 ...

  3. 【51nod】1376 最长递增子序列的数量

    数组A包含N个整数(可能包含相同的值).设S为A的子序列且S中的元素是递增的,则S为A的递增子序列.如果S的长度是所有递增子序列中最长的,则称S为A的最长递增子序列(LIS).A的LIS可能有很多个. ...

  4. js求和算法研究

    如果遇到一个试题是这样. function sum(arr){ //求和 } 你会怎么做? 反正我第一反应是这样做. function sum(arr){ var result = 0; for(va ...

  5. hdu 5339 Untitled

    这题很明显是签到题,可我比赛时却没做出,赤裸裸的爆零了,真悲剧…… 看了题解后才知道直接暴搜就行,只是需要把它们从大到小排序后再搜,我当时就没想到...不想再多说了 一开始我直接枚举所有情况: #in ...

  6. 网站QQ导航

    <a href="http://wpa.qq.com/msgrd?v=3&uin=[color=Red]361983679[/color]&site=qq&me ...

  7. 转!!java泛型概念(泛型类,接口,方法)

    一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下面这段简短的代码: 1 public class GenericTest { 2 3 public static void main(Stri ...

  8. VB6.0调用DLL

    目录 第1章 VB6.0调用DLL    1 1 VC++编写DLL    1 1.1 使用__stdcall    1 1.2 使用 .DEF 文件    1 2 简单数据类型    2 2.1 传 ...

  9. css3实现超过两行文字,超出用三个点显示(兼容性不行,仅供学习)

    <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title> ...

  10. 《Java程序设计》 实验一 实验报告

    实验一 Java开发环境的熟悉(Windows + IDEA) 实验内容 1.使用JDK编译.运行简单的Java程序: 2.使用Eclipse 编辑.编译.运行.调试Java程序. 实验要求 1.完成 ...