Activiti 用户指南(BPMN 2.0介绍)

BPMN 2.0介绍

定义流程

创建一个新的XML文件并为其命名,确保文件以.bpmn20.xml.bpmn结尾,否则引擎将不会选择该文件进行部署。

BPMN 2.0模式的根元素是definitions元素,在此元素内,可以定义多个流程定义(尽管建议在每个文件中只有一个流程定义,因为这样可以简化开发流程的后期维护)。空的流程定义如下所示,请注意,最少definitions元素仅需要xmlnstargetNamespace声明,targetNamespace可以是任何东西,对于对流程定义进行分类很有用。



  
    ..
  

process元素具有两个属性:

  • id:此属性是必需的,并且映射到Activiti ProcessDefinition对象的key属性,然后,可以通过RuntimeService上的startProcessInstanceByKey方法,使用此id来启动流程定义的新流程实例,此方法将始终采用流程定义的最新部署版本。
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess");
  • 这里要注意的重要一点是,这与调用startProcessInstanceById方法不同,此方法期望Activiti引擎在部署时生成的字符串id,可以通过调用processDefinition.getId()方法进行检索。生成的id的格式为key:version,并且长度限制为64个字符。如果你收到一个ActivitiException声明生成的ID太长,请限制该流程的键字段中的文本。
  • name:此属性是可选的,并且映射到ProcessDefinitionname属性,引擎本身不使用此属性,因此,例如,它可用于在用户界面中显示更人性化的名称。

入门:10分钟的教程

在本节中,我们将介绍一个(非常简单的)业务流程,我们将使用它介绍一些基本的Activiti概念和Activiti API。

前提条件

本教程假定你正在运行Activiti演示安装程序,并且你正在使用独立的H2服务器,编辑db.properties并设置jdbc.url=jdbc:h2:tcp://localhost/activiti,然后根据H2的文档运行独立服务器。

目标

本教程的目的是学习Activiti和一些基本的BPMN 2.0概念,最终结果将是一个简单的Java SE程序,该程序将部署流程定义,并通过Activiti引擎API与该流程进行交互。还将介绍Activiti周围的一些工具,当然,在围绕业务流程构建自己的Web应用程序时,也可以使用本教程中学习的内容。

用例

用例很简单:我们有一家公司,我们称之为BPMCorp。在BPMCorp中,每月需要为公司股东编写财务报告,这是会计部门的责任,报告完成后,高层管理人员之一需要批准该文件,然后再将其发送给所有股东。

流程图

可以使用Activiti Designer以图形方式显示上述业务流程,但是,在本教程中,我们将自己键入XML,因为我们在这一点上学到的最多,我们的流程的图形化BPMN 2.0表示法如下所示:

Activiti 用户指南(BPMN 2.0介绍)_第1张图片

我们看到的是一个none Start Event(左侧的圆圈),后面是两个用户任务:“Write monthly financial report”和“Verify monthly financial report”,以none end event(右侧带有粗边框的圆圈)结尾。

XML表示

该业务流程的XML版本(FinancialReportProcess.bpmn20.xml)如下所示:

  • (none) start event告诉我们该流程的切入点是什么。
  • 用户任务声明是我们流程中人工任务的表示,请注意,第一个任务分配给会计组,而第二个任务分配给管理组。
  • 当到达none end event时,该流程结束。
  • 元素通过顺序流相互连接,这些顺序流具有源和目标,它们定义了顺序流的方向。


    

      

      

      
        
          Write monthly financial report for publication to shareholders.
        
        
          
            accountancy
          
        
      

      

      
        
          Verify monthly financial report composed by the accountancy department.
          This financial report is going to be sent to all the company shareholders.
        
        
          
            management
          
        
      

      

      

    

启动流程实例

现在,我们已经创建了业务流程的流程定义,通过这样的流程定义,我们可以创建流程实例,在这种情况下,一个流程实例将与特定月份的单个财务报告的创建和验证相匹配,所有流程实例共享相同的流程定义。

为了能够根据给定的流程定义创建流程实例,我们必须首先部署该流程定义,部署流程定义意味着两件事:

  • 流程定义将存储在为你的Activiti引擎配置的持久性数据存储中,因此,通过部署业务流程,我们确保引擎重新启动后,引擎将找到流程定义。
  • BPMN 2.0流程文件将解析为内存中的对象模型,可以通过Activiti API对其进行操作。

部署可以通过多种方式进行,一种方法是通过以下API,请注意,与Activiti引擎的所有交互都是通过其服务进行的。

Deployment deployment = repositoryService.createDeployment()
  .addClasspathResource("FinancialReportProcess.bpmn20.xml")
  .deploy();

现在,我们可以使用在流程定义中定义的id(请参阅XML文件中的process元素)来启动新流程实例,请注意,Activiti术语中的此id称为key

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

这将创建一个流程实例,该实例将首先经历启动事件,启动事件之后,它遵循所有输出顺序流(在这种情况下只有一个),并且到达了第一个任务(写每月财务报告),Activiti引擎现在将在持久数据库中存储任务,此时,将解决任务中附加的用户或组分配,并将其存储在数据库中,重要的是要注意,Activiti引擎将继续执行流程,直到达到等待状态(例如用户任务)为止。在这种等待状态下,流程实例的当前状态存储在数据库中,它将保持该状态,直到用户决定完成其任务为止,届时,引擎将继续运行直到达到新的等待状态或流程结束,同时,如果引擎重新启动或崩溃,则该流程的状态是安全的,并且在数据库中也处于良好状态。

创建任务后,由于用户任务活动处于等待状态,因此startProcessInstanceByKey方法将返回。在这种情况下,将任务分配给一个组,这意味着该组中的每个成员都是执行任务的候选人。

现在,我们可以将所有内容放在一起,并创建一个简单的Java程序,创建一个新的Eclipse项目,并将Activiti JAR和依赖项添加到其类路径(可以在Activiti发行版的libs文件夹中找到它们)。在调用Activiti服务之前,我们必须首先构造一个ProcessEngine,使我们能够访问这些服务。在这里,我们使用“standalone”配置,该配置构造一个ProcessEngine,该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 tasks = taskService.createTaskQuery().taskCandidateUser("kermit").list();

请注意,我们传递给此操作的用户必须是accountancy的成员,因为它是在流程定义中声明的:


  
    accountancy
  

我们还可以使用任务查询API,通过组名获得相同的结果,现在,我们可以在代码中添加以下逻辑:

TaskService taskService = processEngine.getTaskService();
List tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();

领取任务

会计现在需要申领任务,通过领取任务,特定用户将成为任务的受让人,并且该任务将从accountancy组其他成员的每个任务列表中消失,领取任务是通过编程完成的,如下所示:

taskService.claim(task.getId(), "fozzie");

现在,该任务已在领取该任务的个人任务列表中。

List tasks = taskService.createTaskQuery().taskAssignee("fozzie").list();

完成任务

会计现在可以开始处理财务报告了,报告完成后,他就可以完成任务,这意味着该任务的所有工作都已完成。

taskService.complete(task.getId());

对于Activiti引擎,这是一个外部信号,表明必须继续执行流程实例,任务本身已从运行时数据中删除,接下来是任务的单个传出过渡,将执行移至第二个任务(“verification of the report”)。现在将使用与针对第一个任务所述的相同机制来分配第二个任务,所不同的只是将任务分配给管理组。

结束流程

可以按照与以前完全相同的方式检索并领取任务,完成第二个任务会将流程执行移至结束事件,从而结束流程实例,流程实例和所有相关的运行时执行数据将从数据存储中删除。

通过编程,你还可以使用historyService验证该流程是否已结束:

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

代码概述

合并之前各节中的所有代码片段,你应该具有类似的内容:

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 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 management 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构造时,你将能够通过以下方式增强业务流程:

  • 定义充当决策的网关,这样,经理可以拒绝财务报告,将为会计重新创建财务报告的任务。
  • 声明和使用变量,以便我们可以存储或引用报告,以便可以在表单中将其可视化。
  • 在流程结束时定义服务任务,将报告发送给每个股东。

你可能感兴趣的:(activiti)