Activiti_流程定义语言(BPMN)

1、什么是BPMN

业务流程建模与标注(Business Process Model and Notation,BPMN) ,描述流程的基本符号,包括这些图元如何组合成一个业务流程图(Business Process Diagram)

Eclispse画出流程,有两个文件bpmn文件和png文件,其中bpmn文件又可以叫做流程定义文件,它需要遵循BPMN语言规范.png:就是一个单纯的图片,没有任何作用.

2、流程(process)

bpmn文件一个流程的根元素。一个流程就代表一个工作流。

3、顺序流

3.1、什么是顺序流

顺序流是连接两个流程节点的连线,代表一个节点的出口。流程执行完一个节点后,会沿着节点的所有外出顺序流继续执行。 就是说,BPMN 2.0默认的行为就是并发的: 两个外出顺序流会创造两个单独的,并发流程分支。

顺序流主要由4个属性组成:

Id: 唯一标示,用来区分不同的顺序流

sourceRef:连线的源头节点ID

targetRef:连线的目标节点ID

name(可选):连线的名称,不涉及业务,主要用于显示。多出口原则要设置。

       说明:

1)结束节点没有出口

其他节点有一个或多个出口。如果有一个出口,则代表是一个单线流程;如果有多个出口,则代表是开启并发流程

3.2、分支流程-流程图

3.3、公共代码抽取

package cn.itsource.activiti.day02;

import org.activiti.engine.ProcessEngine;

import org.activiti.engine.ProcessEngines;

import org.activiti.engine.RepositoryService;

import org.activiti.engine.RuntimeService;

import org.activiti.engine.TaskService;

import org.activiti.engine.repository.Deployment;

import org.activiti.engine.repository.DeploymentBuilder;

import org.activiti.engine.runtime.ProcessInstance;

import org.activiti.engine.task.Task;


public class BaseBpmn {

         private ProcessEngine processEngine=ProcessEngines.getDefaultProcessEngine();

         //自己类和子类都可能使用:使用protected修饰

         protected RepositoryService repositoryService = processEngine.getRepositoryService();

         protected RuntimeService runtimeService = processEngine.getRuntimeService();

         protected TaskService taskService = processEngine.getTaskService();

        

         /**

          *

          * @param name 部署流程的名字

          * @param resourceName  加载资源的名字前缀

          * @return

          */

         protected Deployment deploy(String name, String resourceName) {

                   //创建核心

                   //获取服务

                   //做事情

                   // this.getClass().getClassLoader().getResourceAsStream("LeaveFlow.bpmn");从classpath下面加载

                   // this.getClass().getResourceAsStream("/LeaveFlow.bpmn");//从classpath下面加载

                   // this.getClass().getResourceAsStream("LeaveFlow.bpmn");//从当前类当前包加载(采纳)

                   // this.getClass().getResourceAsStream("./LeaveFlow.bpmn");//从当前类当前包加载(采纳)

                   String resourceNameBpmn=resourceName+".bpmn";

                   String resourceNamePng=resourceName+".png";

                   DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();

                   deploymentBuilder.name(name)

                                                        .addInputStream(resourceNameBpmn, this.getClass().getResourceAsStream(resourceNameBpmn))

                                                        .addInputStream(resourceNamePng, this.getClass().getResourceAsStream(resourceNamePng));

                   Deployment deployment = deploymentBuilder.deploy();

                   return deployment;

         }



         /**

          *

          * @param processDefinitionKey  启动流程的定义的key

          */

         protected ProcessInstance startProcess(String processDefinitionKey) {

                  

                   return runtimeService.startProcessInstanceByKey(processDefinitionKey);

         }

        

         /**

          * 在一个流程实例中,一个办理人只有一个唯一的任务

          * @param processInstanceId 流程实例id

          * @param assignee  办理人

          * @return

          */

         protected Task queryPersonalTask(String processInstanceId, String assignee) {

         return       taskService.createTaskQuery()

                   .processInstanceId(processInstanceId)

                   .taskAssignee(assignee)

                   .singleResult();

         }

}

 

3.4、分支流程-测试代码

3.4.1、辅助代码:

@Test

         public void  deployTest() throws Exception {

                   Deployment deployment = deploy("报销申请","SequesceFlowTest");

                   System.out.println("deploymentId:"+deployment.getId());

         }

        

         @Test

         public void  startProcessTest() throws Exception {

                            String processDefinitionKey="SequesceFlowTest";

                            ProcessInstance processInstance = startProcess(processDefinitionKey);

                            System.out.println("ProcessInstanceId:"+processInstance.getId());//2501

         }

3.4.2、测试驳回:

//测试驳回

         /**

          * ①:先完成报销申请,

          * ②:走到审批的时候,设置一个flag的流程变量为flase,驳回

          * ③:回到①,在完成报销申请

          * ④:审批人又得到审批审批任务

          * @throws Exception

          */

         @Test

         public void  notPassTest() throws Exception {

                   //①:先完成报销申请,

                   String processInstanceId="2501";

                   String assignee="小明";

                   Task applyTask = queryPersonalTask(processInstanceId,assignee);

                   System.out.println("获取申请任务:"+applyTask);

                   //先完成报销申请

                   taskService.complete(applyTask.getId());

                  

                    assignee="小刚";

                  Task approveTask = queryPersonalTask(processInstanceId,assignee);

                   System.out.println("获取审批任务:"+applyTask);

                   // ②:走到审批的时候,设置一个flag的流程变量为flase

                   taskService.setVariable(approveTask.getId(),"flag", "false");

                   //驳回

                   taskService.complete(approveTask.getId());

                  

                   //④:审批人又得到审批审批任务

                    assignee="小明";

                    applyTask = queryPersonalTask(processInstanceId,assignee);

                   System.out.println("再次获取申请任务:"+applyTask);

                  

         }

3.4.3测试通过:

/**

          * 通过

          * @throws Exception

          */

         @Test

         public void  passTest() throws Exception {

                   String processInstanceId="2501";

                   String assignee="小刚";

                   Task approveTask = queryPersonalTask(processInstanceId,assignee);

                   System.out.println("获取审批任务:"+approveTask);

                   // ②:走到审批的时候,设置一个flag的流程变量为flase

                   taskService.setVariable(approveTask.getId(),"flag", "true");

                   //通过

                   taskService.complete(approveTask.getId());

         }

4、节点

开始节点

结束节点:加一个执行监听器,修改流程的状态

任务节点

网关节点

监听器节点

4.1、开始事件节点(startEvent)

开始事件对应节点

4.2、结束事件节点(endEvent)

结束事件对应节点

4.3、任务节点 (Task)

 4.3.1、接收任务节点 (receiveTask)

接收任务是一个简单任务,它会等待对应消息的到达。 当前,官方只实现了这个任务的java语义。 当流程达到接收任务,流程状态会保存到数据库中。

在任务创建后,意味着流程会进入等待状态, 直到引擎接收了一个特定的消息, 这会触发流程穿过接收任务继续执行。(短跑比赛一样,预备,信号触发跑)

图形标记:

接收任务显示为一个任务(圆角矩形),右上角有一个消息小标记。 消息是白色的(黑色图标表示发送语义):

1、流程图

销售经理统计当前的营业额,然后短信发送给老板

2、测试代码

公共代码增加:

/**

          * 通过流程实例Id获取流程实例

          * @param processInstanceId

          * @return

          */

         protected ProcessInstance queryProcessInstanceById(String processInstanceId) {

                   return runtimeService.createProcessInstanceQuery()

                                                  .processInstanceId(processInstanceId)

                                                  .singleResult();

         }

         /**

          * 在一次流程中通过活动id查询唯一执行对象

          *     * @param pid

          * @param calcTotalPriceActivityId

          * @return

          */

         protected Execution queryExecution(String pid, String calcTotalPriceActivityId) {

                   return runtimeService.createExecutionQuery()

                                                  .processInstanceId(pid)

                                                  .activityId(calcTotalPriceActivityId)

                                                  .singleResult();

         }

第一个节点:今日销售额计算

// 第一个节点:计算今日销售额

         @Test

         public void testCalcTotalPrice() throws Exception {

                   // 0 查询到"计算今日销售额"的执行对象

                   String pid = "2501";

                   String calcTotalPriceActivityId = "当天营业额Id";

                   Execution calcTotalPriceExecution = queryExecution(pid, calcTotalPriceActivityId);

                   System.out.println("获取当天营业额的执行对象!" + calcTotalPriceExecution.getActivityId());

                   // 1 计算今日销售额

                   double totalPrice = 666666.66666d;

                   System.out.println("计算今日销售额为:" + totalPrice);

                   // 2 把销售额放入流程变量,共享给下一个节点

                   Map<String, Object> processVariables = new HashMap<>();

                   processVariables.put("totalPrice", totalPrice);

                   // 3 发消息触发"计算今日销售额"执行对象往下走

                   System.out.println("设置流程变量,并让它往下走!");

                   runtimeService.signal(calcTotalPriceExecution.getId(), processVariables);


                   // 4 可以获取下一个节点对应的执行对

                   String sendMsgActivityId = "短信发送给老板Id";

                   Execution sendMsgExecution = queryExecution(pid, sendMsgActivityId);

                   System.out.println("获取到第二个节点:" + sendMsgExecution.getActivityId());

                   if (sendMsgExecution != null) {

                            System.out.println("第一个节点已经处理完毕!");

                   }

         }

第二个节点:短信发送给老板

@Test

         public void testSendMsg() throws Exception {

                   String pid = "2501";

                   String sendMsgActivityId = "短信发送给老板Id";

                   Execution sendMsgExecution = queryExecution(pid, sendMsgActivityId);

                   // 1 从流程变量种获取销售额

                   String executionId = sendMsgExecution.getId();

                   Double totalPrice = runtimeService.getVariable(executionId, "totalPrice", Double.class);

                   System.out.println("从流程变量中获取今日销售额:" + totalPrice);

                   // 2 把销售额发送给老板

                   System.out.println("发送短信给老板:今日收获不错,营业额为" + totalPrice);

                   // 3 让流程继续往下走

                   runtimeService.signal(executionId);

                   // 4 判断流程结束

                   ProcessInstance processInstance = queryProcessInstanceById(pid);

                   if (processInstance == null) {

                            System.out.println("流程已结束!");

                   }

         }

 

 4.3.2用户任务节点 (userTask)

用户任务用来设置必须由人员完成的工作。 当流程执行到用户任务,会创建一个新任务, 并把这个新任务加入到分配人或群组的任务列表中。

图形标记

用户任务显示成一个普通任务(圆角矩形),左上角有一个小用户图标。

任务分配

用户任务的办理都需要人工的参与。用户任务可以分为两大类。私有任务(待办任务)和公有任务(可接任务)。

私有任务

私有任务即有直接分配给指定用户的任务。只有一个用户可以成为任务    的执行者。 在activiti中,用户叫做执行者。 拥有执行者的用户任务  (即私有任务)对用户是不可见的。

 

办理者指定分三种方式:

  • 写死
  • 流程变量
  • 监听器

公共代码增加:
        

 /**

          * 通过流程定义key启动流程并且设置流程变量

          * @param processDefinitionKey

          * @return

          */

         protected ProcessInstance startProcess(String processDefinitionKey,Map<String, Object> variables) {

                   return runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);

         }

私有任务测试:
 

/**

 *    私有任务测试

 *     办理者指定:

 *        1)写死,设计流程时写死办理者(不用)

 *           缺点:这个流程只能由写死哪个人申请

 *        2)流程变量设置--第一个节点

 *            ①设计流程时要从流程变量中获取办理者

 *            ②流程执行时要设置对应流程变量-启动时设置

 *          问题:如果第二个-n个节点通过在启动流程时设置变量来指定办理者,

 *              第一个办理长时间没有办理,在这期间后面节点办理者离职了,走下去再离职人员手上还会挂任务.

 *             方案1:下一个任务办理时再指定下一个任务办理者.不好,业务逻辑耦合

 *             方案2:使用监听器设置下一个节点启动时指定人,不需要耦合.(采纳)

 *        3)监听器--第二个-n个节点

 *           ①写一个类实现监听器类-TaskListener

 *           ②把写好类绑定到对应节点

 *           ③实现监听器逻辑

 * @author Administrator

 *

 */

public class PersonalTaskTest extends BaseBpmnTest {

         //写死

         @Test

         public void test1() {

                   //部署

                   deploy("私有任务测试1", "PersonalTaskTest");

                   //启动

                   String processDefinitionKey = "PersonalTaskTest";

                   ProcessInstance pi = startProcess(processDefinitionKey );

                   //获取写死办理的任务

                   String assignee = "小鑫鑫";

                   Task task = queryPersonalTask(pi.getId(), assignee );

                   System.out.println("id:"+task.getId()

                   +",name:"+task.getName()+",assignee:"+task.getAssignee()); 

         }

         @Test

         public void test2() {

                   //部署

                   //deploy("私有任务测试2", "PersonalTaskTest2");

                   //启动时要设置第一个节点的办理者

                   String processDefinitionKey = "PersonalTaskTest2";

                  

                   Map<String, Object> variables = new HashMap<>();

                   //第一个节点办理者

                   String assignee = "晓鑫鑫";//当前登录用户名称

                   variables.put("apply", assignee);

                   ProcessInstance pi = startProcess(processDefinitionKey,variables );

            System.out.println("pid:"+pi.getId());

                   //获取写死办理的任务

                   Task task = queryPersonalTask(pi.getId(), assignee );

                   System.out.println("id:"+task.getId()

                   +",name:"+task.getName()+",assignee:"+task.getAssignee());

         }

         //要把第一个节点走下去

         /**

          * 使用流程变量设置

          * @throws Exception

          */

         @Test

         public void test3Pre() throws Exception {

                   //部署

                   //deploy("私有任务测试3", "PersonalTaskTest3");

                   //启动时要设置第一个节点的办理者

                   String processDefinitionKey = "PersonalTaskTest3";

                  

                   Map<String, Object> variables = new HashMap<>();

                   //第一个节点办理者

                   String assignee = "逗比鑫";//当前登录用户名称

                   variables.put("apply", assignee);

                   ProcessInstance pi = startProcess(processDefinitionKey,variables );

            System.out.println("pid:"+pi.getId());

                   //获取写死办理的任务

                   Task task = queryPersonalTask(pi.getId(), assignee );

                   System.out.println("id:"+task.getId()

                   +",name:"+task.getName()+",assignee:"+task.getAssignee());

                   taskService.complete(task.getId());

         }

         //上一个任务完成设置下一个任务的办理者,没法做,没有办法直接获取下一个任务并设置

         //当前任务创建时,再指定办理者

         @Test

         public void test3() throws Exception {

                   String assignee = "逗比鑫"+"审批人";

                   String processInstanceId = "5001";

                   Task task = queryPersonalTask(processInstanceId, assignee );

                   System.out.println("id:"+task.getId()

                   +",name:"+task.getName()+",assignee:"+task.getAssignee());

                   //完成第二个任务

                   taskService.complete(task.getId());

                   //判断流程是否结束

                   ProcessInstance pi = queryProcessInstanceById(processInstanceId);

                   if (pi == null) {

                            System.out.println("流程结束");

                   }

         }


}

 

 

在下一个节点创建时,增加这个监听器:

import org.activiti.engine.delegate.DelegateTask;

import org.activiti.engine.delegate.TaskListener;


/**

 * ①写一个类实现监听器类-TaskListener

 * ②把写好类绑定到对应节点

 * ③实现监听器逻辑

 * @author Administrator

 *

 */

public class ManagerSettingListener implements TaskListener{

         @Override

         public void notify(DelegateTask delegateTask) {

                   System.out.println(delegateTask.getName());

                   //1 获取申请人

                   String apply = delegateTask.getVariable("apply", String.class);

                   //2 通过申请人获取审批人  通过申请人获取部门经理

                   String manager = apply+"审批人";

                   //3 设置办理者

                   delegateTask.setAssignee(manager);

                   //delegateTask.addCandidateUser(userId);

                   //delegateTask.addCandidateGroup(groupId);
         }
}

公共任务

有的用户任务在指派时无法确定具体的办理者,这时任务也可以加入到人员的候选任务列表中,然后让这些人员选择性认领和办理任务。

公有任务的分配可以分为指定候选用户和候选组两种。

每一种都要三种指定方式

  • 写死(不用的)
  • 流程变量指定第一个节点
  • 任务监听器指定第2-n节点

a) 把任务添加到一批用户的候选任务列表中,使用candidateUsers      属    性,XML内容如下:

<userTaskid="theTask"name="my task"activiti:candidateUsers="sirius,kermit"/>

candidateUsers属性内为用户的ID,多个用户ID之间使用(半角)逗号间隔。

 

b)把任务添加到一个或多个候选组下,这时任务对组下的所有用户可     见,首先得保证每个组下面有用户,通过IdentityService对象创建用户  和组,然后把用户添加到对应的组下,java代码如下:

 

上述两个虽然都可以统称为任务节点,但是还是有本质区别:

  1. receiveTask主要代表机器自动执行的,userTask代表人工干预的。
  2. receiveTask任务产生后会在act_ru_execution表中新增一条记录,    而userTask产生后会在act_ru_execution和act_ru_task(主要记录任    务的发布时间,办理人等信息)中各产生一条记录。

receiveTask任务提交方式使用RuntimeService的signal方法提交,     userTask任务提交方式使用TaskService的complete方法提交。

5、网关节点

网关用来控制流程的流向。

网关显示成菱形图形,内部有有一个小图标。 图标表示网关的类型。

 

5.1、排他网关(ExclusiveGateWay)

简单理解有多个分支,但是只能走一个分支

排他网关(也叫异或(XOR)网关,或更技术性的叫法 基于数据的排他网关), 用来在流程中实现决策。

图形标记

排他网关显示成一个普通网关(比如,菱形图形), 内部是一个“X”图标,表示异或(XOR)语义。 注意,没有内部图标的网关,默认为排他网关。 BPMN 2.0规范不允许在同一个流程定义中同时使用没有X和有X的菱形图形。

XML内容

排他网关的XML内容是很直接的:用一行定义了网关, 条件表达式定义在外出顺序流中。 参考条件顺序流 获得这些表达式的可用配置。

它对应的XML内容如下:

<exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway" default="财务"></exclusiveGateway>

<sequenceFlow id="财务" name="小于1000" sourceRef="exclusivegateway1" targetRef="usertask2">

      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${price<1000}]]></conditionExpression>

    </sequenceFlow>

    <userTask id="usertask3" name="部门经理审批"></userTask>

    <sequenceFlow id="部门经理" name="大于1000小于10000" sourceRef="exclusivegateway1" targetRef="usertask3">

      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${price>1000&&price<10000}]]></conditionExpression>

    </sequenceFlow>

    <userTask id="usertask4" name="老板审批"></userTask>

    <sequenceFlow id="老板" name="大于10000" sourceRef="exclusivegateway1" targetRef="usertask4">

      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${price>10000}]]></conditionExpression>

    </sequenceFlow>

说明:

1.一个排他网关对应一个以上的顺序流

2.由排他网关流出的顺序流都有个conditionExpression元素,在内部维护返回boolean类型的决策结果。

3.决策网关只会返回一条结果。当流程执行到排他网关时,流程引擎会自动检索网关出口,从上到下检索如果发现第一条决策结果为true或者没有设置条件的(默认为成立),则流出。

4.如果没有任何一个出口符合条件则抛出异常。

5.2、并行网关(parallelGateWay)

多个分支要一起执行完,才算完成(会签)

网关也可以表示流程中的并行情况。最简单的并行网关是parallelGateWay,它允许将流程 分成多条分支,也可以把多条分支 汇聚到一起。

图形标记

并行网关显示成一个普通网关(菱形)内部是一个“加号”图标, 表示“与(AND)”语义。

 

XML内容

定义并行网关只需要一行XML:

<parallelGateway id="parallelgateway1" name="Parallel Gateway"></parallelGateway>

实际发生的行为(分支,聚合,同时分支聚合), 要根据并行网关的顺序流来决定。

参考如下代码:

<startEvent id="theStart"/>

<sequenceFlow id="flow1"sourceRef="theStart"targetRef="fork"/>      <parallelGatewayid="fork"/>         <sequenceFlowsourceRef="fork"targetRef="receivePayment"/>    <sequenceFlowsourceRef="fork"targetRef="shipOrder"/>   

<userTaskid="receivePayment"name="Receive Payment"/>    <sequenceFlowsourceRef="receivePayment"targetRef="join"/>   

<userTaskid="shipOrder"name="Ship Order"/>    <sequenceFlowsourceRef="shipOrder"targetRef="join"/>              <parallelGatewayid="join"/>                 <sequenceFlowsourceRef="join"targetRef="archiveOrder"/>  

<userTaskid="archiveOrder"name="Archive Order"/>    <sequenceFlowsourceRef="archiveOrder"targetRef="theEnd"/>          

<endEventid="theEnd"/>

上面例子中,流程启动之后,会创建两个任务:

ProcessInstancepi =runtimeService.startProcessInstanceByKey("forkJoin");

TaskQueryquery=taskService.createTaskQuery()                                                                          .processInstanceId(pi.getId())                                                              .orderByTaskName().asc();

List<Task>tasks =query.list();
assertEquals(2,tasks.size());                                                             Task task1 =tasks.get(0);
assertEquals("Receive Payment",task1.getName());Tasktask2 =tasks.get(1);
assertEquals("Ship Order",task2.getName());

当两个任务都完成时,第二个并行网关会汇聚两个分支,因为它只有一条外出连线, 不会创建并行分支, 只会创建归档订单任务。

说明:

1.并行网关的功能是基于进入和外出的顺序流的:

分支(fork): 并行后的所有外出顺序流,为每个顺序流都创建一个并发分支。

汇聚(join): 所有到达并行网关,在此等待的进入分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关。

2.并行网关的进入和外出都是使用相同节点标示

3.如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。

4.并行网关不会解析条件。 即使顺序流中定义了条件,也会被忽略。

并行网关不需要是“平衡的”(比如, 对应并行网关的进入和外出节点数目相等)。如图中标示是合法的:

6、监听器(Listener)

在流程中我们有时会对整个流程或者一个节点的某种状态做出相应的处理。这时就会用到监听器。

在Activiti中流程的监听主要分为两大类,执行监听器和任务监听器。

任务监听器:只能绑定到用户任务节点。 可用于用户节点办理者的动态设置,操作日志

执行监听器:可以在任何节点绑定。修改流程结束状态(在结束节点绑定执行监听器执行修改),操作日志等

6.1、执行监听器(ExecutionListener)

执行监听器可以执行外部java代码或执行表达式,当流程定义中发生了某个事件。 可以捕获的事件有:

  1. 流程实例的启动和结束。
  2. 选中一条连线。
  3. 节点的开始和结束。
  4. 网关的开始和结束。
  5. 中间事件的开始和结束。
  6. 开始时间结束或结束事件开始。

现在有这样一个简单流程,只包含开始、结束、接收任务和用户任务4个节点:

 

配置监听器,XML代码如下:

说明:

任务监听器支持以下属性:

  1. event(必选):任务监听器会被调用的任务类型。 可能的类型为:
  2. start:流程节点创建后触发。
  3. end:当任务完成,并尚未从运行数据中删除时触发。
  4. take:任务完成后,流程流出时触发
  5. class:必须调用的代理类。 这个类必须实现org.activiti.engine.delegate.

ExecutionListener接口。实现类代码如下: 

执行监听器配置可以放在以下三个地方,如图:

a)监听整个流程的启动和结束状态,配置为process节点的子元素,如图①

b)监听一个节点的启动和结束状态,配置为一个节点的子元素,如图②和③

c)监听一条连线的执行,配置在sequenceFlow节点的内部,只有task一种事件,如图④

启动流程测试代码如下:

结果如下:

6.2、任务监听器(TaskListener)

任务监听器可以在发生对应的任务相关事件时执行自定义java逻辑 或表达式。

任务监听器只能添加到流程定义中的用户任务中。在之前任务节点上添加任务监听:

任务监听器可以在发生对应的任务相关事件时执行自定义java逻辑 或表达式。

任务监听器只能添加到流程定义中的用户任务中。在之前任务节点上添加任务监听:

说明:

任务监听器支持以下属性:

  1.  event(必选):任务监听器会被调用的任务类型。 可能的类型为:
  2. create:任务创建并设置所有属性后触发。
  3. assignment:任务分配给一些人时触发。 当流程到达userTask,assignment事件 会在create事件之前发生。 这样的顺序似乎不自然,但是原因很简单:当获得create时间时, 我们想获得任务的所有属性,包括执行人。
  4.  complete:当任务完成,并尚未从运行数据中删除时触发。
  5. class:必须调用的代理类。 这个类必须实现org.activiti.engine.delegate.TaskListener 接口。Java代码如下:

运行测试代码得到结果:

流程结束,日志内容为:[Start start, Receive Task start, Receive Task end,    Receive Task take, User Task start, User Task assignment, User Task create,      User Task complete, User Task end, End end]

新添加的任务监听包裹在executionListener监听的内部,顺序为:execution Start--> task Assignment-->task Create-->task Complete-->execution End-->execution take。

猜你喜欢

转载自blog.csdn.net/weixin_42560234/article/details/84669352