乐趣区

关于工作流:Activiti中工作流的生命周期详细解析一个BPMN流程示例带你认识项目中流程的生命周期

BPMN 2.0 介绍

  • 业务流程模型注解 (BusinessProcess Modeling Notation – BPMN) 是业务流程模型的一种规范图形注解. 这个规范是由对象治理组 (Object Management Group – OMG) 保护的
  • BPMN 标准的 2.0 版本容许增加准确的技术细节在 BPMN 的图形和元素中, 同时制订 BPMN 元素的执行语法. 通过应用 XML 语言来指定业务流程的可执行语法,BPMN 标准曾经演变为业务流程的语言, 能够执行在任何兼容 BPMN2 的流程引擎中, 同时仍然能够应用弱小的图形注解
  • 简略来说,BPMN 即图标与标签的联合

    定义一个流程

  • 创立一个新的 XML 文件并命名, 确认文件后缀为 .bpmn20.xml.bpmn, 否则引擎无奈公布
  • BPMN 2.0 根节点是 definitions 节点. 这个元素中, 能够定义多个流程定义(不过倡议每个文件只蕴含一个流程定义, 能够简化开发过程中的保护难度)
  • 一个空的流程定义如下所示:留神 definitions 元素起码也要蕴含 xmlns 和 targetNamespace 的申明

    • targetNamespace 能够是任意值, 它用来对流程实例进行分类

      <definitions
      xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
      xmlns:activiti="http://activiti.org/bpmn"
      targetNamespace="Examples">
      
      <process id="myProcess" name="My First Process">
      ..
      </process>
      
      </definitions>
  • 能够抉择增加线上的 BPMN 2.0 格局地位:

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL
                      http://www.omg.org/spec/BPMN/2.0/20100501/BPMN20.xsd
  • ==process 元素有两个属性:==
  • id: 这个属性是必须的, 对应着 Activiti ProcessDefinition 对象的 key 属性.id能够用来启动流程定义的流程实例, 通过 RuntimeServicestartProcessInstanceByKey办法

    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess");

    == 留神:== 它和 startProcessInstanceById 办法不同: 这个办法冀望应用 Activiti 引擎在公布时主动生成的 id. 能够通过调用processDefinition.getId() 办法取得这个值, 生成的 id 的格局为 key:version, 最大长度 限度为 64 个字符, 如果在启动时抛出了一个ActivitiException: 阐明生成的 id 太长了, 须要限度流程的 key 的长度

  • name: 这个属性是可选的, 对应 ProcessDefinitionname属性. 引擎本人不会应用这个属性, 是用来在用户接口显示便于浏览的名称

    BPMN 流程示例前提

  • 曾经装置 Activiti 并且可能运行 Activiti Demo
  • 应用了独立运行的 H2 服务器
  • 批改 db.properties, 设置其中的 jdbc.url=jdbc:h2:tcp://localhost/activiti, 而后启动独立服务器

    指标

  • 学习 Activiti 和一些根本的 BPMN 2.0 概念
  • 最终后果是一个简略的 Java SE 程序能够公布流程定义, 通过 Activiti 引擎 API 操作流程
  • 应用一些 Activiti 相干的工具, 构建本人的业务流程 web 利用

    用例

  • 每个月都要给公司领导一个金融报表, 由会计部门负责
  • 当报表实现时, 一个上级领导须要审批文档, 而后能力发给所有领导

    流程图

  • 流程的图形化 BPMN 2.0 标记:

    空开始事件(左侧圆圈), 前面是两个用户工作: 制作月度财报和验证月度财报, 最初是空完结事件(右侧粗线圆圈)

    XML 内容

  • 在业务流程的 XML 中很容易找到 流程的次要元素:

    • (空)开始事件是流程的入口
    • 用户工作是流程中与操作者相干的工作申明:

      • 第一个任务分配给 accountancy 组
      • 第二个任务分配给 management 组
    • 当流程达到空完结事件就会完结
    • 这些元素都应用连线连贯, 这些连线领有 sourcetarget属性, 定义了连线的方向

      <definitions id="definitions"
      targetNamespace="http://activiti.org/bpmn20"
      xmlns:activiti="http://activiti.org/bpmn"
      xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
      
          <process id="financialReport" name="Monthly financial report reminder process">
      
            <startEvent id="theStart" />
      
            <sequenceFlow id='flow1' sourceRef='theStart' targetRef='writeReportTask' />
      
            <userTask id="writeReportTask" name="Write monthly financial report" >
              <documentation>
                Write monthly financial report for publication to shareholders.
              </documentation>
              <potentialOwner>
                <resourceAssignmentExpression>
                  <formalExpression>accountancy</formalExpression>
                </resourceAssignmentExpression>
              </potentialOwner>
            </userTask>
      
            <sequenceFlow id='flow2' sourceRef='writeReportTask' targetRef='verifyReportTask' />
      
            <userTask id="verifyReportTask" name="Verify monthly financial report" >
              <documentation>
                Verify monthly financial report composed by the accountancy department.
                This financial report is going to be sent to all the company shareholders.
              </documentation>
              <potentialOwner>
                <resourceAssignmentExpression>
                  <formalExpression>management</formalExpression>
                </resourceAssignmentExpression>
              </potentialOwner>
            </userTask>
      
            <sequenceFlow id='flow3' sourceRef='verifyReportTask' targetRef='theEnd' />
      
            <endEvent id="theEnd" />
      
          </process>
      
      </definitions>

      启动一个流程实例

  • 创立好业务流程的流程定义, 就能够创立流程实例
  • 一个流程实例对应了特定月度财报的创立和审批, 所有流程实例都共享同一个流程定义
  • 为了应用流程定义创立流程实例, 首先要公布业务流程:

    • 流程定义会保留到长久化的数据存储里, 是为 Activiti 引擎特地配置的. 所以部署好业务流程, 在引擎重启后还能找到流程定义
    • BPMN 2.0 流程文件会解析成 内存对象模型, 能够通过 Activiti API 操作
  • 通过上面的 API 公布流程, 所有与 Activiti 引擎的交互都是通过 services

    Deployment deployment = repositoryService.createDeployment()
    .addClasspathResource("FinancialReportProcess.bpmn20.xml")
    .deploy();
  • 启动一个新流程实例, 应用咱们定义在流程定义里的 id(对应 XML 文件中的 process 元素).== 留神这里的 id 对于 Activiti 来说, 应该叫做 key==, 个别在流程模型中应用的 ID, 在 Activiti 中都是 Key: 比方工作 ID

    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("financialReport");
  • 这样创立一个流程实例:

    • 首先进入开始事件
    • 开始事件之后, 它会沿着所有的外出连线执行, 达到第一个工作(“制作月度财报”)
    • Activiti 会把一个工作保留到数据库里. 这时, 调配到这个工作的用户或群组会被解析, 也会保留到数据库里
    • 须要留神,Activiti 引擎会继续执行流程的环节, 除非遇到一个 期待状态: 比方用户工作
    • 在期待状态下, 以后的流程实例的状态会保留到数据库中. 直到用户决定实现工作能力扭转这个状态
    • 这时, 引擎会继续执行, 直到遇到下一个期待状态, 或流程完结
    • 如果两头引擎重启或解体, 流程状态也会平安的保留在数据库里
  • 工作创立之后,startProcessInstanceByKey会在达到用户工作这个期待状态之后才会返回. 这时, 任务分配给了一个组, 这意味着这个组是执行这个工作的候选组
  • 当初将所有货色都放在一起, 来创立一个简略的 java 程序:

    • 创立一个 Java 我的项目, 把 Activiti 的 jar 和依赖放到 classpath 下: 这些都能够在 Activiti 公布包的 libs 目录下找到
    • 在调用 Activiti 服务之前, 咱们必须结构一个 ProcessEngine, 能够让咱们拜访服务
    • 这里咱们应用 [独自运行] 的配置, 这会应用 demo 装置时的数据库来构建 ProcessEngine

      public static void main(String[] args) {
      
      // Create Activiti process engine
      ProcessEngine processEngine = ProcessEngineConfiguration
      .createStandaloneProcessEngineConfiguration()
      .buildProcessEngine();
      
      // Get Activiti services
      RepositoryService repositoryService = processEngine.getRepositoryService();
      RuntimeService runtimeService = processEngine.getRuntimeService();
      
      // Deploy the process definition
      repositoryService.createDeployment()
      .addClasspathResource("FinancialReportProcess.bpmn20.xml")
      .deploy();
      
      // Start a process instance
      runtimeService.startProcessInstanceByKey("financialReport");
      }

      工作列表

  • 能够通过 TaskService 来取得工作, 增加以下逻辑:

    List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("kermit").list();
  • 留神传入的用户必须是 accountancy 组的一个成员, 要和流程定义中绝对应:

    <potentialOwner>
    <resourceAssignmentExpression>
      <formalExpression>accountancy</formalExpression>
    </resourceAssignmentExpression>
    </potentialOwner>
  • 也能够应用群组名称, 通过工作查问 API 来取得相干的后果. 在代码中增加如下逻辑:

    TaskService taskService = processEngine.getTaskService();
    List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();
  • 因为配置的 ProcessEngine 应用了与 demo 雷同的数据, 能够登录到 Activiti Explorer. 默认,accountancy(会计)组里没有任何人:

    • 登录
    • 点击组
    • 创立一个新组
    • 点击用户
    • 把组分配给 fozzie
    • 应用 fozzie/fozzie 登录
  • 就能够启动咱们的业务流程了, 抉择 Processes 页, 在 [月度财报] 的[操作]列点击[启动流程]
  • 流程会执行到第一个用户工作. 因为咱们以 kermit 登录, 在启动流程实例之后, 就能够看到有了一个新的待领工作. 抉择工作页来查看这条新工作. 留神即便流程被其他人启动, 工作还是会被会计组里的所有人作为一个候选工作看到

    支付工作

  • 当初一个会计要认领这个工作
  • 认领当前, 这个用户就会成为工作的执行人, 工作会从会计组的其余成员的工作列表中隐没. 认领工作的代码:

    taskService.claim(task.getId(), "fozzie");
  • 工作会进入认领工作人的集体工作列表:

    List<Task> tasks = taskService.createTaskQuery().taskAssignee("fozzie").list();
  • 在 Activiti Explorer UI 中, 点击认领按钮, 会执行雷同的操作. 工作会挪动到登录用户的集体工作列表. 你也会看到工作的执行人曾经变成以后登陆的用户:

    实现工作

  • 当初会计能够开始进行财报的工作
  • 报告实现后, 他能够实现工作, 意味着工作所需的所有工作都实现

    taskService.complete(task.getId());
  • 对于 Activiti 引擎:

    • 须要一个内部信息来让流程实例继续执行
    • 工作会把本人从运行库中删除
    • 流程会沿着独自一个外出连线执行, 挪动到第二个工作(审批报告)
    • 与第一个工作雷同的机制会应用到第二个工作上, 不同的是工作是调配给 management 组
  • 在 demo 中:

    • 实现工作是通过点击工作列表中的实现按钮
    • 因为 Fozzie 不是会计, 咱们先从 Activiti Explorer 登记
    • 而后应用 kermit 登陆(经理), 第二个工作会进入未分配任务列表

      完结流程

  • 审批工作像之前一样查问和支付.
  • 实现第二个工作会让流程执行到完结事件, 就会完结流程实例
  • 流程实例和所有相干的运行数据都会从数据库中删除
  • 登录 Activiti Explorer 就能够进行验证, 能够看到保留流程运行数据的表中曾经没有数据:
  • 能够应用 historyService 判断流程是否曾经完结:

    HistoryService historyService = processEngine.getHistoryService();
    HistoricProcessInstance historicProcessInstance =
    historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult();
    System.out.println("Process instance end time:" + historicProcessInstance.getEndTime());

    源码

  • 思考到你可能会在 Activiti Explorer UI 中启动一些流程实例, 这样, 它会取得多个工作, 而不是一个, 所以代码能够始终失常运行:

    public class TenMinuteTutorial {public static void main(String[] args) {
    
      // Create Activiti process engine
      ProcessEngine processEngine = ProcessEngineConfiguration
        .createStandaloneProcessEngineConfiguration()
        .buildProcessEngine();
    
      // Get Activiti services
      RepositoryService repositoryService = processEngine.getRepositoryService();
      RuntimeService runtimeService = processEngine.getRuntimeService();
    
      // Deploy the process definition
      repositoryService.createDeployment()
        .addClasspathResource("FinancialReportProcess.bpmn20.xml")
        .deploy();
    
      // Start a process instance
      String procId = runtimeService.startProcessInstanceByKey("financialReport").getId();
    
      // Get the first task
      TaskService taskService = processEngine.getTaskService();
      List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();
      for (Task task : tasks) {System.out.println("Following task is available for accountancy group:" + task.getName());
    
        // claim it
        taskService.claim(task.getId(), "fozzie");
      }
    
      // Verify Fozzie can now retrieve the task
      tasks = taskService.createTaskQuery().taskAssignee("fozzie").list();
      for (Task task : tasks) {System.out.println("Task for fozzie:" + task.getName());
    
        // Complete the task
        taskService.complete(task.getId());
      }
    
      System.out.println("Number of tasks for fozzie:"
              + taskService.createTaskQuery().taskAssignee("fozzie").count());
    
      // Retrieve and claim the second task
      tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
      for (Task task : tasks) {System.out.println("Following task is available for accountancy group:" + task.getName());
        taskService.claim(task.getId(), "kermit");
      }
    
      // Completing the second task ends the process
      for (Task task : tasks) {taskService.complete(task.getId());
      }
    
      // verify that the process is actually finished
      HistoryService historyService = processEngine.getHistoryService();
      HistoricProcessInstance historicProcessInstance =
        historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult();
      System.out.println("Process instance end time:" + historicProcessInstance.getEndTime());
    }
    
    }

    总结

  • 能够通过 Activiti 中的 BPMN 2.0 构造, 对业务流程进行以下方面的:

    • 定义网关来实现决策环节: 经理能够驳回财报, 从新给会计创立一个工作
    • 思考应用变量: 能够保留或援用报告, 把它显示到表单中
    • 在流程最初退出服务工作: 把报告发给每个领导
退出移动版