项目实践之工作流引擎基本文档!Activiti工作流框架中流程引擎API和服务详解
流程引擎的API和服务
- 流程引擎API(ProcessEngine API)是与Activiti打交道的最常用方式
- Activiti从ProcessEngine开始.在ProcessEngine中,可以获得很多包括工作流或者BPM方法的服务
- ProcessEngine和服务类都是线程安全的.可以在整个服务器中仅保持它们的一个引用就可以

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
ManagementService managementService = processEngine.getManagementService();
IdentityService identityService = processEngine.getIdentityService();
HistoryService historyService = processEngine.getHistoryService();
FormService formService = processEngine.getFormService();
ProcessEngines.getDefaultProcessEngine():
- 会在第一次调用时,初始化并创建一个流程引擎,以后再调用就会返回相同的流程引擎
- 使用对应的方法可以创建和关闭所有流程引擎:ProcessEngines.init()和ProcessEngines.destroy()
- ProcessEngines会扫描所有activiti.cfg.xml 和 activiti-context.xml 文件
- 对于activiti.cfg.xml文件,流程引擎会使用Activiti的经典方式构建:
- ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(inputStream).buildProcessEngine()
- 对于activiti-context.xml文件,流程引擎会使用Spring方法构建:先创建一个Spring的环境,然后通过环境获得流程引擎
- 所有服务都是无状态的.这意味着可以在多节点集群环境下运行Activiti,每个节点都指向同一个数据库,不用担心哪个机器实际执行前端的调用.无论在哪里执行服务都没有问题
RepositoryService
- 负责静态信息
- 是使用Activiti引擎时最先接触的服务,提供了管理和控制发布包和流程定义的操作
- 流程定义是BPMN 2.0流程的java实现.它包含了一个流程每个环节的结构和行为
- 发布包是Activiti引擎的打包单位.一个发布包可以包含多个BPMN 2.0 xml文件和其他资源
- 开发者可以自由选择把任意资源包含到发布包中
- 既可以把一个单独的BPMN 2.0 xml文件放到发布包里,也可以把整个流程和相关资源都放在一起
- 可以通过RepositoryService来部署这种发布包.发布一个发布包,意味着把它上传到引擎中,所有流程都会在保存进数据库之前分析解析好
- 从这点来说,系统知道这个发布包的存在,发布包中包含的流程就已经可以启动了
- RepositoryService可以查询引擎中的发布包和流程定义
- RepositoryService暂停或激活发布包,对应全部和特定流程定义.暂停意味着它们不能再执行任何操作了,激活是对应的反向操作
- RepositoryService获得多种资源,例如包含在发布包里的文件,引擎自动生成的流程图
- RepositoryService获得流程定义的pojo版本,可以用来通过java解析流程,而不必通过xml
RuntimeService
- 负责启动一个流程定义的新实例
- 流程定义定义了流程各个节点的结构和行为
- 流程实例就是这样一个流程定义的实例
- 对每个流程定义来说,同一时间会有很多实例在执行
- RuntimeService可以用来获取和保存流程变量,这些数据是特定于某个流程实例的,并会被很多流程中的节点使用
- Runtimeservice可以查询流程实例和执行,执行对应BPMN 2.0中的'token',基本上执行指向流程实例当前在哪里
- RuntimeService可以在流程实例等待外部触发时使用,可以用来继续流程实例.流程实例可以有很多暂停状态,而服务提供了多种方法来'触发'实例, 接受外部触发后,流程实例就会继续向下执行
TaskService
- 任务是由系统中真实人员执行的,它是Activiti这类BPMN引擎的核心功能之一, 所有与任务有关的功能都包含在TaskService中
- 在TaskService中,查询分配给用户或组的任务
- 在TaskService中,创建独立运行任务,这些任务与流程实例无关
- 在TaskService中,手工设置任务的执行者,或者这些用户通过何种方式与任务关联
- 在TaskService中,认领并完成一个任务:
- 认领意味着一个人期望成为任务的执行者,即这个用户会完成这个任务
- 完成意味着“做这个任务要求的事情”,通常来说会有很多种处理形式
IdentityService
- 可以管理,创建,更新,删除,查询..群组和用户
- Activiti执行时并没有对用户进行检查.任务可以分配给任何人,但是引擎不会校验系统中是否存在这个用户.这是Activiti引擎也可以使用外部服务:ldap,活动目录...
HistoryService
- HistoryService提供了Activiti引擎的所有历史数据
- 在执行流程时,引擎会根据配置保存很多数据:流程实例启动时间,任务的参与者,完成任务的时间,每个流程实例的执行路径..., 这个服务主要通过查询功能来获得这些数据
FormService
- FormService是一个可选服务,即使不使用它,Activiti也可以完美运行,不会损失任何功能
- FormService提供了启动表单和任务表单两个概念
- 启动表单会在流程实例启动之前展示给用户
- 任务表单会在用户完成任务时展示
- Activiti支持在BPMN 2.0流程定义中设置这些表单.这个服务以一种简单的方式将数据暴露出来,是可选的,表单也不一定要嵌入到流程定义中
ManagementService
- 在使用Activiti的定制环境中基本上不会用到
- ManagementService可以查询数据库的表和表的元数据
- ManagementService提供了查询和管理异步操作的功能
- Activiti的异步操作用途很多:定时器,异步操作,延迟暂停,激活..
异常策略
- Activiti中的基础异常为org.activiti.engine.ActivitiException, 一个非检查异常
- 这个异常可以在任何时候被API抛出,特定方法抛出的特定的异常
/**
* Called when the task is successfully executed.
* @param taskId the id of the task to complete, cannot be null.
* @throws ActivitiObjectNotFoundException when no task exists with the given id.
*/
void complete(String taskId);
当传入一个不存在的任务的id时,就会抛出异常.taskId不能为null,如果传入null,就会抛出ActivitiIllegalArgumentException
- 应该避免过多的异常继承,子类只用于特定的场合
- 流程引擎和API调用的其他场合不使用子类异常,抛出一个普通的ActivitiExceptions
ActivitiWrongDbException: 当Activiti引擎发现数据库版本号和引擎版本号不一致时抛出
ActivitiOptimisticLockingException: 对同一数据进行并发方法并出现乐观锁时抛出
ActivitiClassLoadingException: 当无法找到需要加载的类或在加载类时出现了错误-JavaDelegate,TaskListener
ActivitiObjectNotFoundException: 当请求或操作的对应不存在时抛出
ActivitiIllegalArgumentException: 这个异常表示调用Activiti API时传入了一个非法的参数,可能是引擎配置中的非法值,或提供了一个非法值,或流程定义中使用的非法值
ActivitiTaskAlreadyClaimedException: 当任务已经被认领了,再调用taskService.claim(...)就会抛出
查询 API
- 在Activiti流程引擎中查询数据有两种方式:
- 查询API
- 原生查询
- 查询API: 查询API提供了完全类型安全的API,可以自定义添加查询条件和精确的排序条件,所有条件都以AND组合
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("kermit")
.processVariableValueEquals("orderId", "0815")
.orderByDueDate().asc()
.list();
- 原生查询:
- 需要更强大的查询时:使用OR条件或者能使用查询API实现的条件.
- 可以编写自己的SQL查询. 返回类型由你使用的查询对象决定,数据会映射到正确的对象上:任务,流程实例,执行..
- 查询作用在数据库上,必须使用数据库中定义的表名和列名,要了解内部数据结构
- 使用原生查询时,表名可以通过API获得,可以尽量减少对数据库的依赖
List<Task> tasks = taskService.createNativeTaskQuery()
.sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T WHERE T.NAME_ = #{taskName}")
.parameter("taskName", "gonzoTask")
.list();
long count = taskService.createNativeTaskQuery()
.sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T1, "
+ managementService.getTableName(VariableInstanceEntity.class) + " V1 WHERE V1.TASK_ID_ = T1.ID_")
.count();
表达式
- Activiti使用UEL处理表达式.UEL即统一表达式语言, 是EE6规范的一部分.为了在所有运行环境都支持最新UEL的所有功能,使用JUEL的修改版本
- 表达式可以用在很多场景下:
- Java服务任务
- 执行监听器
- 任务监听器
- 条件流
- 虽然有两重表达式:值表达式和方法表达式, Activiti进行了抽象,所以两者可以同样使用在需要表达式的场景中
- Value expression: 解析为值,默认
${myVar}
${myBean.myProperty}
所有流程变量都可以使用,所有spring bean(spring环境中)也可以使用在表达式中
- Method expression: 调用一个方法,使用或不使用参数
${printer.print()}
${myBean.addNewOrder('orderName')}
${myBean.doSomething(myVar, execution)}
当调用一个无参数的方法时,记得在方法名后添加空的括号,以区分值表达式
传递的参数可以是字符串也可以是表达式,它们会被自动解析
- 这些表达式支持解析原始类型:
- bean
- list
- 数组
- map
- 包括比较
- 在流程实例中,表达式中可以使用一些默认对象:
- execution: DelegateExecution,提供外出执行的额外信息
- task: DelegateTask,提供当前任务的额外信息 ,只对任务监听器的表达式有效
- authenticatedUserId: 当前登录的用户id.如果没有用户登录,这个变量就不可用
单元测试
- 业务流程是软件项目的一部分,它也应该和普通的业务流程一样进行测试:使用单元测试
- 因为Activiti是一个嵌入式的java引擎,所以为业务流程编写单元测试和写普通单元测试完全一样
- Activiti支持JUnit 3和4进行单元测试
- 使用JUnit 3时, 必须集成org.activiti.engine.test.ActivitiTestCase. 它通过保护的成员变量提供ProcessEngine和服务,
- 在测试的setup()中,默认会使用classpath下的activiti.cfg.xml初始化流程引擎
- 要使用不同的配置文件,可以重写getConfigurationResource() 方法
- 如果配置文件相同的话,对应的流程引擎会被静态缓存,就可以用于多个单元测试
- 继承了ActivitiTestCase, 可以在测试方法上使用org.activiti.engine.test.Deployment注解.测试执行前,与测试类在同一个包下的,格式为testClassName.testMethod.bpmn20.xml的资源文件,会被部署.测试结束后,发布包也会被删除,包括所有相关的流程实例,任务...Deployment注解也可以直接设置资源的位置
public class MyBusinessProcessTest extends ActivitiTestCase {
@Deployment
public void testSimpleProcess() {
runtimeService.startProcessInstanceByKey("simpleProcess");
Task task = taskService.createTaskQuery().singleResult();
assertEquals("My Task", task.getName());
taskService.complete(task.getId());
assertEquals(0, runtimeService.createProcessInstanceQuery().count());
}
}
- 要想在使用JUnit 4编写单元测试时获得同样的功能
- 可以使用org.activiti.engine.test.ActivitiRule. 通过它,可以通过getter方法获得流程引擎和各种服务
- 使用这个Rule也会启用org.activiti.engine.test.Deployment注解
- 它会在classpath下查找默认的配置文件,如果配置文件相同的话,对应的流程引擎会被静态缓存,就可以用于多个单元测试
public class MyBusinessProcessTest {
@Rule
public ActivitiRule activitiRule = new ActivitiRule();
@Test
@Deployment
public void ruleUsageExample() {
RuntimeService runtimeService = activitiRule.getRuntimeService();
runtimeService.startProcessInstanceByKey("ruleUsage");
TaskService taskService = activitiRule.getTaskService();
Task task = taskService.createTaskQuery().singleResult();
assertEquals("My Task", task.getName());
taskService.complete(task.getId());
assertEquals(0, runtimeService.createProcessInstanceQuery().count());
}
}
调试单元测试
- 使用内存数据库H2进行单元测试,在调试环境监视Activiti的数据库:
- 在单元测试里设置了一个断点:

- 用调试模式运行单元测试,右击单元测试,选择[运行为]和[单元测试],测试会停在我们的断点上, 然后我们就可以监视测试的变量,它们显示在调试面板里

- 要监视Activiti的数据,打开[显示]窗口(如果找不到,打开[窗口]-[显示视图]-[其他],选择[显示]并点击[代码已完成],org.h2.tools.Server.createWebServer("-web").start()

- 选择你点击的行,右击.然后选择[显示]

- 打开一个浏览器,输入http://localhost:8082, 输入内存数据库的JDBC URL(默认为jdbc:h2:mem:activiti),点击连接按钮

- 可以看到Activiti的数据,通过它们可以了解单元测试时,如何以及为什么这样运行的

Web中的流程引擎
- ProcessEngine是线程安全的,可以在多线程下共享
- 在web应用中, 意味着可以在容器启动时创建流程引擎, 在容器关闭时关闭流程引擎
- 编写一个ServletContextListener 在普通的Servlet环境下初始化和销毁流程引擎:
public class ProcessEnginesServletContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
ProcessEngines.init();
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
ProcessEngines.destroy();
}
}
contextInitialized方法会执行ProcessEngines.init() 这会查找classpath下的activiti.cfg.xml文件,根据配置文件创建一个ProcessEngine(比如,多个jar中都包含配置文件)如果classpath中包含多个配置文件,确认它们有不同的名字
- 需要使用流程引擎时,可以通过
ProcessEngines.getDefaultProcessEngine()
或
ProcessEngines.getProcessEngine("myName");
- ContextListener中的contextDestroyed方法会执行ProcessEngines.destroy().这会关闭所有初始化的流程引擎
项目实践之工作流引擎基本文档!Activiti工作流框架中流程引擎API和服务详解的更多相关文章
- 使用DOM进行xml文档的crud(增删改查)操作<操作详解>
很多朋友对DOM有感冒,这里我花了一些时间写了一个小小的教程,这个能看懂,会操作了,我相信基于DOM的其它API(如JDOM,DOM4J等)一般不会有什么问题. 后附java代码,也可以下载(可点击这 ...
- Atitit 项目的主体设计与结构文档 v3
Atitit 项目的主体设计与结构文档 v3 1. 实现的目标2 1.1. cross device跨设备(pc 手机 平板)作为规划2 1.2. 企业级Java体系与开发语言2 1.3. 高扩展性, ...
- Atitit 项目的主体设计与结构文档 v5
Atitit 项目的主体设计与结构文档 v5 1. 版本历史说明2 2. 功能大概说明2 3. 实现的目标3 3.1. cross device跨设备(pc 手机 平板)3 3.2. cross sc ...
- swagger:API在线文档自动生成框架
传统的API从开发测试开始我们经常借用类似Postman.fiddle等等去做接口测试等等工具:Swagger 为API的在线测试.在线文档提供了一个新的简便的解决方案: NET 使用Swagger ...
- 随时发布:REST API文档的代码仓库中的持续集成与协作
本文主要内容:API文档提供了预测客户成功的关键路径:在代码附近的文档上进行协作可以更好地检查代码和文档文件,提高自动化效率,并专门针对文档进行质量测试:提供通用文档框架,标准,自动化和工具,以提高团 ...
- VS2010-MFC(利用MFC向导生成单文档应用程序框架)
一.VC++与MFC 讲VC++免不了要提MFC,MFC全称Microsoft Foundation Classes,也就是微软基础类库.它是VC++的核心,是C++与Windows API的结合,很 ...
- VS2010/MFC编程入门之二(利用MFC向导生成单文档应用程序框架)
VS2010/MFC编程入门之二(利用MFC向导生成单文档应用程序框架)-软件开发-鸡啄米 http://www.jizhuomi.com/software/141.html 上一讲中讲了VS20 ...
- ElasticSearch 学习记录之 分布式文档存储往ES中存数据和取数据的原理
分布式文档存储 ES分布式特性 屏蔽了分布式系统的复杂性 集群内的原理 垂直扩容和水平扩容 真正的扩容能力是来自于水平扩容–为集群添加更多的节点,并且将负载压力和稳定性分散到这些节点中 ES集群特点 ...
- Xml学习笔记(3)利用递归解析Xml文档添加到TreeView中
利用递归解析Xml文档添加到TreeView中 private void Form1_Load(object sender, EventArgs e) { XmlDocument doc = new ...
随机推荐
- pwnable.tw orw
orw 首先,检查一下程序的保护机制 开启了canary保护,还是个32位的程序,应该是个简单的题
- 自学转行JAVA,没有项目经历怎么找工作?
应届生或者是刚参加工作的转行人员都有这样一个疑惑,刚学出来没有工作经验,但是企业又要求你必须要有工作经验,但是刚毕业找不到工作就不可能有工作经验,感觉陷入一个死循环.其实这种情况那些企业是不可能不知道 ...
- Nuget-自定义模板的创建与使用
参考官方文档: https://docs.microsoft.com/zh-cn/dotnet/core/tools/custom-templates https://devblogs.microso ...
- JAVAEE_Servlet_02_Servlet接口实现类HttpServlet
Servlet接口实现类 * Servlet接口: Servlet接口是来自于Servlet规范下的一个接口,存在于HTTP服务器(Tomcat)所提供的jar包中.Servlet-api.jar. ...
- zipfile tarfile模块
zipfile --- 使用ZIP存档 这个模块提供了创建.读取.写入.添加及列出 ZIP 文件的工具 # 创建一个ZipFile对象, 可使用上下文管理 with class zipfile.Zip ...
- 03- web表单测试
软件分为 b/s c/s两种架构 表单测试 1.用户注册,登录,信息提交. 2.用户查询商品. 3.用户订购商品. 4.用户查询订单等. 表单测试实例 表单数据添加测试(一) 添加按钮可用,测试点击添 ...
- 【工具库】Java实体映射工具MapStruct
一.什么是MapStruct? MapStruct是用于代码中JavaBean对象之间的转换,例如DO转换为DTO,DTO转换为VO,或Entity转换为VO等场景,虽然Spring库和 Apache ...
- (转)Amazon Aurora MySQL 数据库配置最佳实践
转自:https://zhuanlan.zhihu.com/p/165047153 Amazon Aurora MySQL 数据库配置最佳实践 AWS云计算 已认证的官方帐号 1 人赞同了该文章 ...
- POJ2431贪心(最少加油次数)
题意: 给一个终点,然后给你一个卡车距离终点的距离,还有其他个加油站距离终点的距离,然后每走一个单位距离要花费一个单位油,卡车的邮箱是无限大的,而每个加油站的油量是有限的,整个路径是一个 ...
- 每天一道面试题LeetCode 80--删除排序数组中的重复项 II(python实现)
LeetCode 80--删除排序数组中的重复项 II 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输 ...