Activiti工作流简单入门

简介:

自jBPM创始人Tom离开之后,jBPM和Activiti就开始大相径庭,jBPM被迫使用新的LGPL协议,而Activiti则使用一种更为宽松的Apache License 2.0协议。不管使用jBPM还是Activiti,两者都遵循BPMN 2.0规范,都可满足项目的一般需求,相比于jBPM,Activiti使用起来不会进行太大的二次改动,但jBPM则是使用Drools较为强大的规则引擎作为后盾,至于两者谁优谁劣,需要在实际项目中权衡利弊。

BPMN 2.0

BPMN最初由业务流程倡议组织(BPMI)定案,现在BPMI并入到OMG(Object Management Group)了,则由OMG建立规范和维护。

BPMN 2.0正式更名为(Business Process Model And Notation)业务流程符号和模型,也有人继续称呼为业务流程建模标记法(Business Process Modeling Notaion),不过无所谓,不管是jBPM、Activiti还是国人开发的FixFlow,都遵循BPMN规范。

Maven配置

JBoss的开源框架都是比较庞大的,不过相对Activiti体积要小一点。下面为配置的依赖项。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!--Junit-->
< dependency >
     < groupId >junit</ groupId >
     < artifactId >junit</ artifactId >
     < version >${junit.version}</ version >
</ dependency >
 
<!--activiti-->
< dependency >
     < groupId >org.activiti</ groupId >
     < artifactId >activiti-engine</ artifactId >
     < version >${activiti.version}</ version >
</ dependency >
 
< dependency >
     < groupId >org.activiti</ groupId >
     < artifactId >activiti-bpmn-layout</ artifactId >
     < version >${activiti.version}</ version >
</ dependency >
 
< dependency >
     < groupId >org.activiti</ groupId >
     < artifactId >activiti-spring</ artifactId >
     < version >${activiti.version}</ version >
</ dependency >
 
<!--apache组件-->
...

整合数据库

为什么要整合数据库?如果不整合数据库,我们大可以使用Quartz这些框架来做流程任务。实际上,Work Flow是用于一种长周期的几乎异步的项目运行环境中,并且我们时刻需要将工作流程的状态记录下来,就是一种既注重结果,又注重过程的事务中,因此,整合数据库很有必要。

下面为配置源数据的XML文件,并且将databaseSchemaUpdate属性配置为drop-create,即在运行前删除原有的数据内容。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<? 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 id = "processEngineConfiguration" class = "org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration" >
         < property name = "databaseSchemaUpdate" value = "drop-create" />
         < property name = "jdbcUrl" value = "jdbc:mysql://localhost:3306/db_activiti?useUnicode=true&amp;characterEncoding=utf-8" />
         < property name = "jdbcDriver" value = "com.mysql.jdbc.Driver" />
         < property name = "jdbcUsername" value = "root" />
         < property name = "jdbcPassword" value = "****" />
         < property name = "jobExecutorActivate" value = "true" />
     </ bean >
</ beans >

默认配置文件名为activiti.cfg.xml,可以在源文件中找到。

另外,如果有必要,请将MySQL设置为区分大小写,即lower_case_table_names = 0。

数据内容会在运行前自动创建,详细表结构内容可参考官网完整信息。

创建工作流文件

由于BPMN规范的作用,一些高级的IDE会自动识别后缀为*.bpmn的文件,不过这些都无所谓,bpmn文件实际上就是XML文件,只是加上了一些图形的标记,如width、height、x和y的坐标,下面为一个招聘面试流程,只包含流程节点,不包含位置标记节点。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<? xml version = "1.0" encoding = "UTF-8" ?>
< definitions xmlns = "http://www.omg.org/spec/BPMN/20100524/MODEL"
              xmlns:activiti = "http://activiti.org/bpmn"
              xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
              targetNamespace = "Examples"
              xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL
              http://www.omg.org/spec/BPMN/2.0/20100501/BPMN20.xsd">
     < process id = "Interview" name = "某公司2012年实习生招聘流程" >
         < documentation >招聘工作流程</ documentation >
         < startEvent id = "start" name = "实习生招聘启动" />
         < userTask id = "bishi" name = "笔试" activiti:candidateGroups = "人力资源部" />
         < sequenceFlow id = "flow1" name = "" sourceRef = "start" targetRef = "bishi" />
         < userTask id = "yimian" name = "技术一面" activiti:candidateGroups = "技术部" />
         < sequenceFlow id = "flow2" name = "" sourceRef = "bishi" targetRef = "yimian" />
         < userTask id = "ermian" name = "技术二面" activiti:candidateGroups = "技术部" />
         < sequenceFlow id = "flow3" name = "" sourceRef = "yimian" targetRef = "ermian" />
         < userTask id = "hrmian" name = "HR面" activiti:candidateGroups = "人力资源部" />
         < sequenceFlow id = "flow4" name = "" sourceRef = "ermian" targetRef = "hrmian" />
         < userTask id = "luyong" name = "录用,发放Offer" activiti:candidateGroups = "人力资源部" />
         < sequenceFlow id = "flow5" name = "" sourceRef = "hrmian" targetRef = "luyong" />
         < endEvent id = "end" name = "实习生招聘结束" />
         < sequenceFlow id = "flow6" name = "" sourceRef = "luyong" targetRef = "end" />
     </ process >
 
</ definitions >

为了便于阅读,一些高级IDE可以转化为图形符号,如下图:

测试运行

有了流程引擎的配置文件和流程文件后,就可以编写代码启动流程引擎并加载该流程文件了。测试清单如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
@Test
public void processTests(){
     // 加载配置文件
     ProcessEngine processEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource( "activiti.cfg.xml" ).buildProcessEngine();
     RepositoryService repositoryService = processEngine.getRepositoryService();
     RuntimeService runtimeService = processEngine.getRuntimeService();
     repositoryService.createDeployment().addClasspathResource( "Interview.bpmn" ).deploy();
     String processId = runtimeService.startProcessInstanceByKey( "Interview" ).getId();
 
     TaskService taskService = processEngine.getTaskService();
     //得到笔试的流程
     System.out.println( "\n***************笔试流程开始***************" );
 
     List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup( "人力资源部" ).list();
     for (Task task : tasks) {
         System.out.println( "人力资源部的任务:name:" +task.getName()+ ",id:" +task.getId());
         taskService.claim(task.getId(), "张三" );
     }
 
     System.out.println( "张三的任务数量:" +taskService.createTaskQuery().taskAssignee( "张三" ).count());
     tasks = taskService.createTaskQuery().taskAssignee( "张三" ).list();
     for (Task task : tasks) {
         System.out.println( "张三的任务:name:" +task.getName()+ ",id:" +task.getId());
         taskService.complete(task.getId());
     }
 
     System.out.println( "张三的任务数量:" +taskService.createTaskQuery().taskAssignee( "张三" ).count());
     System.out.println( "***************笔试流程结束***************" );
 
     System.out.println( "\n***************一面流程开始***************" );
     tasks = taskService.createTaskQuery().taskCandidateGroup( "技术部" ).list();
     for (Task task : tasks) {
         System.out.println( "技术部的任务:name:" +task.getName()+ ",id:" +task.getId());
         taskService.claim(task.getId(), "李四" );
     }
 
     System.out.println( "李四的任务数量:" +taskService.createTaskQuery().taskAssignee( "李四" ).count());
     for (Task task : tasks) {
         System.out.println( "李四的任务:name:" +task.getName()+ ",id:" +task.getId());
         taskService.complete(task.getId());
     }
 
     System.out.println( "李四的任务数量:" +taskService.createTaskQuery().taskAssignee( "李四" ).count());
     System.out.println( "***************一面流程结束***************" );
 
     System.out.println( "\n***************二面流程开始***************" );
     tasks = taskService.createTaskQuery().taskCandidateGroup( "技术部" ).list();
     for (Task task : tasks) {
         System.out.println( "技术部的任务:name:" +task.getName()+ ",id:" +task.getId());
         taskService.claim(task.getId(), "李四" );
     }
 
     System.out.println( "李四的任务数量:" +taskService.createTaskQuery().taskAssignee( "李四" ).count());
     for (Task task : tasks) {
         System.out.println( "李四的任务:name:" +task.getName()+ ",id:" +task.getId());
         taskService.complete(task.getId());
     }
 
     System.out.println( "李四的任务数量:" +taskService.createTaskQuery().taskAssignee( "李四" ).count());
     System.out.println( "***************二面流程结束***************" );
 
     System.out.println( "***************HR面流程开始***************" );
     tasks = taskService.createTaskQuery().taskCandidateGroup( "人力资源部" ).list();
     for (Task task : tasks) {
         System.out.println( "技术部的任务:name:" +task.getName()+ ",id:" +task.getId());
         taskService.claim(task.getId(), "李四" );
     }
 
     System.out.println( "李四的任务数量:" +taskService.createTaskQuery().taskAssignee( "李四" ).count());
     for (Task task : tasks) {
         System.out.println( "李四的任务:name:" +task.getName()+ ",id:" +task.getId());
         taskService.complete(task.getId());
     }
 
     System.out.println( "李四的任务数量:" +taskService.createTaskQuery().taskAssignee( "李四" ).count());
     System.out.println( "***************HR面流程结束***************" );
 
     System.out.println( "\n***************录用流程开始***************" );
     tasks = taskService.createTaskQuery().taskCandidateGroup( "人力资源部" ).list();
     for (Task task : tasks) {
         System.out.println( "技术部的任务:name:" +task.getName()+ ",id:" +task.getId());
         taskService.claim(task.getId(), "李四" );
     }
 
     System.out.println( "李四的任务数量:" +taskService.createTaskQuery().taskAssignee( "李四" ).count());
     for (Task task : tasks) {
         System.out.println( "李四的任务:name:" +task.getName()+ ",id:" +task.getId());
         taskService.complete(task.getId());
     }
 
     System.out.println( "李四的任务数量:" +taskService.createTaskQuery().taskAssignee( "李四" ).count());
     System.out.println( "***************录用流程结束***************" );
 
     HistoryService historyService = processEngine.getHistoryService();
     HistoricProcessInstance historicProcessInstance = historyService
             .createHistoricProcessInstanceQuery()
             .processInstanceId(processId).singleResult();
     System.out.println( "\n流程结束时间:" +historicProcessInstance.getEndTime());
}

代码清单中使用 ProcessEngines类加载默认的流程配置文件(activiti.cfg.xml),再获取各个服务组件的实例。 RepositoryService主要用于管理流程的资源, RuntimeService主要用于流程运行时的流程管理,TaskService主要用于管理流程任务。最后, HistoricProcessInstance会将工作的流程历史记录下来。

目录
相关文章
|
7月前
|
缓存 监控 Java
从零到一构建完整知识体系,阿里最新SpringBoot原理最佳实践真香
Spring Boot不用多说,是咱们Java程序员必须熟练掌握的基本技能。工作上它让配置、代码编写、部署和监控都更简单,面试时互联网企业招聘对于Spring Boot这个系统开发的首选框架也是考察的比较严苛,如果你不是刚入行,只是停留在会用的阶段,那是远远不够的。 虽然Spring Boot易上手,但很多小伙伴也是时不时会跟我反映,Spring Boot技术体系太庞杂了,包含了太多的技术组件,不知道到底该如何高效学习,建立起全面且完整的Spring Boot技术体系和实践技巧,这个时候站在巨人的肩膀上学习就变得非常有必要了,汲取大佬们的学习经验,避免工作面试踩坑,轻松构建Spring Bo
|
12月前
|
Oracle Java 关系型数据库
史上最全 Activiti 学习教程,一文搞定最强工作流引擎!(2)
史上最全 Activiti 学习教程,一文搞定最强工作流引擎!
|
12月前
|
XML 存储 供应链
史上最全 Activiti 学习教程,一文搞定最强工作流引擎!(1)
史上最全 Activiti 学习教程,一文搞定最强工作流引擎!
|
12月前
|
XML Java 数据库连接
史上最全 Activiti 学习教程,一文搞定最强工作流引擎!(4)
史上最全 Activiti 学习教程,一文搞定最强工作流引擎!
|
12月前
|
XML 前端开发 数据可视化
史上最全 Activiti 学习教程,一文搞定最强工作流引擎!(3)
史上最全 Activiti 学习教程,一文搞定最强工作流引擎!
|
12月前
|
存储 XML Java
Activiti工作流
Activiti工作流
113 0
|
前端开发 Java 应用服务中间件
【手把手】JavaWeb 入门级项目实战 -- 文章发布系统 (第七节)
【手把手】JavaWeb 入门级项目实战 -- 文章发布系统 (第七节)
|
SQL Java 关系型数据库
【手把手】JavaWeb 入门级项目实战 -- 文章发布系统 (第五节)
在上一节中,我们成功将数据从前台的JSP页面传递到了controller层,但是还没有写service层,老实说还有很多工作没有,尤其是和数据库的链接方面的,所以,这一节,我们专门来处理一下关于数据库连接方面的东西。
|
前端开发 Java 大数据
【手把手】JavaWeb 入门级项目实战 -- 文章发布系统 (第六节)
【手把手】JavaWeb 入门级项目实战 -- 文章发布系统 (第六节)
|
存储 XML Java
Activiti工作流与业务整合实战
Activiti工作流与业务整合实战
420 0
Activiti工作流与业务整合实战