论坛首页 Java企业应用论坛

Activiti5.12用户指南之BPMN2.0介绍

浏览 6245 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2013-05-09  

     一、什么是BPMN

     首先BPMN规范是由标准组织BPMI发布的.BPMN 1.0规范发布于2004年5月。此规范展示了BPMI组织两年多的努力成果。BPMN的主要目标就是要提供被所有业务用户理解的一套标记语言,包括业务分析者、软件开发者以及业务管理者与监察者。BPMN还将支持生成可执行的BPEL4WS语言。所以,BPMN在业务流程设计与流程实现之间搭建了一条标准化的桥梁。
BPMN定义了业务流程图,其基于流程图技术,同时为创建业务流程操作的图形化模型进行了裁减。业务流程的模型就是图形化对象的网图,包括活动(也可以说工作)和定义操作顺序的流控制。
二、定义一个流程
注:这个介绍是写在假设你正在用Eclipse IDE创建并且编辑文件。但是你也能用其他的工具去创建包含BPMN2.0的XML文件。
创建一个新的XML文件,并且命名。确保文件是以.bpmn2.0或者.bpmn结尾,否则部署的时候,引擎不会识别。


 
bpmn2.0架构的根节点是definitions元素。在这个元素下,可以定义多个流程定义(虽然我们建议在每个文件中只定义一个流程,因为这样在部署流程的时候,简化了维护)。一个空的流程定义看起来像下面列出来的一样。注:最简化的definitions元素只需要生命xmls和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>
 

 

你也能添加在线的架构路径,作为在Eclipse里XML分类配置的替代。
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属性。通过RuntimeService的startProcessInstanceByKey方法, id能被用在开始流程定义的一个新的流程实例。这个方法会得到最新的流程定义的发布版本号。
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess");
     
注:这个方法和startProcessInstanceById 方法不一样。这个方法中的id是被Activiti引擎在部署的时候生成的,并且能通过调用 processDefinition.getId()方法被检索。生成的id格式是:'key:version',长度被约束为64个字符。如果你得到异常信息ActivitiException,以生成的id太长开头,那么你要减少流程的key值得文本长度。
  • name:这个属性是可选的,存储了流程定义的名字属性。引擎自己不会用这个属性,因此它能用来在用户界面展现一个更人性化的名称。换句话说,这只是一个用来展示的字段,没有什么用处。
三、开始简单介绍
在这一节,我们会通过一个非常简单的业务流程,介绍一些基本的Activiti概念和Acitiviti的API。
1、前期准备
    这一节假设你已经安装了Activiti的demo并且运行成功。
2、目标
     这一节的目标是学习关于Activiti和一些基本的BPMN2.0的概念。最终的结果会是一个简单的Java SE程序,这个程序部署了一个流程定义,并且通过流程引擎的API和流程交互。我们也能触摸到Activiti周围的一些工具。当然,当你在你的业务流程周围创建的web应用程序,在这一节学到的东西能用到。
3、使用情景
     使用情景是简单的:我们有一个公司,让我们称之为BPMCorp。在BPMCorp里,金融报表每个月都需要书面给公司的股东。这是会计部门的职责。当报表完成的时候,在发送给公司股东之前,上级管理成员中的一个需要审批。
4、流程图
     上面描述的业务流程,能用Activiti Designer来实现图形可视化。然而,这一节我们要自己输入XML。我们流程的图形化的BPMN2.0的符号,看起来像这样:
     

 
 5、XML表示
     业务流程的XML版本看起来像下面展示的一样。我们流程的主要元素是很容易识别的:
  •  start event告诉我们对于一个流程,输入点是什么。
  •  User Tasks描述的是我们的流程人员认为的职责。注:第一个任务被分配给 accountancy 组,第二个任务被分配给management组 。后面我们会讲更多的关于用户和组怎么被分配user tasks。
  • 当到达end event,流程结束。
  • 通过顺序流,每一个元素都被连接在一起。这些顺序流有来源和目的,定义了顺序流的方向。
     
<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>
 
          
          6、开始一个流程实例
                    我们现在已经创建了我们业务流程的流程定义。从这样的一个流程定义中,我们能创建流程实例。在这种情况下,一个流程实例会在特定月份,匹配一份财务报告的创建和检验。所有的流程实例会在同样的流程定义分享。
                    为了能从给定的流程定义中创建流程实例,我们必须首先部署这个流程定义。发布一个流程定义意味着两件事:
  • 流程定义会被保存进为Activiti引擎中配置的数据库中。  因此通过部署我们的业务流程,我们确保引擎会在重启之后重新找到我们的流程定义。
  •  BPMN2.0流程文件会被解析到一个内存对象模型中,并且能通过Activiti的API来操纵。
          像那节描述的,部署有好几种方式。一种是通过下面的API。注:通过它的services,所有的交互作用都会发生。
          
Deployment deployment = repositoryService.createDeployment()
  .addClasspathResource("FinancialReportProcess.bpmn20.xml")
  .deploy();
 

 


           现在我们能用我们定义的id来启动一个新的流程实例。注:在流程术语中,这个id被称为key。
     
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("financialReport");
 
          这样会创建一个流程实例,这个流程实例首先会经过start event。start event之后,到达第一个任务。Activiti引擎会在现在的数据库中存储一个任务信息。在这一点上,用户和组分配的任务被解决,也被存进数据库。流程引擎会继续执行,直到它到达等待状态,比如一个用户任务。在这个等待状态,流程实例当前的状态会被保存到数据库中。它会停留在这个状态,直到一个用户决定去完成他们的任务。在这一点上,引擎会继续直到它到达一个新的等待状态或者流程结束。当流程重启或者崩溃,流程的状态是安全的,在数据库中也是安全的。
         调用startProcessInstanceByKey 方法, 任务被创建之后,用户任务会处于等待状态。在这种情况下,任务被分配给一个组,这也意味着组中的每一个成员都是执行任务的候选人。
          现在我们可以把他们糅合在一起,并且创建一个Java程序。创建一个Eclipse项目,并且添加Activiti的jar文件和其依赖jar到classpath中。在我们能调用Activiti的services之前,我们必须首先构造一个ProcessEngine,可以让我们访问到services。这里我们用‘独立’的配置来构造一个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");
}
 
          7、任务列表
          我们现在能通过TaskService取回任务。
List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("kermit").list();
 
          注:我们定义这个操作的用户需要是会计(accountancy)组的成员。因为我们在流程定义中声明了:
<potentialOwner>
  <resourceAssignmentExpression>
    <formalExpression>accountancy</formalExpression>
  </resourceAssignmentExpression>
</potentialOwner>
 
          我们也能用gourp的名字查询获取到同样的结果:

 

          
TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();
 
          
          8、签收任务
          现在一个会计(accountant)需要签收任务。通过签收任务,那么具体的的用户会成为任务的代理人,并且这个任务会在会计(accountant)组其他成员的任务列表中消失。可以像下面一样通过编程实现:
taskService.claim(task.getId(), "fozzie");
 

          现在这个任务就会在签收人的个人任务列表中。
List<Task> tasks = taskService.createTaskQuery().taskAssignee("fozzie").list();
 
          在Activiti Explorer UI中,点击claim按钮会调用同样的操作。这个任务现在被移到登录用户的个人任务列表中。你也能看到任务的代理人变成了当前登录的用户。
          

 
          9、完成任务
          会计现在能开始金融报表的工作了。一旦报表完成,他就可以完成这个任务,这意味着这个任务的所有工作被完成。
taskService.complete(task.getId());
 
          对于Activiti 引擎来说,这是流程实例必须继续执行的一个外部信号。这个任务自己会从运行中数据中移除。流程会移动到执行第二个任务。像第一个任务中描述的同样的机制会分配第二个任务,只是有一个很小的区别,就是第二个任务会被分配到管理(management)组。
          在demo中,完成任务是通过点击完成(complete)按钮。因为Fozzie用户不是一个会计,我们需要退出,用kermit(管理人员)登录。第二个任务现在是在未分配任务列表中。
          10、结束流程
          验收工作可以像前面一样,用同样的方式去检索和签收。完成第二个任务后,流程会移动到结束事件上,也就是完成流程实例。和这个流程实例所有相关的运行执行数据都会从数据库中移除。
          你可以登录到Activiti Explorer中查看ACT_RU_EXECUTION表。

 
          可以用historyService验证流程是否结束。
HistoryService historyService = processEngine.getHistoryService();
HistoricProcessInstance historicProcessInstance = 
historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult();
System.out.println("Process instance end time: " + historicProcessInstance.getEndTime());
 
          11、代码概述
          结合之前的所有代码片段,会是这样:
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());
  }

}
 
  • 大小: 35.3 KB
  • 大小: 8.2 KB
  • 大小: 54.5 KB
  • 大小: 38.9 KB
   发表时间:2013-05-10  
如果BPMN2.0定义的XML结构能够把流程的拓扑数据和流程的控制数据分离一下,采取精简化的XML模型,是否可以让二次开发者的工作变得简单些呢?  我是一个简单流程XML结构的拥护者。。请原谅
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics