1、因为我用的是eclipse,所以介绍一下eclipse安装activiti的方法,help——>Install new software,如下图:
点击add
会出现如下图:
安装图示输入,点击ok,然后finish,等待安装完成。
2、使用
创建一个项目:新建MyProcess.bpmn文件,代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="myProcess" name="My process" isExecutable="true">
<startEvent id="startevent1" name="Start">
<extensionElements>
<activiti:executionListener event="start" class="org.pan.activity.bpmnlistern.MyExecutionListern"></activiti:executionListener>
</extensionElements>
</startEvent>
<userTask id="usertask1" name="申请开始任务">
<extensionElements>
<activiti:taskListener event="create" class="org.pan.activity.bpmnlistern.MyTaskListernBegin"></activiti:taskListener>
</extensionElements>
</userTask>
<sequenceFlow id="flow1" name="申请" sourceRef="startevent1" targetRef="usertask1">
<extensionElements>
<activiti:executionListener event="take" class="org.pan.activity.bpmnlistern.MyExecutionListern"></activiti:executionListener>
</extensionElements>
</sequenceFlow>
<userTask id="usertask2" name="任务开始结束">
<extensionElements>
<activiti:taskListener event="create" class="org.pan.activity.bpmnlistern.MyTaskListernBegin"></activiti:taskListener>
</extensionElements>
</userTask>
<sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2">
<extensionElements>
<activiti:executionListener event="take" class="org.pan.activity.bpmnlistern.MyExecutionListern"></activiti:executionListener>
</extensionElements>
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${isTrue == "true"}]]></conditionExpression>
</sequenceFlow>
<endEvent id="endevent1" name="End">
<extensionElements>
<activiti:executionListener event="end" class="org.pan.activity.bpmnlistern.MyExecutionListern"></activiti:executionListener>
</extensionElements>
</endEvent>
<sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask4"></sequenceFlow>
<userTask id="usertask3" name="个人任务">
<extensionElements>
<activiti:taskListener event="create" class="org.pan.activity.bpmnlistern.MyTaskListernOwern"></activiti:taskListener>
<activiti:taskListener event="complete" class="org.pan.activity.bpmnlistern.MyTaskListernOwern"></activiti:taskListener>
</extensionElements>
</userTask>
<sequenceFlow id="flow4" sourceRef="usertask1" targetRef="usertask3">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${isTrue == "false"}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow5" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow>
<userTask id="usertask4" name="分组任务">
<extensionElements>
<activiti:taskListener event="create" class="org.pan.activity.bpmnlistern.MyTaskListernGroup"></activiti:taskListener>
</extensionElements>
</userTask>
<sequenceFlow id="flow6" sourceRef="usertask4" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
<bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="50.0" y="216.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0" x="180.0" y="206.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
<omgdc:Bounds height="55.0" width="105.0" x="362.0" y="100.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="770.0" y="216.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
<omgdc:Bounds height="55.0" width="105.0" x="466.0" y="310.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask4" id="BPMNShape_usertask4">
<omgdc:Bounds height="55.0" width="105.0" x="580.0" y="100.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="85.0" y="233.0"></omgdi:waypoint>
<omgdi:waypoint x="180.0" y="233.0"></omgdi:waypoint>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="14.0" width="24.0" x="85.0" y="233.0"></omgdc:Bounds>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="232.0" y="206.0"></omgdi:waypoint>
<omgdi:waypoint x="414.0" y="155.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="467.0" y="127.0"></omgdi:waypoint>
<omgdi:waypoint x="580.0" y="127.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="232.0" y="261.0"></omgdi:waypoint>
<omgdi:waypoint x="518.0" y="310.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
<omgdi:waypoint x="518.0" y="310.0"></omgdi:waypoint>
<omgdi:waypoint x="787.0" y="251.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
<omgdi:waypoint x="632.0" y="155.0"></omgdi:waypoint>
<omgdi:waypoint x="787.0" y="216.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
activiti视图如下:
pom文件需要添加如下几个包:
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>5.21.0</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>5.21.0</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-diagram-rest</artifactId>
<version>5.21.0</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-modeler</artifactId>
<version>5.21.0</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
activiti有两种监听器,分别是:ExecutionListern主要用于流程的开始、结束和连线的监听,共有三个值:"start"、"end"、"take",其中start和end用于整个流程的开始和结束,take用于连线。TaskListern主要用于节点的监听,共有四个事件:分别是:"create"、"assignment"、"complete"、"delete",当流转到这个节点是触发create事件,当被委托是触发assignment事件,当事件完成时, 因为activity会删除相应数据表中的节点信息所以会同时触发complete和delete事件。
项目中用到的四个监听器代码如下:
/**
*@description executionListern主要用于流程的开始、结束和连线的监听
* 共有三个值:"start"、"end"、"take"。
* 其中start和end用于整个流程的开始和结束,take用于连线
*@auth panmingshuai
*@time 2018年4月5日下午8:59:16
*
*/
public class MyExecutionListern implements ExecutionListener{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void notify(DelegateExecution execution) throws Exception {
//得到现在事件阶段的值,用"start".endsWith(eventName)来判断
String eventName = execution.getEventName();
if("start".endsWith(eventName)){
System.out.println("-------------------流程开始-------------------");
} else if("end".equals(eventName)){
System.out.println("-------------------流程结束-------------------");
}
}
}
/**
*@description
*@auth panmingshuai
*@time 2018年4月5日下午11:18:24
*
*/
public class MyTaskListernBegin implements TaskListener {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void notify(DelegateTask delegateTask) {
//得到现在事件阶段的值,用"create".endsWith(eventName)来判断
String eventName = delegateTask.getEventName();
if("create".equals(eventName)){
System.out.println("-------------------分组任务开始---------------");
//指定组任务审核人员
delegateTask.addCandidateUser("ming1");
delegateTask.addCandidateUser("ming2");
} else if("complete".equals(eventName)){
System.out.println("-------------------分组任务结束---------------");
}
}
}
/**
*@description taskListern主要用于节点的监听
* 共有四个事件:分别是:"create"、"assignment"、"complete"、"delete"。
* 当流转到这个节点是触发create事件,当被委托是触发assignment事件,当事件完成时,
* 因为activity会删除相应数据表中的节点信息所以会触发complete和delete事件
*@auth panmingshuai
*@time 2018年4月5日下午8:59:16
*
*/
public class MyTaskListernGroup implements TaskListener {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void notify(DelegateTask delegateTask) {
//得到现在事件阶段的值,用"create".endsWith(eventName)来判断
String eventName = delegateTask.getEventName();
if("create".equals(eventName)){
System.out.println("-------------------分组任务开始---------------");
//指定组任务审核人员
delegateTask.addCandidateUser("ming1");
delegateTask.addCandidateUser("ming2");
} else if("complete".equals(eventName)){
System.out.println("-------------------分组任务结束---------------");
}
}
}
/**
*@description
*@auth panmingshuai
*@time 2018年4月5日下午9:44:16
*
*/
public class MyTaskListernOwern implements TaskListener {
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public void notify(DelegateTask delegateTask) {
//得到现在事件阶段的值,用"create".endsWith(eventName)来判断
String eventName = delegateTask.getEventName();
if("create".equals(eventName)){
System.out.println("-------------------个人任务开始---------------");
//添加个人任务
delegateTask.setOwner("ming1");
} else if("complete".equals(eventName)){
System.out.println("-------------------个人任务结束---------------");
}
}
}
接下来是activiti的配置,因为activiti需要操作数据表,因此如果要使用activiti必须要配置数据库连接池和事物管理:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxPoolSize" value="${jdbc.maxPoolSize}"/>
<property name="minPoolSize" value="${jdbc.minPoolSize}"/>
<property name="initialPoolSize" value="${jdbc.initialPoolSize}"/>
<property name="acquireIncrement" value="${jdbc.acquireIncrement}"/>
<property name="maxIdleTime" value="${jdbc.maxIdleTime}"/>
</bean>
<!-- 启用注解式事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- 加载activiti引擎 -->
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="true" />
<property name="jobExecutorActivate" value="false" />
</bean>
<!-- activiti的各种服务接口 -->
<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService" />
<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService" />
<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService" />
<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService" />
对于其中activiti的服务接口的作用如下:
RepositoryService 管理流程定义
RuntimeService 执行管理,包括启动、推进、删除流程实例等操作
TaskService 任务管理
HistoryService 历史管理(执行完的数据的管理)
具体使用时需要注入相应的接口:
当然可以不使用这些接口使用processEngine的各种get方法也可以,只是不怎么方便:
3、具体的业务使用:
3.1、首先如果要使用工作流,必须先部署流程:
// 部署流程,只要是符合BPMN2规范的XML文件,理论上都可以被ACTIVITI部署
Deployment deployment = repositoryService.createDeployment().addClasspathResource("org/pan/activity/bpmn/MyProcess.bpmn").deploy();
System.out.println("deployment: id: " + deployment.getId() + "----- name: " + deployment.getName());
return "发布成功";
注:项目启动时,activiti会自动在你连接的数据库生成它所需要的数据表,都是以act_打头的。
一般如果没改流程图的话,也没改数据库的话,只用部署一次就够了。
现在假设一个人提交了一个申请过来,怎么给他一个流程的实例呢?如下:
//开启流程时设置的变量是全局变量
Map<String, Object> variables = new HashMap<>();
variables.put("shuai", "haha");
// 开启流程,myprocess是流程的ID,每个流程图都有自己专属的id,businessid代表每个具体的业务流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess", businessId, variables);
Task activityTask = taskService.createTaskQuery().processInstanceId(processInstance.getId()).taskDefinitionKey(processInstance.getActivityId()).singleResult();
System.out.println("processInstance: id: " + processInstance.getId() + "----- activityTaskId: " + activityTask.getId());
return "开启成功";
这便开启了一个专属的流程实例给某一个业务,值得注意的是processInstance.getId()这个id是activiti自己生成的id,businessId是我们自己的业务id,但是这个可以不用设置,这看项目的需要,我们可以拥有自己的业务申请表,但是也可以把申请的内容放到variables中,需要的时候再查出来,这样就不用再建立自己的申请表了。
流程开始了就需要处理任务了,但是有谁来处理任务呢?
上面的监听器里其实就有代码,需要注意的是当任务的creat事件被触发时设置处理人,设置的处理人也只能处理这个任务,当然还有其他设置处理人的方式,但是我觉得这种方式最好。另外delegateTask.addCandidateUser("ming1");这个是设置组任务处理人的。
delegateTask.setOwner("ming1");这个是设置个人任务的。所谓的组任务是指这个组里的人都会看到这个任务,个人任务是指只有这个人会看到这个任务。
任务处理人设置好了,那一个人怎么看到自己的任务呢?
// 个人任务查询
List<Task> taskList = taskService.createTaskQuery().taskAssignee(userName).list();
for(Task task : taskList){
System.out.println("assignee: " + task.getAssignee() + "---- taskName:" + task.getName() + "---- taskId: " + task.getId());
}
return taskList.size() + "";
// 组任务查询
List<Task> taskList = taskService.createTaskQuery().taskCandidateUser(userName).list();
for(Task task : taskList){
System.out.println("assignee: " + task.getAssignee() + "---- taskName:" + task.getName() + "---- taskId: " + task.getId());
}
return taskList.size() + "";
需要注意的一点是组任务查询时,只要是这个组的组员的人都会看到所有的组任务,但是组任务怎么确定是谁来处理的这个任务呢?这个时候就需要将这个任务指定了,比如说一个组员说这个任务归他了,那个我们就得把任务派发给他,不然不知道是谁搞定的。如下指定:
taskService.claim(taskId, userName);
好了任务也查好了,我该怎么处理呢:
String val = (String) taskService.getVariable(taskId, "isTrue");
String shuai = (String) taskService.getVariable(taskId, "shuai");
System.out.println(shuai);
System.out.println(val);
//这里设置的变量是局部变量,只对下一个节点有用
Map<String, Object> variables = new HashMap<>();
variables.put("isTrue", isTrue);
taskService.complete(taskId, variables);
return "任务:" + taskId + " 完成";
这里的getVariable方法是获取从上一个节点传过来的参数,taskService调用complete方法完成节点,这个时候会触发该节点的complete和delete事件以及下一个节点的create事件。complete方法中的variables是传到下一个节点的参数。需要特别注意的是,启动流程实例时startProcessInstanceByKey方法中的variables参数是全局的,你可以在任意一个节点调到他们的值。其中有一个用处就是:比如你要限制一个申请被打回的次数,你可以用它。
好了任务完成了,我们怎么看这个流程走到了哪里呢?
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceBusinessKey(businessId).singleResult();
Task activityTask = taskService.createTaskQuery().processInstanceId(processInstance.getId()).taskDefinitionKey(processInstance.getActivityId()).singleResult();
System.out.print("----- activityTaskName: " + activityTask.getName());
System.out.println("----- isEnded:" + processInstance.isEnded());
return "查询成功";
其中activityTask即为当前流程所在的任务节点。
那怎么查一个任务经历了哪些节点呢?
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessId).singleResult();
List<HistoricTaskInstance> taskInstances = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstance.getId()).orderByTaskCreateTime().asc().list();
for(HistoricTaskInstance taskInstance : taskInstances){
System.out.print("taskId:" + taskInstance.getId());
System.out.print("---taskName:" + taskInstance.getName());
System.out.print("---assignee:" + taskInstance.getAssignee());
System.out.print("---startTime:" + taskInstance.getStartTime());
System.out.print("---endTime:" + taskInstance.getEndTime());
System.out.println("---duration:" + taskInstance.getDurationInMillis());
}
return "查询成功";
怎么查一个人处理了哪些任务呢?
List<HistoricTaskInstance> taskInstances = historyService.createHistoricTaskInstanceQuery().taskAssignee(userName).list();
for(HistoricTaskInstance taskInstance : taskInstances){
System.out.print("taskId:" + taskInstance.getId());
System.out.print("---taskName:" + taskInstance.getName());
System.out.print("---assignee:" + taskInstance.getAssignee());
System.out.print("---startTime:" + taskInstance.getStartTime());
System.out.print("---endTime:" + taskInstance.getEndTime());
System.out.println("---duration:" + taskInstance.getDurationInMillis());
}
基本上一个项目中就用到了这些流程的功能,基本满足了,完毕