俯瞰开源工作流引擎Activiti

写作背景

  在办公纸质时代甚至现在,许多流程审核采用手工递纸张表单的方式,一级一级审批签字,工作效率非常低下,而且对于数据统计以及生成报表的功能,需要经过大量的手工操作才能实现。随着计算机的普及以及互联网时代的到来,这些工作的参与者只需要在计算机的系统中填入工作内容,系统就会按照定义好的流程自动执行,各级审批者可以得到工作的信息并做出相应的审批和管理操作。数据统计以及报表的生成也由系统代为完成,这样大大提高了工作效率,在这种背景下,各种的工作流应用以及中间件应运而生。
  两年前,初入公司在营销平台就开始为营销活动申请专门定制了工作流审核平台–甜橙金融营销自助平台。今年在接管新平台后有些老业务需要迁移,在迁移过程中发现很多业务都有这种一级审核、二级审核甚至多级审核机制。随着自助平台定制化需求越来越多,流程与业务耦合性增强;以及老业务平台审核方案产生多套临时业务申请表,维护临时表越来越困难。为了实现这种业务与流程的分离,启发了脑海中的思考,是不是可以将流程从业务中单独剥离出来做统一的流程管理。

目录


一、工作流介绍

  在了解 Activiti 之前我们先了解一下什么是工作流,什么是工作流建模,以及工作流要解决的问题:
  工作流,是对工作流程及其各操作步骤之间业务规则的抽象、概括、描述。工作流建模,即将工作流程中的工作如何前后组织在一起的逻辑和规则在计算机中以恰当的模型进行表示并对其实施计算。
这里写图片描述
  工作流要解决的主要问题是:为实现某个业务目标,在多个参与者之间,利用计算机,按某种预定规则自动传递文档、信息或者任务。

BPMN2.0

  计算机在近时代的发展,工作流被广泛应用于电信、软件、制造、金融和办公自动化领域,随着工作流技术的兴起,为了给全部业务的参与者提供易于理解的标准标记法,由业务流程管理倡议组织(BPMI),开发出了“业务流程建模标记法” (Business Process Modeling Notation, BPMN), BPMN规范的发布是为了让业务流程的全部参与人员对流程可以进行可视化管理,提供一套让所有参与人员都易于理解的语言和标记,为业务流程的设计人员(非技术人员)和流程的实现人员(技术人员)建立起一座桥梁.。BPMI组织于2005年并入OMG组织,当前BPMN规范由OMG组织进行维护:
  2004年发布 BPMN 1.0规范。
  2008年1月发布 BPMN 1.1规范。
  2011年1月发布 BPMN 2.0规范,并且全称改为 Business Process Model And Notation(业务流程模型和符号)。

元素

  使用BPMN 2.0的目的是建立简单并且易懂的业务流程模型,但是同时又需要处理高度复的业务流程,因此要解决这两个矛盾的要求,需要在规范中定义标准的图形和符号。BPMN定义了5个基础的元素类别:

  • 流对象(Flow Objects),在一个业务流程中,流对象是用于定义行为的图形元素,主要有事件(Events)、活动(Activities)和网关(Gateways)三种流对象。
  • 数据(Data):主要有数据对象(Data Objects)、数据输入(Data Inputs) 、数据输出(Data Inputs)和数据存储(Data Stores) 4种元素。
  • 连接对象(Connecting Objects):用于连接流对象,主要有4种连接流对象的方式,包括顺序流(Sequence Flows)、消息流(Message Flows)、关联(Associations)和数据关联(Data Associations) 。
  • 泳道(Swimlanes):泳道提供了两种途径用于组织基础的模型元素,分别是池(Pools)和道(Lanes) 。
  • 制品(Artifacts):制品主要用于为流程提供附加信息,当前制品包括组(Group)和注释(Text Annotation) 。

  以上的元素分类以及下面表格中所列的元素,均是BPMN规范中元素的组成部分,每个对象均有自己对应的图形。下面的表格给出了各个元素的图形及其描述。

元素 图形 描述
事件(Events) 这里写图片描述 发生在流程执行过程中的事情
活动(Activities) 这里写图片描述 在流程执行过程中执行的工作
网关(Gateways) 这里写图片描述 控制流程的分支和聚合
顺序流(Sequence Flows) 这里写图片描述 表示流对象的前后执行顺序
消息流(Message Flows) 这里写图片描述 表示公开流程或协作模型里参与者之间的消息、交互
关联(Association) 这里写图片描述 为流程中的元素关联信息或数据
池(Pool) 这里写图片描述 我们使用池来表示流程的参与者
道(Lane) 这里写图片描述 我们使用道对池内的活动进行分组
数据对象(Data Object) 这里写图片描述 数据对象是一个显示活动是如何需要或产生数据的
消息(Message) 这里写图片描述 消息主要用于描述流程参与者之间的沟通内容
组(Group) 这里写图片描述 对元素进行分类
注释(Text Annotation) 这里写图片描述 给元素附加信息,便于阅读

  以上是 BPMN 规范中定义的基本元素,在这些元素的基础上,会产生很多子元素,例如网关(Gateways),还可以细分为排他网关、并行网关等,详细分类见下图。
这里写图片描述

BPMN 2.0 xml 结构

  一份遵循BPMN 2.0 规范的流程描述文件,除了可以用流程元素的图形定义,还可以用 XML 语法规范定义,这样流程描述文件就可在不同的流程引擎中使用,只要该流程引擎遵守 BPMN 2.0规范,例如可以用 标签来表示用户任务。
  除了 BPMN2.0规范的元素及其属性外,工作流引擎的供应商还可以在不改变已有属性的前提下,在这些规范的基础上添加额外的属性,但是添加的属性不能与已有的属性产生冲突,而且还需要简单易懂。 Activiti 扩展了额外的属性,为了不产生冲突,属性名称统一用 “activiti:” 开头,后面再拼接属性名。例如,属性 activiti:class=”com.bestpay.DemoService”。

二、Activiti 介绍

  Activiti是由Alfresco软件在2010年5月17日发布的业务流程管理(BPM)框架,它是覆盖了业务流程管理、工作流、服务协作等领域的一个开源的、灵活的、易扩展的可执行流程语言框架。Activiti基于Apache许可的开源BPM平台,采用了宽松的Apache Licence 2.0开源协议,因此Activiti一经推出,就得到了开源社区的大力支持,在开源社区的支持下, Activiti吸引了很多的工作流专家参与到该项目中,并且也促使了Activiti在工作流领域的创新。
  Activiti的创始人Tom Baeyens是jBPM的创始人,由于在jBPM的未来架构上产生意见分歧, Tom Baeyens在2010年离开了JBoss并加入Alfresco公司,Tom Baevens的离开使得jBPM5完全放弃了jBPM4的架构,基于Drools Flow重新开发。而在2010年的5月,Tom Baeyens发布了第一个Activiti版本(5.0alphal),由此看来,Activiti更像是jBPM4的延续,也许为了让其看起来更像jBPM4的延续,Activiti团队直接将Activiti的第一个版本定义为5.0。
Activiti

服务组件

  在 Activiti 中,流程引擎的实例对象是 ProcessEngine, 当创建了了流程引擎实例后,在ProcessEngine中会初始化一系列服务组件,这些组件提供了大部分操作流程引擎数据的业务方法,它们就好像J2EE中的Service层,可以使用ProcessEngine中的getXXXService方法得到这些组件的实例。一个ProcessEngine主要有以下实例,如下图。
这里写图片描述

  • RepositoryService: 提供一系列管理流程定义和流程部署的API。
  • RuntimeService: 在流程运行时对流程实例进行管理与控制。
  • TaskService: 对流程任务进行管理,例如任务提醒、任务完成和创建任务等。
  • IdentityService:提供对流程角色数据进行管理的API,这些角色数据包括用户组、用户及它们之间的关系。
  • MangementService:提供对流程引擎进行管理和维护的服务。
  • HistoryService:对流程的历史数据进行操作,包括查询、删除这些历史数据。
  • DynamicBpmnService:使用该服务,可以不需要重新部署流程模型,就可以实现对流程模型的部分修改。

环境准备

Eclipse 插件安装

  Eclipse 插件安装方法这里不做说明,这里提供安装的 URL:http://activiti.org/designer/update/。安装完成之后,需要重启 Eclipse ,在新建文件的对话框中,如果看到下图,则表示安装成功。
这里写图片描述

IDEA 插件安装

  在偏好设置中的 Plugins 选项中(mac:Perferences->plugins,windows:setting->plugins)搜索 actiBPM 插件,点击 Install 重启 IDEA 即可,安装成功后如下图。
这里写图片描述

Activiti 下载

  Activiti 的官网是http://www.activiti.org/, 在我们的 activiti 之旅中将使用 activit 6.0版本,下面是该版本的下载地址:https://github.com/Activiti/Activiti/releases/download/activiti-6.0.0/activiti-6.0.0.zip
下载解压后得到 activiti-6.0.0目录,该目录下有三个子目录:database、libs、wars。下面对各个目录进行简单的额描述:
->database: 用于存放 activiti 数据表的初始化脚本(create 子目录)、删除脚本(drop 子目录)和升级脚本(update 子目录)。从目录里面的脚本文件名可得知,Activiti 支持主流的数据库 oracle,mysql,ms,postgres,DB2等。
->libs:用来存放该版本的 jar 包和源码。
->wars: 存放 activiti 官方提供的示例 war 包,包含三个 activiti-app.war,activiti-admin.war,activit-rest.war。以上三个war包都是基于activiti开发的示例应用,并且都是使用内存数据库(h2)所以不需要配置数据库环境,直接复制到tomcat的 webapps 目录中就可以使用,当然如果你想使用本地的数据库可以用压缩工具打开war包修改WEB-INF/classes/META-INF/activiti-app/下的activiti-app.properties 文件即可。
activiti-app.war 是一套完整的工作流应用;activiti-rest.war 是一套提供restful接口的应用,其实activiti已经提供了restful接口,这个war包只是稍微配置了一下并部署了几个示例流程;activiti-admin.war 顾名思义是可以管理activiti流程相关的应用,这个大家可以打开就可以很快上手。

编写第一个 Activiti 程序

  1. 建立工程环境,打开IDEA,新建项目如下图所示:
    这里写图片描述
  2. 导入 jar 包依赖,除了引入 activiti 相关 jar 之外,还需引入对应的数据驱动 jar 包,maven 依赖如以下代码片段:
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-engine</artifactId>
    <version>6.0.0</version>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring</artifactId>
    <version>6.0.0</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.196</version>
</dependency>

3.创建配置文件,如果没有指定的 Activiti 的配置文件,那么默认情况下将会到 classpath 下读取 activiti.cfg.xml 文件作为 Activiti 的配置文件,该文件主要用于配置数据库连接等属性。配置文件activiti.cfg.xml,如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   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">
   <!-- 流程引擎配置的bean -->
   <!--<bean id="processEngineConfiguration"
      class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
      <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti" />
      <property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
      <property name="jdbcUsername" value="root" />
      <property name="jdbcPassword" value="123456" />
      <property name="databaseSchemaUpdate" value="true" />
   </bean>-->
   <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
      <property name="jdbcUrl" value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
      <property name="jdbcDriver" value="org.h2.Driver" />
      <property name="jdbcUsername" value="sa" />
      <property name="jdbcPassword" value="" />
      <property name="databaseSchemaUpdate" value="true" />
   </bean>
</beans>

  activiti.cfg.xml是一份标准的 xml 文档,在该 xml 文档中只配置了一个名称为processEngineConfiguration的 bean 元素,bean 的前面四个属性是一个数据库名称为 activiti 的数据库连接,后面databaseSchemaUpdate的值为 true 表示在项目启动过程中会自动创建数据库表(数据库activiti需要手工创建),上述配置文件还提供的 mysql 的配置方式(已注释)。

4.创建流程文件,流程文件是遵循 BPMN2.0规范的 xml 文件,我们创建一个简单的请假流程,下图是流程图。
这里写图片描述
流程图对应的文件 leave.xml 如下:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/testm1533999566823" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1533999566823" name="" targetNamespace="http://www.activiti.org/testm1533999566823" typeLanguage="http://www.w3.org/2001/XMLSchema">
  <process id="myProcess_1" isClosed="false" isExecutable="true" name="leave" processType="None">
    <startEvent id="_2" name="StartEvent"/>
    <userTask activiti:exclusive="true" id="_3" name="请假申请"/>
    <userTask activiti:exclusive="true" id="_4" name="请假审核"/>
    <endEvent id="_5" name="EndEvent"/>
    <sequenceFlow id="_6" sourceRef="_2" targetRef="_3"/>
    <sequenceFlow id="_7" sourceRef="_3" targetRef="_4"/>
    <sequenceFlow id="_8" sourceRef="_4" targetRef="_5"/>
  </process>
  <bpmndi:BPMNDiagram documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
    <bpmndi:BPMNPlane bpmnElement="myProcess_1">
      <bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
        <dc:Bounds height="32.0" width="32.0" x="40.0" y="65.0"/>
        <bpmndi:BPMNLabel>
          <dc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
        <dc:Bounds height="55.0" width="85.0" x="110.0" y="55.0"/>
        <bpmndi:BPMNLabel>
          <dc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
        <dc:Bounds height="55.0" width="85.0" x="235.0" y="55.0"/>
        <bpmndi:BPMNLabel>
          <dc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
        <dc:Bounds height="32.0" width="32.0" x="355.0" y="65.0"/>
        <bpmndi:BPMNLabel>
          <dc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6" sourceElement="_2" targetElement="_3">
        <di:waypoint x="72.0" y="81.0"/>
        <di:waypoint x="110.0" y="82.5"/>
        <bpmndi:BPMNLabel>
          <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_7" id="BPMNEdge__7" sourceElement="_3" targetElement="_4">
        <di:waypoint x="195.0" y="82.5"/>
        <di:waypoint x="235.0" y="82.5"/>
        <bpmndi:BPMNLabel>
          <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_4" targetElement="_5">
        <di:waypoint x="320.0" y="82.5"/>
        <di:waypoint x="355.0" y="81.0"/>
        <bpmndi:BPMNLabel>
          <dc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

  上述代码清单为一个流程文件,该文件中的 process 元素用于描述流程信息, 而bpmndi:BPMNDiagram元素则用于描述流程节点的位置信息。在 process 元素中定义了开始事件 srartEvent 、两个 usertask和结束事件 endEvent 以及连接这些元素的顺序流(sequenceFlow)。

5.加载流程文件与启动流程,在准备好流程需要的 jar 包,数据库表(这里配置的是自动创建表,但是数据库还是需要手动创建)流程配置文件和流程文件之后,终于可以 coding 了,Talk is cheap, Show you the code。

package me.lbing.workflow.demo01;

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.task.Task;

/**
 * 第一个流程运行类
 * @author  jeremy king
 *
 */
public class Leave {
   public static void main(String[] args) {
      // 创建流程引擎,获取默认的流程引擎
      ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
      // 得到流程存储服务
      RepositoryService repositoryService = engine.getRepositoryService();
      // 得到运行时服务
      RuntimeService runtimeService = engine.getRuntimeService();
      // 获取流程任务
      TaskService taskService = engine.getTaskService();
      // 部署流程文件
      repositoryService.createDeployment()
            .addClasspathResource("bpmn/leave.bpmn").deploy();
      // 开启流程
      runtimeService.startProcessInstanceByKey("myProcess_1");
      // 查询第一个节点的任务并且输出
      Task task = taskService.createTaskQuery().singleResult();
      System.out.println("第一个任务完成前,当前任务名称:" + task.getName());
      // 完成第一个任务,相当于流程图中的请假申请
      taskService.complete(task.getId());
      // 查询第二个节点的任务并且输出
      task = taskService.createTaskQuery().singleResult();
      System.out.println("第二个任务完成前,当前任务名称:" + task.getName());
      // 完成第二个任务,相当于流程图中的请假审核(流程结束)
      taskService.complete(task.getId());
      task = taskService.createTaskQuery().singleResult();
      System.out.println("流程结束后,查找任务:" + task);
   }
}

运行结果:
第一个任务完成前,当前任务名称:请假申请
第二个任务完成前,当前任务名称:请假审核
流程结束后,查找任务:null

样例Github源码地址(包含配置文件,流程文件,以及代码)

参考网址:
https://blog.csdn.net/zezezuiaiya/article/details/78121663
https://blog.csdn.net/yongboyhood/article/details/70833021
http://www.mossle.com/docs/activiti/index.html

参考书籍:
<<疯狂工作流讲义 杨恩雄>>

猜你喜欢

转载自blog.csdn.net/jgroup/article/details/82116523