最近对Activiti做了一些深入的研究,对Activiti的流程机制有了些理解,对动态调整流程也有了一些实践方法。

现在好好总结一下,一来是对这段时间自己辛苦探索的一个记录,二来也是为后来者指指路~~~

如下内容准备采用QA的方式写,很多问题都是当初自己极疑惑的问题,希望能为大家解惑!

Q:可以动态调整流程吗?

A:可以!可以动态更改流程指向,或者创建新的节点,等等。。。

Q: 更改流程还需要注意什么?

A: 必须要实现持久化!否则一旦应用重启,你的流程就犯糊涂了!譬如,你创建了一个新节点,但由于没有持久化,重启之后流程引擎找不到那个新节点了。。。

Q: 如何做到优雅?

A: 除了持久化之外,还记住尽量不要因为临时调整直接更改现有活动(没准这个活动后面还要照常使用呢!),这种情况可以考虑克隆。第三,不要直接操作数据库,或者SqlSession,记住自己写Command!参见我前面的另外一篇文章。如下代码示出执行某个activity后续流程的Cmd:

public class CreateAndTakeTransitionCmd implements Command<java.lang.Void>
{private ActivityImpl _activity;private String _executionId;public CreateAndTakeTransitionCmd(String executionId, ActivityImpl activity){_executionId = executionId;_activity = activity;}@Overridepublic Void execute(CommandContext commandContext){Logger.getLogger(TaskFlowControlService.class).debug(String.format("executing activity: %s", _activity.getId()));ExecutionEntity execution = commandContext.getExecutionEntityManager().findExecutionById(_executionId);execution.setActivity(_activity);execution.performOperation(AtomicOperation.TRANSITION_CREATE_SCOPE);return null;}
}

Q: 如何新建一个活动?

A: 新建活动可以调用processDefinition.createActivity(newActivityId),我们往往可以以某个活动对象为模板来克隆一个新的活动,克隆的方法是分别拷贝各个字段的值:

  protected ActivityImpl cloneActivity(ProcessDefinitionEntity processDefinition, ActivityImpl prototypeActivity,String newActivityId, String... fieldNames){ActivityImpl clone = processDefinition.createActivity(newActivityId);CloneUtils.copyFields(prototypeActivity, clone, fieldNames);return clone;}

拷贝字段的代码如下:

import org.apache.commons.lang.reflect.FieldUtils;
import org.apache.log4j.Logger;
import org.junit.Assert;public abstract class CloneUtils
{public static void copyFields(Object source, Object target, String... fieldNames){Assert.assertNotNull(source);Assert.assertNotNull(target);Assert.assertSame(source.getClass(), target.getClass());for (String fieldName : fieldNames){try{Field field = FieldUtils.getField(source.getClass(), fieldName, true);field.setAccessible(true);field.set(target, field.get(source));}catch (Exception e){Logger.getLogger(CloneUtils.class).warn(e.getMessage());}}}
}

一个示例的用法是:

       ActivityImpl clone = cloneActivity(processDefinition, prototypeActivity, cloneActivityId, "executionListeners","properties");

这个语句的意思是克隆prototypeActivity对象的executionListeners和properties字段。

Q: 如何实现新建活动的持久化?

A: 一个办法是将新建活动的类型、活动ID(activityId)、incomingTransitions、outgoingTransitions等信息保存起来,然后在ProcessEngine启动的时候,在ProcessDefinition中注册这些活动。

但还有一种更好的办法,即只持久化“活动工厂”的信息。譬如,我们根据step2活动创建一个step21活动,所有的信息都一样,这个时候只要持久化工厂类型(活动克隆)、模板活动ID(step2)、新活动ID(step21),这种方法是极其节省空间的,而且简化了代码。比较复杂的例子,是将某个活动分裂成N个串行的会签活动,这种情况只需要记录模板活动ID、新活动ID数组就可以了,不需要记录更多的信息。如下示出一个创建N个用户任务活动的例子:

public class ChainedActivitiesCreator extends RuntimeActivityCreatorSupport implements RuntimeActivityCreator
{@Overridepublic ActivityImpl[] createActivities(ProcessEngine processEngine, ProcessDefinitionEntity processDefinition,RuntimeActivityDefinition info){info.setFactoryName(ChainedActivitiesCreator.class.getName());if (info.getCloneActivityIds() == null){info.setCloneActivityIds(CollectionUtils.arrayToList(new String[info.getAssignees().size()]));}return createActivities(processEngine, processDefinition, info.getProcessInstanceId(),info.getPrototypeActivityId(), info.getNextActivityId(), info.getAssignees(), info.getCloneActivityIds());}private ActivityImpl[] createActivities(ProcessEngine processEngine, ProcessDefinitionEntity processDefinition,String processInstanceId, String prototypeActivityId, String nextActivityId, List<String> assignees,List<String> activityIds){ActivityImpl prototypeActivity = ProcessDefinitionUtils.getActivity(processEngine, processDefinition.getId(),prototypeActivityId);List<ActivityImpl> activities = new ArrayList<ActivityImpl>();for (int i = 0; i < assignees.size(); i++){if (activityIds.get(i) == null){String activityId = createUniqueActivityId(processInstanceId, prototypeActivityId);activityIds.set(i, activityId);}ActivityImpl clone = createActivity(processEngine, processDefinition, prototypeActivity,activityIds.get(i), assignees.get(i));activities.add(clone);}ActivityImpl nextActivity = ProcessDefinitionUtils.getActivity(processEngine, processDefinition.getId(),nextActivityId);createActivityChain(activities, nextActivity);return activities.toArray(new ActivityImpl[0]);}
}

这里,RuntimeActivityDefinition代表一个工厂信息,为了方便,不同工厂的个性化信息存成了一个JSON字符串,并会在加载的时候解析成一个Map:

public class RuntimeActivityDefinition
{String _factoryName;String _processDefinitionId;String _processInstanceId;Map<String, Object> _properties = new HashMap<String, Object>();String _propertiesText;public void deserializeProperties() throws IOException{ObjectMapper objectMapper = new ObjectMapper();_properties = objectMapper.readValue(_propertiesText, Map.class);}public List<String> getAssignees(){return getProperty("assignees");}public String getCloneActivityId(){return getProperty("cloneActivityId");}//...
}

一个节点分裂的工厂属性:

{"sequential":true,"assignees":["bluejoe","alex"],"cloneActivityId":"2520001:step2:1419823449424-8","prototypeActivityId":"step2"}

转载于:https://www.cnblogs.com/bluejoe/p/5115888.html

优雅的实现Activiti动态调整流程(自由跳转、前进、后退、分裂、前加签、后加签等),含范例代码!...相关推荐

  1. 在 Web 级集群中动态调整 Pod 资源限制

    作者 阿里云容器平台技术专家 王程 阿里云容器平台技术专家 张晓宇(衷源) 引子 不知道大家有没有过这样的经历,当我们拥有了一套 Kubernetes 集群,然后开始部署应用的时候,我们应该给容器分配 ...

  2. activiti动态办理人_Activiti动态设置办理人扩展

    Activiti动态设置办理人扩展 作者:邓家海 扩展是要求对Activiti基础有一定的功底的 我们一直在努力,不是为了改变世界,只是不让世界去改变我们. 关键词:Assignee.Candidat ...

  3. Activiti如何实现流程的回退

    1.概述 流程回退一直以来是个老旧的难题,也一直没有好的解决方法,本文就来详述流程回退的解决办法.首先我们来分析一下不同的流程审批情况,并在对应的节点上实现流程的回退处理,以及应该提供的回退处理,当然 ...

  4. 动态调整线程池_调整线程池的重要性

    动态调整线程池 无论您是否知道,您的Java Web应用程序很可能都使用线程池来处理传入的请求. 这是许多人忽略的实现细节,但是迟早您需要了解如何使用该池以及如何为您的应用程序正确调整池. 本文旨在说 ...

  5. 日志级别动态调整——小工具解决大问题

    随着外卖业务的快速发展,业务复杂度不断增加,线上系统环境有任何细小波动,对整个外卖业务都可能产生巨大的影响,甚至形成灾难性的雪崩效应,造成巨大的经济损失.每一次客诉.系统抖动等都是对技术人员的重大考验 ...

  6. java 动态线程池_线程池的参数动态调整

    经典面试题 这次的文章还是绕回了我写的第三篇原创文章<有的线程它死了,于是它变成一道面试题>中留下的几个问题: 哎,兜兜转转,走走停停.天道好轮回,苍天饶过谁? 在这篇文章中我主要回答上面 ...

  7. 动态调整web主题(3): 基于tailwindcss插件的主题色生成方案‘

    动态调整web主题(3): 基于tailwindcss插件的主题色生成方案 动态调整web主题(3): 基于tailwindcss插件的主题色生成方案 前言 tailwind-css-variable ...

  8. 论文笔记:基于透射率修正的湍流模型与动态调整retinex的水下图像增强

    基于透射率修正的湍流模型与动态调整retinex的水下图像增强 先验知识 lab空间 瑞利分布 维纳滤波 导向滤波 本文流程 匀光预处理 改进的湍流模型 retinex水下图像增强 实验结果 论文地址 ...

  9. 【并发编程】线程池参数设置与动态调整

    看了美团的一篇技术文章后才知道原来线程池的参数还可以动态调节. 一.场景分析 1.1 一个线程池中的线程异常了,那么线程池会怎么处理这个线程? public class ThreadPoolExecu ...

  10. 动态调整线程池参数实践

    欢迎大家关注我的微信公众号[老周聊架构],Java后端主流技术栈的原理.源码分析.架构以及各种互联网高并发.高性能.高可用的解决方案. 一.线程池遇到的挑战 我们上一篇 <一文读懂线程池的实现原 ...

最新文章

  1. vim无法打中文_vim+ctags+cscope+Taglist+Nerdtree打造成sourceinsight
  2. Codeforces Round #354 (Div. 2)-A
  3. mysql mongodb qps_极高的QPS - DynamoDB与MongoDB相比其他noSQL?
  4. php try catch
  5. Pandas数据分析常用数据操作(3年总结)
  6. 在东岸听刘元演奏萨克斯
  7. maven简单工具命令
  8. 欢迎大家推荐使用小技巧
  9. win10和win7鲁大师测试软件,实测:老电脑用Win7、Win10哪个流畅?
  10. java安装后在哪里打开_java安装后怎么打开教程
  11. 来拥抱星辰大海吧!中国风云气象卫星系列数字藏品荣耀首发
  12. 计算机与操作系统基础-计算机发展史
  13. 知识蒸馏论文翻译(9)—— Multi-level Knowledge Distillation via Knowledge Alignment and Correlation
  14. 银行卡号每输四位加空格,及银行卡的识别(此银行卡号是那个银行)
  15. css实现六边形及其它灵活布局
  16. 【支付FM】个人免签-微信支付宝云闪付 个人二维码收款即时到账
  17. 中国电脑黑白棋软件八强
  18. CSDN周刊:AI补代码神器问世,百度网盘克星诞生,Google 已经取消中国搜索引擎项目!
  19. Adobe Audition (AU)安装教程(附Adobe Audition下载地址)
  20. 决战下半场,超级APP的商业思考

热门文章

  1. 手把手教你如何加入到github的开源世界!
  2. Android两种 旋转Bitmap方法
  3. Apache 跟踪用户会话
  4. 详解由VS 2010生成的Bug报告(2) - 报告的内容
  5. iOS开发-停止WebView播放视频/音频
  6. 记录 PHP 缓存区ob
  7. 《HTML5 2D游戏编程核心技术》——第3章,第3.8节绘制动画帧
  8. django之创建第11个项目-页面整合
  9. Redis集群功能配置以及Redis Sentinel配置
  10. AEAI Miscdp文件上传功能使用心得