一、背景介绍
公司最近接了一个监狱AB门系统的项目,在对项目进行调研时,发现客户的关注点主要是在AB门流程这块,项目大部分功能都是审批流程和单据流动状态等。而之前公司的项目关于流程主要都是在表中设置状态后使用代码进行流程控制,正好基于此项目,将工作流框架整合到项目中,记录整合过程中框架知识和在整合过程中的问题。
二、Activiti与JBPM对比
在进行工作流框架选择上,根据市场占用率主要有Activiti和jBPM。既然这样,我们就在这两个框架中选择一个,由于首次引入项目,主要考虑的还是当遇到问题时社区比较活跃和后期的一个扩展。
技术组成 | Activiti | jBPM |
ORM框架 | Mybatis3 | Hibernate3 |
持久化标准 | 无 | EJB JPA规范 |
事务管理 | Mybatis自带 / Spring集成事务 | Bitronix / 基于JTA事务管理 |
数据库连接方式 | Jdbc / DataSource | Jdbc / DataSource |
Spring支持 | 天然支持Spirng | 默认未提供对Spring支持 |
支持的数据库 | Oracle / SQLServer / MySQL / H2 / 内存数据库 | Oracle / SQLServer / MySQL / 内存数据库 |
设计模式 | 命令模式 / 观察者模式 等 | 无 |
内部服务通信 | Service间通过API调用 | 基于Apache Mina异步通信 |
集成接口 | SOAP / Mule / RESTful | 消息通信 |
支持的流程格式 | BPMN2 / xPDL / jPDL 等 | 只支持BPMN2 xml |
引擎核心 | PVM流程虚拟机 | Drools |
技术前身 | jBPM3 / jBPM4 | Drools Flow |
插件工具 | IDEA的actiBPM / Eclipse的Eclipse Designer / 提供基于REST风格的Activit Explorer | 提供Eclipse插件和Web应用管理 |
更新周期 | 大约每两个月发布一次 | 不定时 |
基于以上及项目需求,我们选择Activiti作为项目的工作流框架,其中Activiti中5,6,7各个版本又有些不同,根据稳定性和社区情况,我们最终选择Activiti6.X。
三、工作流基础
3.1 什么时BPM
BPM即Business Process Management的缩写,为业务流程管理。是一套达成企业各种业务环节整合的全面管理模式。BPM是一系列逻辑相关的活动的集合,BPM最早是由工作流和企业应用集成融合发展而来,当时是为了满足无纸化办公需求。
3.2 工作流的生命周期
一个完整的工作流生命周期主要有5步:
1、定义:即流程的定义,所有的流程总是从定义开始。主要任务是收集需求并将其转化为流程定义。
2、发布:开发人员将资源打包后在系统平台中发布流程定义,主要任务流程定义文件/自定义表单/任务监听类等。
3、执行:具体的流程引擎按照上面定义的流程处理路线来执行业务。
4、监控:收集每个任务的结果,将根据不同结果来做处理。
5、优化:此时业务流程已经完成,需要的就是优化流程或重新设计等。
3.3 什么时BPMN
即Business Process Modeling Notation的简称,全称为业务流程建模标注 ,由BPMN标准组织发布,2011年发布到2.0之后,市场常用的都是此版本规范。
BPMN定义类业务流程图,其基于流程图技术,同时对创建业务流程操作的图形化模型进行了裁剪。业务流程的模型即图形化对象的网图,包含有活动和定义操作顺序的流程控制。
四、Activiti介绍
4.1 什么是Activiti
Activiti是一个针对企业用户、开发人员、系统管理员的轻量级工作流业务管理平台,其核心是使用Java开发的快速、稳定的BPMN2.0流程引擎。Activiti是在ApacheV2许可下发布的,可以运行在任何类型的Java程序中,如:服务器、集群、云服务等。Activiti可以完美的与Spring集成,是基于简约的设计思想而创建。
4.2 Activiti的特点
1、数据持久化:Activiti设计思想是简洁与快速。一般情况下系统的瓶颈主要就体现在应用和数据库的交互上,针对这种情况Activiti选择了使用Mybatis,而通过最优SQL语句执行Command。
2、引擎Service接口:Activiti引擎提供了七大Service接口,都是通过ProcessEngine获取,同时支持链式API编程风格。
Service接口 | 作用 |
RepositoryService | 流程仓库Service,用于管理流程仓库,如:部署、删除、读取流程资源 |
IdentifyService | 身份Service,可管理和查询用户、组之间的关系 |
RuntimeService | 运行时Service,处理所有正在运行的任务和流程实例等 |
TaskService | 任务Service,用于管理查询任务,如签收、办理、指派等 |
FormService | 表单Service,用于读取和任务、流程相关的表单数据 |
HistoryService | 历史Service,可查询所有历史数据 |
ManagementService | 引擎管理Service,和具体业务无关,可用查询引擎配置、数据库、作业等 |
3、流程设计器:Activiti团队设计了基于BPMN2.0规范的设计器-Eclipse Designer,除此还有Signavio公司为Activiti定制的基于Web的Activiti Modeler流程设计器。
4、原生支持Spring:当前企业开发,基本上都会基于Spring去开发自己的系统,由于Activiti原生支持Spring,所以很轻松地进行Spring集成。
5、分离运行时与历史数据:运行与历史数据的分离,可以加快运行时数据的性能,当需要历史数据时,我们在去查询。
4.3 Activiti的应用
1、在系统集成方面:与ESB整合 / 与规则引擎整合 / 嵌入已有系统平台(也是本项目的需求)
2、在其他产品中应用:Alfresco公司的ECM产品在企业中应用,主要涉及文档管理 / 协作 / 记录管理 / 知识库管理 / Web内容管理等。
4.4 Activiti框架与组件
Activiti最重要的就是引擎,除此之外就是外部的工具和组件。
Modeling | Runtion | Management |
Activiti Modeler | Activiti Engine | Activiti Exproler |
Activiti Designer | Activiti REST | |
Activiti Kickstart |
下面对以上组件进行简单的说明:
1、Activiti Engine:最核心的模块,提供针对BPMN2.0规范的解析 / 执行 / 创建 / 管理(任务-流程实例) / 查询历史记录并生成相应报表等。
2、Activiti Modeler:模型设计器,非Activiti公司开发。用于将需求转换为规范流程定义。
3、Activiti Designer:设计器,与Activiti Modeler功能类似。
4、Activiti Exproler:用来管理仓库 / 用户 / 组,启动流程 / 任务办理等。
5、Activiti REST:提供REST风格的服务,允许客户端以JSON的方式与引擎的REST API交互,协议具有跨平台 / 跨语言。
五、搭建Activiti开发环境并测试
5.1 下载Activiti
从Activiti下载6.0版本并将其解压,我们可对目录结构进行说明。
1、database:该文件夹包含Activiti引擎表的创建 / 删除 / 版本升级三种类型的SQL脚本,命名规则为 “activiti.[数据库类型].[脚本类型create|drop|upgradestep].[其他].sql”,升级脚本包含 xx.to.yy。
2、docs:该目录包含javadocs(开发文档) / userguide(用户手册) / xsd(与流程定义相关的scheme)
3、libs:包含Activiti引擎的各个模块包。
4、wars:一些可以独立运行的项目,用来熟悉Activiti操作。activiti-explorer.war / activiti-rest.war。
5.2 配置环境
1、安装JDK并配置环境变量。
2、安装Ant
3、安装Maven
以上三个环境的安装方法到对应官方网站下载安装包,并按照官方文档说明来安装。
5.3 配置文件介绍
1、Activiti配置文件,我们首先开看一个标准的Activiti配置文件内容。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 5 6 <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"> 7 <property name="databaseType" value="h2" /> 8 <property name="databaseSchemaUpdate" value="true" /> 9 <property name="jobExecutorActivate" value="false" /> 10 <property name="history" value="full" /> 11 </bean> 12 13 </beans>
对Spring熟悉的同学一看该配置文件就知道这是一个普通的Spring配置文件,从这也可体现出Activiti对Spring的原生支持。
关于相关的配置见下图:
2、Maven配置,在Maven项目的pom文件中引入依赖
<dependency> <groupId>org.activiti</groupId> <artifactId>activiti-engine</artifactId> <version>6.0.0</version> </dependency>
5.4 一个简单的实例
下面我们就使用一个请假流程来对Activit做简单的实战。由请假人发起,部门领导审批,完成。
首先我们需要创建流程图:
这就是流程图设计器的界面,流程设计总是从start开始且只有一个入口,总是由End结束可有多个结束。中间就是任务、结果、流程线等,关于各个节点后续再做介绍,基于此流程图我们来看看代码时如何实现的。
1 /** 2 * 该实例测试简单流程包含审批和审批结果 3 * @throws Exception 4 */ 5 @Test 6 public void testStartProcess2() throws Exception { 7 // 1 创建流程引擎,使用内存数据库 8 ProcessEngine processEngine = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration().buildProcessEngine(); 9 10 // 2 部署流程定义文件 11 RepositoryService repositoryService = processEngine.getRepositoryService(); 12 // String bpmnFileName = "leave2.bpmn"; 13 String bpmnFileName = "leave2.bpmn.xml"; 14 repositoryService.createDeployment().addInputStream("leave2.bpmn", this.getClass().getClassLoader().getResourceAsStream(bpmnFileName)).deploy(); 15 16 // 3 验证已部署的流程定义 17 ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().singleResult(); 18 assertEquals("leave2", processDefinition.getKey()); 19 20 // 4 启动流程并返回流程实例 21 RuntimeService runtimeService = processEngine.getRuntimeService(); 22 23 Map<String, Object> variables = new HashMap<>(); 24 variables.put("applyUser", "employee1"); //申请人名称 25 variables.put("days", 3); //请假天数 26 27 ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("leave2", variables); 28 assertNotNull(processInstance); 29 System.out.println("pid=" + processInstance.getId() + ", pdid=" + processInstance.getProcessDefinitionId()); 30 31 TaskService taskService = processEngine.getTaskService(); 32 Task deptLeaderTask = taskService.createTaskQuery().taskCandidateGroup("deptLeader").singleResult(); //查询deptLeader组未签收的任务 33 assertNotNull(deptLeaderTask); 34 assertEquals("领导审批", deptLeaderTask.getName()); 35 taskService.claim(deptLeaderTask.getId(), "leaderUser"); //签收此任务归用户leaderUser所有 36 37 variables = new HashMap<>();//.clear(); //处理结果 38 variables.put("approved", true); //设置审批通过 39 40 taskService.complete(deptLeaderTask.getId(), variables); //设置审批结果 41 42 deptLeaderTask = taskService.createTaskQuery().taskCandidateGroup("deptLeader").singleResult(); //由于任务已经完成,此时查询任务应该为空 43 assertNull(deptLeaderTask); 44 45 // 获取历史记录 46 HistoryService historyService = processEngine.getHistoryService(); 47 long count = historyService.createHistoricProcessInstanceQuery().finished().count(); //已经完成的流程实例数 48 assertEquals(1, count); 49 }
运行结果:
1 pid=4, pdid=leave2:1:3 2 applyUser:employee1, days:3, approval:true