Activiti 介绍
什么是 Activiti?
Activiti5 是由 Alfresco 软件在 2010 年 5 月 17 日发布的业务流程管理 (BPM) 框架, 它是覆盖了业务流程管理工作流服务协作等领域的一个开源的灵活的易扩展的可执行流程语言框架 Activiti 基于 Apache 许可的开源 BPM 平台, 创始人 Tom Baeyens 是 JBoss jBPM 的项目架构师, 它特色是提供了 eclipse 插件, 开发人员可以通过插件直接绘画出业务 流程图.
我们即将学习的是一个业务流程管理框架, 常见开源工作流引擎框架 : OSWorkFlowjBPM(jboss business process management),Activiti 工作流 (是对 jBPM 升级) 一般我们称作为工作流框架..
为什么要学习 Activiti
那我们为什么要学习业务流程管理框架呢??? 学习它干嘛??? 工作流(Workflow), 就是业务过程的部分或整体在计算机应用环境下的自动化
我们来提出一个常见的需求来更好地理解:
我们在学生时代, 肯定会遇到请假写请假条的情况, 如果学校的请假抓得非常严, 就需要经过多层的同意才能确定能不能请假..
班主任 ->任课老师 ->教学总监 ->校长这么一个流程, 首先我们先明确一点: 我们作为一个学生, 不可能直接跳过老师, 去找校长申请请假的校长是你随便找的吗??
因此我们请假的流程是一步一步的, 并不能做跳跃
也就是说, 当班主任没有批准请假的时候, 即使你去找任课老师了, 任课老师会让你回去找班主任的, 作为任课老师了, 只关注班主任有没有批准你请假, 同理, 作为校长, 只关注教学总监有没有批准你请假!
进一步说: 当教学总监还没有批准你请假时, 你请假的请求是不会出现在校长的范围里的
其实是非常好理解的, 就是一步步往下执行, 当还没有执行到自己处理的点子上时, 是不会有对应的处理请求的分工有序
对上面的请假流程进行分析 **, 如果我们没有使用框架, 而把这么一个请假流程写到我们的网站中, 我们会怎么做呢 **??
我们需要维护一个变量, 来不断传递过去给下一个处理者... 如果一切正常, 需求不会变, 并没有条件的处理这是我们非常希望看到的... 但是, 如果有条件判断请假三天以下请假三天以上的处理方式不一样, 需求会变不需要校长批准了, 教学总监批准完, 你就能够请假了, 那么我们的代码就会变得乱
基于这么一个原因, 我们是需要学习一个框架来帮我们完成工作流的...
采用工作流管理系统的优点
1 提高系统的柔性, 适应业务流程的变化
2 实现更好的业务过程控制, 提高顾客服务质量
3 降低系统开发和维护成本
一快速入门 Activiti
首先我们来梳理一下 Activiti 的开发步骤:
我们要用到一个工作流, 首先就要把这个工作流定义出来也就是工作流的步骤的怎么样的, Activiti 支持以图的方式来定义工作流
定义完工作流, 就要部署到起来我们可以联想到 Tomcat, 我们光下载了 Tomcat 是没有用的, 要把它部署起来
随后我们就执行该工作流, 该工作流就随着我们定义的步骤来一一执行!
1.1BPMN
业务流程建模与标注(Business Process Model and Notation,BPMN) , 描述流程的基本符号, 包括这些图元如何组合成一个业务流程图(Business Process Diagram)
BPMN 这个就是我们所谓把工作流定义出来的流程图..
1.2 数据库相关
我们在执行工作流步骤的时候会涉及到很多数据执行该流程的人是谁所需要的参数是什么包括想查看之前流程执行的记录等等, 因此我们会需要用到数据库的表来保存数据...
由于我们使用的是 Activiti 框架, 这个框架会自动帮我们把对应的数据库表创建起来, 它涉及的表有 23 个, 但是常用的并不是很多, 因此也不用很慌...
下面就列举一下表的情况
Activiti 的后台是有数据库的支持, 所有的表都以 ACT_开头 第二部分是表示表的用途的两个字母标识 用途也和服务的 API 对应
ACT_RE_*: 'RE'表示 repository 这个前缀的表包含了流程定义和流程静态资源 (图片, 规则, 等等)
ACT_RU_*: 'RU'表示 runtime 这些运行时的表, 包含流程实例, 任务, 变量, 异步任务, 等运行中的数据 Activiti 只在流程实例执行过程中保存这些数据, 在流程结束时就会删除这些记录 这样运行时表可以一直很小速度很快
ACT_ID_*: 'ID'表示 identity 这些表包含身份信息, 比如用户, 组等等
ACT_HI_*: 'HI'表示 history 这些表包含历史数据, 比如历史流程实例, 变量, 任务等等
ACT_GE_*: 通用数据, 用于不同场景下, 如存放资源文件
1.3 搭建配置环境
我这里使用的 Intellij idea 来使用 Activiti, 首先, 我们得下载插件来使用 Activiti 因为定义流程图需要用到插件
详情可以看这篇博文: http://blog.sina.com.cn/s/blog_4b3196670102woix.html
Activiti 插件中文乱码问题:
http://www.cnblogs.com/mymelody/p/6049291.html
流程之前的连线是通过图中的蓝色小点点拖动来进行连接的...
导入对应的 jar 包
- activation-1.1.jar
- activiti-bpmn-converter-5.13.jar
- activiti-bpmn-layout-5.13.jar
- activiti-bpmn-model-5.13.jar
- activiti-common-rest-5.13.jar
- activiti-engine-5.13.jar
- activiti-json-converter-5.13.jar
- activiti-rest-5.13.jar
- activiti-simple-workflow-5.13.jar
- activiti-spring-5.13.jar
- aopalliance-1.0.jar
- commons-dbcp-1.4.jar
- commons-email-1.2.jar
- commons-fileupload-1.2.2.jar
- commons-io-2.0.1.jar
- commons-lang-2.4.jar
- commons-pool-1.5.4.jar
- h2-1.3.170.jar
- hamcrest-core-1.3.jar
- jackson-core-asl-1.9.9.jar
- jackson-mapper-asl-1.9.9.jar
- javaGeom-0.11.0.jar
- jcl-over-slf4j-1.7.2.jar
- jgraphx-1.10.4.2.jar
- joda-time-2.1.jar
- junit-4.11.jar
- log4j-1.2.17.jar
- mail-1.4.1.jar
- mybatis-3.2.2.jar
- mysql-connector-java.jar
- org.restlet.ext.fileupload-2.0.15.jar
- org.restlet.ext.jackson-2.0.15.jar
- org.restlet.ext.servlet-2.0.15.jar
- org.restlet-2.0.15.jar
- slf4j-api-1.7.2.jar
- slf4j-log4j12-1.7.2.jar
- spring-aop-3.1.2.RELEASE.jar
- spring-asm-3.1.2.RELEASE.jar
- spring-beans-3.1.2.RELEASE.jar
- spring-context-3.1.2.RELEASE.jar
- spring-core-3.1.2.RELEASE.jar
- spring-expression-3.1.2.RELEASE.jar
- spring-jdbc-3.1.2.RELEASE.jar
- spring-orm-3.1.2.RELEASE.jar
- spring-tx-3.1.2.RELEASE.jar
1.4 开发步骤
上面已经说过了, 我们要想使用 Activiti 就需要有数据库的支持, 虽然 Activiti 是自动帮我们创建对应的数据库表, 但是我们是需要配置数据库的信息的我们配置数据库的信息, 接着拿到 Activiti 最重要的 API------Activiti 引擎
1.4.1 得到工作流引擎
Activiti 提供使用代码的方式来配置数据库的信息:
- @Test
- public void createActivitiEngine(){
/* *1. 通过代码形式创建
* - 取得 ProcessEngineConfiguration 对象
* - 设置数据库连接属性
* - 设置创建表的策略 (当没有表时, 自动创建表)
* - 通过 ProcessEngineConfiguration 对象创建 ProcessEngine 对象 *$/ 取得 ProcessEngineConfiguration 对象
- ProcessEngineConfiguration engineConfiguration=ProcessEngineConfiguration.
- createStandaloneProcessEngineConfiguration();
- // 设置数据库连接属性
- engineConfiguration.setJdbcDriver("com.mysql.jdbc.Driver");
- engineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/activitiDB?createDatabaseIfNotExist=true"
- + "&useUnicode=true&characterEncoding=utf8");
- engineConfiguration.setJdbcUsername("root");
- engineConfiguration.setJdbcPassword("root");
- // 设置创建表的策略 (当没有表时, 自动创建表)
- // public static final java.lang.String DB_SCHEMA_UPDATE_FALSE = "false";// 不会自动创建表, 没有表, 则抛异常
- // public static final java.lang.String DB_SCHEMA_UPDATE_CREATE_DROP = "create-drop";// 先删除, 再创建表
- // public static final java.lang.String DB_SCHEMA_UPDATE_TRUE = "true";// 假如没有表, 则自动创建
- engineConfiguration.setDatabaseSchemaUpdate("true");
- // 通过 ProcessEngineConfiguration 对象创建 ProcessEngine 对象
- ProcessEngine processEngine = engineConfiguration.buildProcessEngine();
- System.out.println("流程引擎创建成功!");
- }
Activiti 也可以通过配置文件来配置数据库的信息, 加载配置文件从而得到工作流引擎
- /**2. 通过加载 activiti.cfg.xml 获取 流程引擎 和自动创建数据库及表
- *
- ProcessEngineConfiguration engineConfiguration=
- ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
- // 从类加载路径中查找资源 activiti.cfg.xm 文件名可以自定义
- ProcessEngine processEngine = engineConfiguration.buildProcessEngine();
- System.out.println("使用配置文件 Activiti.cfg.xml 获取流程引擎");
- */
- activiti.cfg.xml
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
- <!-- 配置 ProcessEngineConfiguration -->
- <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
- <!-- 配置数据库连接 -->
- <property name="jdbcDriver" value="com.mysql.jdbc.Driver"></property>
- <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activitiDB?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8"></property>
- <property name="jdbcUsername" value="root"></property>
- <property name="jdbcPassword" value="root"></property>
- <!-- 配置创建表策略 : 没有表时, 自动创建 -->
- <property name="databaseSchemaUpdate" value="true"></property>
- </bean>
- </beans>
上面的那种加载配置文件方式, 配置文件的名字是可以自定义的, 如果我们配置文件的名字默认就是 activiti.cfg.xml 的话, 也是放在类路径下, 我们就可以使用默认的方式来进行加载了!
- @Test
- public void createActivitiEngine(){
- /**
- * 3. 通过 ProcessEngines 来获取默认的流程引擎
- *$/ 默认会加载类路径下的 activiti.cfg.xml
- ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
- System.out.println("通过 ProcessEngines 来获取流程引擎");
- }
- 1.4.2 定义工作流
- 定义工作流就需要我们刚才下载的插件了, 我们是使用图形的方式来定义工作流的....
- 在每个流程中, 我们都可以指定对应的处理人是谁, 交由谁处理
- 1.4.3 部署工作流
- 我们上面已经定义了工作流了, 工作流引擎我们也已经拿到了, 接下来就是把工作流部署到工作流引擎中了
- @Test
- public void deploy() {
- // 获取仓库服务 : 管理流程定义
- RepositoryService repositoryService = processEngine.getRepositoryService();
- Deployment deploy = repositoryService.createDeployment()// 创建一个部署的构建器
- .addClasspathResource("LeaveActiviti.bpmn")// 从类路径中添加资源, 一次只能添加一个资源
- .name("请求单流程")// 设置部署的名称
- .category("办公类别")// 设置部署的类别
- .deploy();
- System.out.println("部署的 id"+deploy.getId());
- System.out.println("部署的名称"+deploy.getName());
- }
- 相对应的数据库表就会插入数据涉及到的数据库表后面会详细说明现在我们只要了解到, 我们工作流引擎执行操作会有数据库表记录
- 1.4.4 执行工作流
- 指定指定工作流就是我们定义时工作流程表的 id
- @Test
- public void startProcess(){
- // 指定执行我们刚才部署的工作流程
- String processDefiKey="leaveBill";
- // 取运行时服务
- RuntimeService runtimeService = processEngine.getRuntimeService();
- // 取得流程实例
- ProcessInstance pi = runtimeService.startProcessInstanceByKey(processDefiKey);// 通过流程定义的 key 来执行流程
- System.out.println("流程实例 id:"+pi.getId());// 流程实例 id
- System.out.println("流程定义 id:"+pi.getProcessDefinitionId());// 输出流程定义的 id
- }
- 1.4.5 根据代理人查询当前任务的信息
- 刚才我们已经开始了工作流了, 随后工作流应该去到了申请请假的流程, 申请请假的处理人是钟福成, 我们可以查询出对应的信息:
- // 查询任务
- @Test
- public void queryTask(){
- // 任务的办理人
- String assignee="钟福成";
- // 取得任务服务
- TaskService taskService = processEngine.getTaskService();
- // 创建一个任务查询对象
- TaskQuery taskQuery = taskService.createTaskQuery();
- // 办理人的任务列表
- List<Task> list = taskQuery.taskAssignee(assignee)// 指定办理人
- .list();
- // 遍历任务列表
- if(list!=null&&list.size()>0){
- for(Task task:list){
- System.out.println("任务的办理人:"+task.getAssignee());
- System.out.println("任务的 id:"+task.getId());
- System.out.println("任务的名称:"+task.getName());
- }
- }
- }
- 1.4.6 处理任务
- 我们现在处理流程去到申请请假中, 处理人是钟福成... 接着就是钟福成去处理任务, 根据任务的 id 使得流程继续往下走
- 任务的 id 刚才我们已经查询出来了上面, 我们如果是在 web 端操作数据的话, 那么只要传递过去就行了!
- // 完成任务
- @Test
- public void compileTask(){
- String taskId="304";
- //taskId: 任务 id
- processEngine.getTaskService().complete(taskId);
- System.out.println("当前任务执行完毕");
- }
- 当我们处理完该任务的时候, 就到了批准班主任任务了, 我们查询一下是不是如我们想象的效果:
- 我们按照定义的工作流程图一步一步往下走, 最终把流程走完
- 二流程定义细讲
- 管理流程定义主要涉及到以下的 4 张表:
- -- 流程部署相关的表
- SELECT * FROM act_ge_bytearray # 通用字节资源表
- SELECT * FROM act_ge_property # 通用属性表, 可以生成部署 id
- SELECT * FROM act_re_deployment #部署表
- SELECT * FROM act_re_procdef # 流程定义表
- 2.1PNG 资源
- 在 Eclipse 中 Activiti 插件会自动生成一个 BPMN 与之对应的 PNG 图片, 是需要通过加载 PNG 图片的
- @Test
- public void deploy() {
- // 获取仓库服务 : 管理流程定义
- RepositoryService repositoryService = processEngine.getRepositoryService();
- Deployment deploy = repositoryService.createDeployment()// 创建一个部署的构建器
- .addClasspathResource("LeaveActiviti.bpmn")// 从类路径中添加资源, 一次只能添加一个资源
- .name("请求单流程")// 设置部署的名称
- .category("办公类别")// 设置部署的类别
- .deploy();
- System.out.println("部署的 id"+deploy.getId());
- System.out.println("部署的名称"+deploy.getName());
- }
- 而我们的 Intellij idea 插件不会自动生成 PNG 图片, 但是我们在加载 BPMN 文件的时候, 好像会自动插入 PNG 图片, 数据库是有对应的记录的我们是没有手动加载 PNG 图片资源的
- 我们查看一下
- // 查看 bpmn 资源图片
- @Test
- public void viewImage() throws Exception{
- String deploymentId="201";
- String imageName=null;
- // 取得某个部署的资源的名称 deploymentId
- List<String> resourceNames = processEngine.getRepositoryService().getDeploymentResourceNames(deploymentId);
- // buybill.bpmn buybill.png
- if(resourceNames!=null&&resourceNames.size()>0){
- for(String temp :resourceNames){
- if(temp.indexOf(".png")>0){
- imageName=temp;
- }
- }
- }
- /**
- * 读取资源
- * deploymentId: 部署的 id
- * resourceName: 资源的文件名
- */
- InputStream resourceAsStream = processEngine.getRepositoryService()
- .getResourceAsStream(deploymentId, imageName);
- // 把文件输入流写入到文件中
- File file=new File("d:/"+imageName);
- FileUtils.copyInputStreamToFile(resourceAsStream, file);
- }
可惜的是, 查看出来的图片的中文数据会乱码...
2.2 查看流程定义
我们当时候查询流程定义是通过我们设置流程图的 id 来查看的.... 其实我们可以通过其他的属性来查询... 并且可以查询出更加详细的数据
- // 查看流程定义
- @Test
- public void queryProcessDefination(){
- String processDefiKey="buyBill";// 流程定义 key
- // 获取流程定义列表
- List<ProcessDefinition> list = processEngine.getRepositoryService().createProcessDefinitionQuery()
- // 查询 , 好比 where
- // .processDefinitionId(proDefiId) // 流程定义 id
- // 流程定义 id : buyBill:2:704 组成 : proDefikey(流程定义 key)+version(版本)+ 自动生成 id
- .processDefinitionKey(processDefiKey)// 流程定义 key 由 bpmn 的 process 的 id 属性决定
- // .processDefinitionName(name)// 流程定义名称 由 bpmn 的 process 的 name 属性决定
- // .processDefinitionVersion(version)// 流程定义的版本
- .latestVersion()// 最新版本
- // 排序
- .orderByProcessDefinitionVersion().desc()// 按版本的降序排序
- // 结果
- // .count()// 统计结果
- // .listPage(arg0, arg1)// 分页查询
- .list();
- // 遍历结果
- if(list!=null&&list.size()>0){
- for(ProcessDefinition temp:list){
- System.out.print("流程定义的 id:"+temp.getId());
- System.out.print("流程定义的 key:"+temp.getKey());
- System.out.print("流程定义的版本:"+temp.getVersion());
- System.out.print("流程定义部署的 id:"+temp.getDeploymentId());
- System.out.println("流程定义的名称:"+temp.getName());
- }
- }
- }
2.3 资源来自 ZIP
我们还可以加载的是 ZIP 类型的资源数据
- // 部署流程定义, 资源来自 zip 格式
- @Test
- public void deployProcessDefiByZip(){
- InputStream in=getClass().getClassLoader().getResourceAsStream("BuyBill.zip");
- Deployment deploy = processEngine.getRepositoryService()
- .createDeployment()
- .name("采购流程")
- .addZipInputStream(new ZipInputStream(in))
- .deploy();
- System.out.println("部署名称:"+deploy.getName());
- System.out.println("部署 id:"+deploy.getId());
- }
2.4 删除流程定义
- // 删除流程定义
- @Test
- public void deleteProcessDefi(){
- // 通过部署 id 来删除流程定义
- String deploymentId="101";
- processEngine.getRepositoryService().deleteDeployment(deploymentId);
- }
再次查询的时候, 已经没有 101 这个流程定义的数据了
三流程实例与任务执行细讲
流程实例与任务执行的常用表有以下几个:
-- 流程实例与任务
- SELECT * FROM act_ru_execution # 流程执行对象信息
- SELECT * FROM act_ru_task # 正在运行的任务表
- SELECT * FROM act_hi_procinst # 历史流程实例表
- SELECT * FROM act_hi_taskinst # 历史流程任务表
这里就简单简述一下流程实例与流程对象的区别:
(1)如果是单例流程, 执行对象 ID 就是流程实例 ID
(2)如果一个流程有分支和聚合, 那么执行对象 ID 和流程实例 ID 就不相同
(3)一个流程中, 流程实例只有 1 个, 执行对象可以存在多个
3.1 开始流程 ##
- @Test
- public void startProcess(){
- String processDefiKey="leaveActiviti";//bpmn 的 process id 属性
- ProcessInstance pi = processEngine.getRuntimeService()
- .startProcessInstanceByKey(processDefiKey);
- System.out.println("流程执行对象的 id:"+pi.getId());//Execution 对象
- System.out.println("流程实例的 id:"+pi.getProcessInstanceId());//ProcessInstance 对象
- System.out.println("流程定义的 id:"+pi.getProcessDefinitionId());// 默认执行的是最新版本的流程定义
- }
3.2 查看正在运行的任务
- // 查询正在运行任务
- @Test
- public void queryTask(){
- // 取得任务服务
- TaskService taskService = processEngine.getTaskService();
- // 创建一个任务查询对象
- TaskQuery taskQuery = taskService.createTaskQuery();
- // 办理人的任务列表
- List<Task> list = taskQuery.list();
- // 遍历任务列表
- if(list!=null&&list.size()>0){
- for(Task task:list){
- System.out.println("任务的办理人:"+task.getAssignee());
- System.out.println("任务的 id:"+task.getId());
- System.out.println("任务的名称:"+task.getName());
- }
- }
- }
查询 SELECT * FROM act_ru_task 表
3.3 获取流程实例的状态
有的时候, 我们需要判断它是在该流程, 还是该流程已经结束了我们可以根据获取出来的对象是否为空来进行判断
- // 获取流程实例的状态
- @Test
- public void getProcessInstanceState(){
- String processInstanceId="605";
- ProcessInstance pi = processEngine.getRuntimeService()
- .createProcessInstanceQuery()
- .processInstanceId(processInstanceId)
- .singleResult();// 返回的数据要么是单行, 要么是空 , 其他情况报错
- // 判断流程实例的状态
- if(pi!=null){
- System.out.println("该流程实例"+processInstanceId+"正在运行..."+"当前活动的任务:"+pi.getActivityId());
- }else{
- System.out.println("当前的流程实例"+processInstanceId+"已经结束!");
- }
- }
3.4 查看历史流程实例的信息
- // 查看历史执行流程实例信息
- @Test
- public void queryHistoryProcInst(){
- List<HistoricProcessInstance> list = processEngine.getHistoryService()
- .createHistoricProcessInstanceQuery()
- .list();
- if(list!=null&&list.size()>0){
- for(HistoricProcessInstance temp:list){
- System.out.println("历史流程实例 id:"+temp.getId());
- System.out.println("历史流程定义的 id:"+temp.getProcessDefinitionId());
- System.out.println("历史流程实例开始时间 -- 结束时间:"+temp.getStartTime()+"-->"+temp.getEndTime());
- }
- }
- }
查询表:
3.5 查看历史实例执行任务信息
- @Test
- public void queryHistoryTask(){
- String processInstanceId="605";
- List<HistoricTaskInstance> list = processEngine.getHistoryService()
- .createHistoricTaskInstanceQuery()
- .processInstanceId(processInstanceId)
- .list();
- if(list!=null&&list.size()>0){
- for(HistoricTaskInstance temp:list){
- System.out.print("历史流程实例任务 id:"+temp.getId());
- System.out.print("历史流程定义的 id:"+temp.getProcessDefinitionId());
- System.out.print("历史流程实例任务名称:"+temp.getName());
- System.out.println("历史流程实例任务处理人:"+temp.getAssignee());
- }
- }
- }
给予对应的实例 id 就可以查询出执行到哪个任务了...
3.6 执行任务
根据任务的 id, 就可以把该任务执行了
- @Test
- public void compileTask(){
- String taskId="608";
- //taskId: 任务 id
- processEngine.getTaskService().complete(taskId);
- System.out.println("当前任务执行完毕");
- }
四流程变量细讲
流程变量涉及到的数据库表:
act_ru_variable: 正在执行的流程变量表
act_hi_varinst: 流程变量历史表
流程变量在工作流中扮演着一个非常重要的角色例如: 请假流程中有请假天数请假原因等一些参数都为流程变量的范围 ** 流程变量的作用域范围是只对应一个流程实例 ** 也就是说各个流程实例的流程变量是不相互影响的流程实例结束完成以后流程变量还保存在数据库中(存放到流程变量的历史表中)
4.1 设置流程变量
我们有两种服务可以设置流程变量, TaskService 任务服务和 RuntimeService 运行时服务
场景
在流程开始的时候设置流程变量
在完成某个任务的时候设置流程变量
使用 TaskService 设置服务
使用 RuntimeService 设置服务
作用:
传递业务参数
动态指定代理人我们快速入门的例子是固定在流程定义图上写上代理人的
指定连接决定流程往哪边走
4.2 流程变量支持类型
如果我们使用 JavaBean 来作为流程的变量, 那么我们需要将 JavaBean 实现 Serializable 接口
Javabean 类型设置获取流程变量, 除了需要这个 javabean 实现了 Serializable 接口外, 还要求流程变量对象的属性不能发生变化, 否则抛出异常解决方案, 固定序列化 ID
4.3setVariable 和 setVariableLocal 的区别
4.4 例子
- // 模拟流程变量设置
- @Test
- public void getAndSetProcessVariable(){
- // 有两种服务可以设置流程变量
- // TaskService taskService = processEngine.getTaskService();
- // RuntimeService runtimeService = processEngine.getRuntimeService();
- /**1. 通过 runtimeService 来设置流程变量
- * executionId: 执行对象
- * variableName: 变量名
- * values: 变量值
- *$/ runtimeService.setVariable(executionId, variableName, values);
- // runtimeService.setVariableLocal(executionId, variableName, values);
- // 设置本执行对象的变量 , 该变量的作用域只在当前的 execution 对象
- // runtimeService.setVariables(executionId, variables);
- // 可以设置多个变量 放在 Map<key,value> Map<String,Object>
- /**2. 通过 TaskService 来设置流程变量
- * taskId: 任务 id
- *$/ taskService.setVariable(taskId, variableName, values);
- // taskService.setVariableLocal(taskId, variableName, values);
- //// 设置本执行对象的变量 , 该变量的作用域只在当前的 execution 对象
- // taskService.setVariables(taskId, variables); // 设置的是 Map<key,values>
- /**3. 当流程开始执行的时候, 设置变量参数
- * processDefiKey: 流程定义的 key
- * variables: 设置多个变量 Map<key,values>
- *$/ processEngine.getRuntimeService()
- // .startProcessInstanceByKey(processDefiKey, variables)
- /**4. 当任务完成时候, 可以设置流程变量
- * taskId: 任务 id
- * variables: 设置多个变量 Map<key,values>
- *$/ processEngine.getTaskService().complete(taskId, variables);
- /** 5. 通过 RuntimeService 取变量值
- * exxcutionId: 执行对象
- *
- *$/ runtimeService.getVariable(executionId, variableName);// 取变量
- // runtimeService.getVariableLocal(executionId, variableName);// 取本执行对象的某个变量
- // runtimeService.getVariables(variablesName);// 取当前执行对象的所有变量
- /** 6. 通过 TaskService 取变量值
- * TaskId: 执行对象
- *
- *$/ taskService.getVariable(taskId, variableName);// 取变量
- // taskService.getVariableLocal(taskId, variableName);// 取本执行对象的某个变量
- // taskService.getVariables(taskId);// 取当前执行对象的所有变量
- }
- // 设置流程变量值
- @Test
- public void setVariable(){
- String taskId="1804";// 任务 id
- // 采用 TaskService 来设置流程变量
- //1. 第一次设置流程变量
- // TaskService taskService = processEngine.getTaskService();
- // taskService.setVariable(taskId, "cost", 1000);// 设置单一的变量, 作用域在整个流程实例
- // taskService.setVariable(taskId, "申请时间", new Date());
- // taskService.setVariableLocal(taskId, "申请人", "何某某");// 该变量只有在本任务中是有效的
- //2. 在不同的任务中设置变量
- // TaskService taskService = processEngine.getTaskService();
- // taskService.setVariable(taskId, "cost", 5000);// 设置单一的变量, 作用域在整个流程实例
- // taskService.setVariable(taskId, "申请时间", new Date());
- // taskService.setVariableLocal(taskId, "申请人", "李某某");// 该变量只有在本任务中是有效的
- /**
- * 3. 变量支持的类型
- * - 简单的类型 :String booleanIntegerdoubledate
- * - 自定义对象 bean
- */
- TaskService taskService = processEngine.getTaskService();
- // 传递的一个自定义 bean 对象
- AppayBillBean appayBillBean=new AppayBillBean();
- appayBillBean.setId(1);
- appayBillBean.setCost(300);
- appayBillBean.setDate(new Date());
- appayBillBean.setAppayPerson("何某某");
- taskService.setVariable(taskId, "appayBillBean", appayBillBean);
- System.out.println("设置成功!");
- }
- // 查询流程变量
- @Test
- public void getVariable(){
- String taskId="1804";// 任务 id
- // TaskService taskService = processEngine.getTaskService();
- // Integer cost=(Integer) taskService.getVariable(taskId, "cost");// 取变量
- // Date date=(Date) taskService.getVariable(taskId, "申请时间");// 取本任务中的变量
- //// Date date=(Date) taskService.getVariableLocal(taskId, "申请时间");// 取本任务中的变量
- // String appayPerson=(String) taskService.getVariableLocal(taskId, "申请人");// 取本任务中的变量
- //// String appayPerson=(String) taskService.getVariable(taskId, "申请人");// 取本任务中的变量
- /$/ System.out.println("金额:"+cost);
- // System.out.println("申请时间:"+date);
- // System.out.println("申请人:"+appayPerson);
- // 读取实现序列化的对象变量数据
- TaskService taskService = processEngine.getTaskService();
- AppayBillBean appayBillBean=(AppayBillBean) taskService.getVariable(taskId, "appayBillBean");
- System.out.println(appayBillBean.getCost());
- System.out.println(appayBillBean.getAppayPerson());
- }
- public class AppayBillBean implements Serializable{
- private Integer id;
- private Integer cost;// 金额
- private String appayPerson;// 申请人
- private Date date;// 申请日期
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- public Integer getCost() {
- return cost;
- }
- public void setCost(Integer cost) {
- this.cost = cost;
- }
- public String getAppayPerson() {
- return appayPerson;
- }
- public void setAppayPerson(String appayPerson) {
- this.appayPerson = appayPerson;
- }
- public Date getDate() {
- return date;
- }
- public void setDate(Date date) {
- this.date = date;
- }
- }
五连线
上面我们已将学过了流程变量了, 可以在任务服务运行时服务流程开始完成某个任务时设置流程变量, 而我们的连接就是流程变量的实际应用了....
5.1 定义流程图
我们并不是所有的流程都是按一条的路径来走的, 我们有的时候会根据条件来走不同的路当然了, 最终该流程是会一步步走完....
例子:
重要的信息交由老板来处理, 不重要的信息交由经理来处理
表达式的结果必须是布尔型
- #{variable=='value'}
- $
5.2 测试
我在任务完成时设置流程变量为不重要, 那么跳到下一个流程时就是经理来进行处理
当我设置为重要的时候, 那么就是交由老板来处理
六排他网关
上面我们使用连线的时候用了两个条件 : 要么条件是重要, 要么条件是不重要.... 如果有另一种情况呢??? 就是用户把条件输入错了, 写成不知道重不重要, 那么我们的流程怎么走??? 岂不是奔溃了???
因此, 我们要有一条默认的路来走, 就是当该变量不符合任何的条件时, 我们也有一条默认的路
值得注意的是: 如果是在 Eclipse 中使用插件的 BPMN 流程图, 如果使用了排他网关, 那么在 Idea 下是解析不了的...
解决:
我们只要重新定义 BPMN 流程图的排他网关就行了, idea 中的 Activiti 插件是不用制定默认流程的, 只要我们不设置条件, 那就是默认的连接线
6.1 测试
- public class ExclusiveGetWay {
- private ProcessEngine processEngine = ProcessEngines
- .getDefaultProcessEngine();
- // 部署流程定义, 资源来在 bpmn 格式
- @Test
- public void deployProcessDefi() {
- Deployment deploy = processEngine.getRepositoryService()
- .createDeployment().name("排他网关流程")
- .addClasspathResource("ExclusiveGateway.bpmn")
- .deploy();
- System.out.println("部署名称:" + deploy.getName());
- System.out.println("部署 id:" + deploy.getId());
- }
- // 执行流程, 开始跑流程
- @Test
- public void startProcess() {
- String processDefiKey = "bankBill";// bpmn 的 process id 属性
- ProcessInstance pi = processEngine.getRuntimeService()
- .startProcessInstanceByKey(processDefiKey);
- System.out.println("流程执行对象的 id:" + pi.getId());// Execution 对象
- System.out.println("流程实例的 id:" + pi.getProcessInstanceId());// ProcessInstance
- // 对象
- System.out.println("流程定义的 id:" + pi.getProcessDefinitionId());// 默认执行的是最新版本的流程定义
- }
- // 查询正在运行任务
- @Test
- public void queryTask() {
- // 取得任务服务
- TaskService taskService = processEngine.getTaskService();
- // 创建一个任务查询对象
- TaskQuery taskQuery = taskService.createTaskQuery();
- // 办理人的任务列表
- List<Task> list = taskQuery.list();
- // 遍历任务列表
- if (list != null && list.size()> 0) {
- for (Task task : list) {
- System.out.println("任务的办理人:" + task.getAssignee());
- System.out.println("任务的 id:" + task.getId());
- System.out.println("任务的名称:" + task.getName());
- }
- }
- }
- // 完成任务
- @Test
- public void compileTask() {
- String taskId = "2404";
- Map<String,Object> params=new HashMap<String, Object>();
- params.put("visitor", 6);
- // taskId: 任务 id
- processEngine.getTaskService().complete(taskId, params);
- // processEngine.getTaskService().complete(taskId);
- System.out.println("当前任务执行完毕");
- }
- }
我们指定的值并不是 VIP 也不是后台, 那么就会自动去普通窗口中处理
七拓展阅读
并行网关:
等待活动:
用户任务:
使用流程变量指定处理人:
我们在快速入门的例子中, 是在定义流程图中硬性指定处理人, 其实这么干是不够灵活的, 我们学了流程变量之后, 我们是可以灵活地指定处理人的....
- @Test
- public void deployProcessDefi() {
- Deployment deploy = processEngine.getRepositoryService()
- .createDeployment().name("用户任务指定流程")
- .addClasspathResource("AppayBill.bpmn")
- .deploy();
- System.out.println("部署名称:" + deploy.getName());
- System.out.println("部署 id:" + deploy.getId());
- }
- // 执行流程, 开始跑流程
- @Test
- public void startProcess() {
- String processDefiKey = "appayBill";// bpmn 的 process id 属性
- Map<String,Object> params=new HashMap<String, Object>();
- params.put("userID", "王某某");
- ProcessInstance pi = processEngine.getRuntimeService()
- .startProcessInstanceByKey(processDefiKey, params);
- System.out.println("流程执行对象的 id:" + pi.getId());// Execution 对象
- System.out.println("流程实例的 id:" + pi.getProcessInstanceId());// ProcessInstance
- // 对象
- System.out.println("流程定义的 id:" + pi.getProcessDefinitionId());// 默认执行的是最新版本的流程定义
- }
- // 查询正在运行任务
- @Test
- public void queryTask() {
- String assignee="王某某";// 指定任务处理人
- // 取得任务服务
- TaskService taskService = processEngine.getTaskService();
- // 创建一个任务查询对象
- TaskQuery taskQuery = taskService.createTaskQuery();
- // 办理人的任务列表
- List<Task> list = taskQuery
- .taskAssignee(assignee)
- .list();
- // 遍历任务列表
- if (list != null && list.size()> 0) {
- for (Task task : list) {
- System.out.println("任务的办理人:" + task.getAssignee());
- System.out.println("任务的 id:" + task.getId());
- System.out.println("任务的名称:" + task.getName());
- }
- }
- }
使用类指定:
组任务:
直接指定办理人
使用流程变量
使用类
总结
如果一个业务需要多方面角色进行处理的话, 那么我们最好就是用工作流框架因为如果其中一个环节的需求发生了变化的话, 我们要是没有用到工作流那就需要修改很多的代码十分麻烦
Activiti 工作流框架快速入门:
定义工作流, 使用插件来把我们的流程图画出来这个流程图就是我们定义的工作流
工作流引擎是工作流的核心, 能够让我们定义出来的工作流部署起来
由于我们使用工作流的时候是有很多数据产生的, 因此 Activiti 是将数据保存到数据库表中的这些数据库表由 Actitviti 创建, 由 Activiti 维护
部署完的工作流是需要手动去执行该工作流的
根据由谁处理当前任务, 我们就可以查询出具体的任务信息
根据任务的 id, 我们就可以执行任务了
流程定义涉及到了四张数据库表
我们可以通过 API 把我们的流程定义图读取出来
可以根据查询最新版本的流程定义
删除流程定义
部署流程定义的时候也可以是 ZIP 文件
流程在运行中, 涉及到两个对象, 四张数据库表:
流程实例
流程任务
流程实例可以有多个, 流程对象只能有一个
如果流程没有分支的话, 那么流程实例就等于流程对象
基于这么两个对象, 我们就可以做很多事情了
获取流程实例和任务的历史信息
判断流程实例是否为空来判断流程是否结束了
查看正在运行服务的详细信息
通过流程实例来开启流程
流程变量: 它涉及到了两张表流程变量实际上就是我们的条件
流程变量的作用域只在流程实例中
我们可以在流程开始的时候设置流程变量, 在任务完成的时候设置流程变量
运行时服务和流程任务都可以设置流程变量
通过连线我们可以在其中设置条件, 根据不同的条件流程走不同的分支
如果没有设置默认的条件, 当条件不吻合的时候, 那么流程就走不下去了, 因此需要排他网关来设置一条默认的路径
来源: https://www.cnblogs.com/Java3y/p/8604308.html