工作流(Workflow),就是通过计算机对业务流程自动化执行管理。实现多个参与者共同完成一个业务流程,使用业务流程自动化去执行。
什么工作流系统?
具有工作流功能的系统叫做工作流系统,如果具有工作流功能,如果流程变化只需要修改流程,而不需要修改业务功能。
了解工作流系统应用行业及具体应用场景:
消费品行业,制造业,电信服务业,银证险等金融服务业,物流服务业,物业服务业,物业管理,大中型进出口贸易公司,政府事业机构,研究院所及教育服务业等,特别是大的跨国企业和集团公司
Activiti是一个工作流引擎,引擎需要嵌入到业务系统中才可以发挥作用。通过activiti实现工作流。
将业务流程抽取成一个流程定义,流程定义按照bpmn2.0标准进行定义,activiti就会按照讲预先定义好的流程进行流程的执行,从而实现流程的自动化管理,减少系统开发由业务流程变更增加的成本。
Activiti是前身就是jbpm4(也是一个工作流引擎),Activiti中的api及service接口和jbpm4很相似。
l BPM:
BPM(Business Process Management),即业务流程管理,是一种以规范化的构造端到端的卓越业务流程为中心,以持续的提高组织业务绩效为目的的系统化方法,常见商业管理教育如EMBA、MBA等均将BPM包含在内。
最早在企业提出进行业务流程管理。企业中对业务流程进行管理优化,减少企业成本。
l Bpm软件
实现了业务流程管理的软件,相当于上边说的工作流系统。
l Bpmn2.0(掌握)
Bpmn2.0和上边的bpm是不同的概念!!!
BPMN(Business Process Model And Notation)- 业务流程模型和符号,是由BPMI(Business Process Management Initiative)开发的一套标准的业务流程建模符号,使用BPMN提供的符号可以创建业务流程。
Activiti是基于bpmn2.0标准进行流程定义,activiti在进行流程定义时,使用这些模型和符号绘制流程图。
Event 用一个圆圈表示,它是流程中运行过程中发生的事情。
活动用圆角矩形表示,一个流程由一个活动或多个活动组成
一个bpmn图形的例子:
Activiti是一个工作流引擎,通常情况下activiti需要嵌入到业务系统中运行。
工具准备时的注意事项 :
Activiti运行需要在工程中加入jar包:
Activiti本身的jar包
Activiti和spring的整合jar
依赖jar包
创建数据库(存储流程运行的数据)。
Activiti官方提供了一个和eclipse集成插件(activity-designer),用来画流程图,使用bpmn2.0标准(定义模型和符号),画流程图过程就是流程定义的过程。
在线下画流程图。
使用Activiti视图
将.bpmn文件(流程定义文件),让activiti识别,让activiti按照流程定义文件的内容去管理业务流程。
将.bpmn文件部署到activiti的数据库中,流程定义完成了。
流程定义完成后,如果有参者来发起一个流程,activiti可以按照.bpmn文件内容去管理流程。
比如:张三创建一个采购单表示发起了一个采购流程。李四创建一个采购单,李四也发起一个采购流程。
流程定义和流程实例关系:静态和动态,类似java类和java对象。
因为现在系统的业务流程已经交给activiti管理,通过activiti就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些activiti帮我们管理了,而不像上边需要我们在sql语句中的where条件中指定当前查询的状态值是多少。
当前流程运行到哪了这是由activiti自动管理的,当前参与者就可以完成/办理属于他的当前任务。
当任务办理完成没有下一个任务/结点了,这个流程实例就完成了。
第一个环境:没有加入工作流采购系统
作用:主要学习业务流程和SSH+Activiti
第二个环境:activiti测试环境
作用:用于测试activiti的api,提供各种service接口。
需要创建一个数据库:
仅仅有activiti的数据表
第三个环境:activiti应用环境,加入工作流的采购系统
需要创建一个数据库:
包括activiti的数据表和业务表(采购系统的表)
Activiti自身环境包括:activiti的jar包和数据库
Activiti下载地址:http://activiti.org/download.html
下载activiti5.14版本,
解压:
1) Database:
activiti运行需要有数据库的支持,支持的数据库有:h2, mysql, oracle, postgres, mssql, db2等,该目录存放activiti的建表脚本。
2) Docs
Activiti的帮助文档。
参考
中文的说明文档(activiti各各api和service)
3) Libs
Activiti所需要的jar 包
源码:
核心包:
activiti-engine-5.14.jar
和spring整合包:
activiti-spring-5.14.jar
4) Wars
官方自带的示例工程。
用于进行流程定义,按照bpmn2.0标准画流程图。
在eclipse-juno版本中安装activiti designer设计器:
参数文档开发工具目录下的“activiti开发环境配置.docx”中“eclipse插件安装”,其中包括了Activiti插件。
流程图画好后,生成一个.bpmn的文件(内容遵循bpmn2.0标准),通过插件可以同时生成一个png图片(流程图)。
Activiti的设计器插件安装后:
打开菜单Windows->Preferences->Activiti->Save下流程图片的生成方式:
本教程使用mysql数据库
使用mysql5.1版本。
5.3.1 创建一个用于activiti测试的数据库
5.3.2 创建数据库表
方式1:从下载activiti5.14目录/database中执行sql脚本
方法2:通过java程序
创建数据库思路:
Activiti运行需要核心门面接口ProcessEngine,在创建processEngine时自动检查数据库环境,可以通过数据库操作策略操作数据库。
5.4.1 第一步:创建一个java 工程
这是第二个环境,测试activiti。
5.4.2 第二步:加入jar
上边是activiti单独运行的jar包
下边是activiti和spring整合的jar,比上边目录多了spring的jar包:tx、springmvc。。。
使用activiti和spring整合的jar
5.4.3 第三步:log4j.properties
5.4.4 第四步:activiti.cfg.xml
Activiti的全局配置文件,是spring的配置文件。
如果和spring没有整合时,配置数据源和processEngineConfiguration
数据源:使用dbcp第三方连接池
processEngineConfiguration:用于创建processEngine
Activiti通过spring容器在创建processEngineConfiguration。
数据库操作策略:
false(默认):检查数据库表的版本和依赖库的版本, 如果版本不匹配就抛出异常。
true: 构建流程引擎时,执行检查,如果需要就执行更新。 如果表不存在,就创建。
create-drop: 构建流程引擎时创建数据库表, 关闭流程引擎时删除这些表。
drop-create:先删除表再创建表。
create: 构建流程引擎时创建数据库表, 关闭流程引擎时不删除这些表。
5.4.5 第五步:编写java程序
创建一个processEngineConfiguration对象
通过processEngineConfiguration创建procesEngine对象,同时自动创建数据库表。
5.4.6 数据库创建完成,
共23张表:
5.4.7 表的命名规则
根据activiti.cfg.xml创建processEngineConfiguration
通过processEngineConfiguration创建ProcessEngine,
通过ProcessEngine调用getXXXXService文件得到各各Service,
回顾:
1. 什么是工作流
2. Activiti是什么?
3. 如何使用工作流
4. 什么是BPM?什么是BPMN?
5. 操作步骤?
6. 体系结构?
6.2.1 Activiti单独运行:
activiti.cfg.xml是activiti的核心配置文件,名称不固定。
在此文件中配置processEngineConfiguration,通过processEngineConfiguration创建processEngine
Activiti单独运行通过StandaloneProcessEngineConfiguration创建processEngine
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="databaseSchemaUpdate" value="drop-create" />
bean>
6.2.2 Activiti与spring整合
创建applicationContext-activiti.xml,在此文件配置activiti组件。
使用SpringProcessEngineConfiguration让activiti与spring整合。
配置:processEngineConfiguration
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<property name="dataSource" ref="dataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="databaseSchemaUpdate" value="drop-create" />
<property name="jobExecutorActivate" value="false" />
bean>
在此文件中配置processEngine
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
bean>
配置service;
<bean id="repositoryService" factory-bean="processEngine"
factory-method="getRepositoryService" />
<bean id="runtimeService" factory-bean="processEngine"
factory-method="getRuntimeService" />
<bean id="taskService" factory-bean="processEngine"
factory-method="getTaskService" />
<bean id="historyService" factory-bean="processEngine"
factory-method="getHistoryService" />
<bean id="identityService" factory-bean="processEngine"
factory-method="getIdentityService" />
<bean id="managementService" factory-bean="processEngine"
factory-method="getManagementService" />
相当于一个门面接口,通过ProcessEngineConfiguration创建processEngine,通过ProcessEngine创建各各service接口。
门面是一个设计模式,定义一个简单bean,定义属性(门面包括组件java对象),定义get/set方法,调用门面get方法获取组件。
l 创建方式1;
ProcessEngine processEngine = ProcessEngineConfiguration
.createProcessEngineConfigurationFromResource(resource).buildProcessEngine();
l 创建方式2:
RepositoryService |
activiti的资源管理类,部署流程定义、查询流程定义、删除流程定义、查看流程定义文件(.bpmn,.png) |
RuntimeService |
activiti的流程运行管理类,启动一个流程实例,查询当前运行流程 |
TaskService |
activiti的任务管理类,查询当前待办任务,办理当前的任务 |
HistoryService |
activiti的历史管理类,查询历史流程信息、查询历史任务信息 |
IdentityService |
activiti的用户身份管理类,用户授权(用户信息、组信息、用户与组关系信息) |
FormService |
activiti的表单管理类,在流程图中配置表单数据 |
ManagerService |
activiti的引擎管理类,监控activiti运行状态,通常用超级管理员使用 |
注:红色标注为常用service。
通过调用ProcessEngine.getXXXService()方法:
RepositoryService repositoryService = processEngine.getRepositoryService ()
RuntimeService runtimeService = processEngine.getRuntimeService();
…..
员工创建采购单,经过部门经理、总经理、财务三级审核。业务流程图如下:
使用流程设计器,定义流程,生成.bpmn文件、.png文件。
打开properties视图,
l 流程定义的key
流程定义key用于标识符一个业务流程,针对相同的业务流程key是相同的,比如两个采购流程,由于前后修改采购流程不一样,key定义为一样,因为它就是一个采购流程。
l 创建采购单结点:
指定任务的办理人:
在assignee中设置任务办理人。对每个userTask结点都设置assignee
自动生成流程定义文件:.bpmn,.png
流程修改后,一定要重新部署流程!!!!!
将流程定义文件部署到activiti的数据库中。
使用RepositoryService进行流程定义部署。
由参与者发起一个流程,使用runTimeService
用户查询当前自己需要办理的任务。
使用TaskService查询当前待办任务。
使用TaskService完成任务
1、 用户在线下定义bpmn
生成bpmn文件,png不是必须要生成的,在web页面上提供一个功能查询流程定义图这个功能,activiti本身也能生成png,生成图片会中文乱码,图中结点坐标位置错乱,要提前通过eclipse插件生成png文件。
需要两个文件:.bpmn、.png
2、 通过repositoryService对流程定义进行部署
8.2.1 单个文件部署
将.bpmn、.png两个文件部署到activiti
l 代码:
数据库操作
向流程部署表写一条记录
SELECT * FROM act_re_deployment #流程部署表
向流程定义表写记录,如果一次部署多个流程需要写多条记录。
SELECT * FROM act_re_procdef #流程定义表
Id_ 流程定义id:流程定义key:版本号:流水号
Version_ 流程定义版本:流程定义key,版本号自动加1
Key_ 流程定义key:用于标识一个业务流程,不管版本是否改变,相同的业务流程使用相同的key
deployment_id_ 流程部署id:
RESOURCE_NAME_ bpmn文件名称:
DGRM_RESOURCE_NAME_ png文件名称
将bpmn和png写到资源表:
SELECT * FROM act_ge_bytearray #资源表
注意:流程部署表和流程定义表是一对多关系。建议一次部署只部署一个流程,方便管理。
建议使用单个文件部署部署。
8.2.2 压缩包部署
将.bpmn、.png两个文件压缩成zip文件,通过repositoryService进行部署
通过代码部署zip文件;
@Test
public void deployProcessByZip() {
// 定义zip输入流
InputStream inputStream = this
.getClass()
.getClassLoader()
.getResourceAsStream(
"cn/itcast/activiti/first/diagram/purchasing/purchasingflow01.zip");
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
// 获取repositoryService
RepositoryService repositoryService = processEngine
.getRepositoryService();
// 流程部署
Deployment deployment = repositoryService.createDeployment()//
.addZipInputStream(zipInputStream)//
.deploy();
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
通过此功能查询本系统通过actviti管理的流程有哪些。
使用repositoryService查询流程定义,可以根据流程定义的key查询某个业务流程在activiti中的流程定义。
// 流程定义的查询
@Test
public void queryProcessDefinition() {
//流程定义的key
String processDefinitionKey = "purchasingflow";
// 使用repositoryService
RepositoryService repositoryService = processEngine
.getRepositoryService();
//流程定义查询对象
ProcessDefinitionQuery processDefinitionQuery = repositoryService
.createProcessDefinitionQuery();
//设置查询条件
processDefinitionQuery.processDefinitionKey(processDefinitionKey);
//得出查询列表
List
for(ProcessDefinition processDefinition:list){
System.out.println("--------------------------------");
System.out.println("流程定义的id:"+processDefinition.getId());
System.out.println("流程定义的名称:"+processDefinition.getName());
System.out.println("流程定义的key:"+processDefinition.getKey());
System.out.println("流程部署id:"+processDefinition.getDeploymentId());
System.out.println("bpmn文件名:"+processDefinition.getResourceName());
System.out.println("png文件名:"+processDefinition.getDiagramResourceName());
}
}
需求:查看activiti中流程定义的资源文件(.bpmn和.png),程序员需要查看bpmn(xml格式),终端用户查询图片。
使用repositoryService。
// 流程定义资源文件查看
@Test
public void getProcessResources() throws IOException {
// 使用repositoryService
RepositoryService repositoryService = processEngine
.getRepositoryService();
// 流程定义id
String processDefinitionId = "purchasingflow:3:1104";
// 查询一个流程定义
ProcessDefinition processDefinition = repositoryService
.createProcessDefinitionQuery()
.processDefinitionId(processDefinitionId).singleResult();
String bpmn_name = processDefinition.getResourceName();
String png_name = processDefinition.getDiagramResourceName();
// 部署id,来源于流程部署表
String deploymentId = processDefinition.getDeploymentId();
// 资源文件名称
String resourceName_bpmn = bpmn_name;
String resourceName_png = png_name;
//bmpn的输入流
InputStream inputStream_bpmn = repositoryService.getResourceAsStream(deploymentId, resourceName_bpmn);
//png的输入流
InputStream inputStream_png = repositoryService.getResourceAsStream(deploymentId, resourceName_png);
//将输入流通过文件输出流写到磁盘
FileOutputStream fileOutputStream_bpmn = new FileOutputStream(new File("D:/purchasingflow.bpmn"));
FileOutputStream fileOutputStream_png = new FileOutputStream(new File("D:/purchasingflow.png"));
byte[] b = new byte[1024];
int len=-1;
while((len=inputStream_bpmn.read(b, 0, 1024))!=-1){
fileOutputStream_bpmn.write(b, 0, len);
}
while((len=inputStream_png.read(b, 0, 1024))!=-1){
fileOutputStream_png.write(b, 0, len);
}
//释放资源
inputStream_bpmn.close();
inputStream_png.close();
fileOutputStream_bpmn.close();
fileOutputStream_png.close();
}
使用repositoryService删除
流程一但启动是不删除的,给超级管理开放级联的功能。可以暂停/激活流程的执行。
流程定义变量
@Test
public void testVariable(){
String processDefinitionKey = ResourceBundle.getBundle("bpmnkey").getString("processDefinitionKey");
RuntimeService runtimeService = pe.getRuntimeService();
Map
variables.put("time", "明天请假");
variables.put("address", "去医院");
variables.put("reason","因为生病了" );
variables.put("day", 8);
variables.put("startDay", new Date());
Person person = new Person();
person.setId(1);
person.setName("abc");
person.setBirthday(new Date());
variables.put("person",person);
runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
}
@Test
public void testGetVariable(){
String processDefinitionKey = ResourceBundle.getBundle("bpmnkey").getString("processDefinitionKey");
TaskService taskService = pe.getTaskService();
Task task = taskService.createTaskQuery()
.processDefinitionKey(processDefinitionKey)
.taskAssignee("张三")
.singleResult();
System.out.println(taskService.getVariable(task.getId(), "reason"));
Date startTime = (Date)taskService.getVariable(task.getId(), "startDay");
System.out.println(startTime.toLocaleString());
Person p = (Person) taskService.getVariable(task.getId(), "person");
System.out.println(p);
}
历史记录的查看
@Test
public void testHistoric(){
HistoryService historyService = pe.getHistoryService();
HistoricProcessInstanceQuery hpiq = historyService.createHistoricProcessInstanceQuery();
String processDefinitionKey = ResourceBundle.getBundle("bpmnkey").getString("processDefinitionKey");
hpiq.processDefinitionKey(processDefinitionKey);
List
for(HistoricProcessInstance hiProcessInstance :list){
System.out.println(hiProcessInstance.getProcessDefinitionId());
System.out.println(hiProcessInstance.getId());
}
}
@Test
public void testHistoric01(){
HistoryService historyService = pe.getHistoryService();
HistoricActivityInstanceQuery query = historyService.createHistoricActivityInstanceQuery();
query.processInstanceId("1501");
List
for(HistoricActivityInstance ai :list){
System.out.println(ai.getActivityId());
System.out.println(ai.getActivityName());
System.out.println(ai.getProcessDefinitionId());
System.out.println(ai.getProcessInstanceId());
System.out.println("==============================");
}
}