相关概念:
Process definition---流程定义
流程定义就是整个流程的一个描述.
Process instance---流程实例
一个流程实例包括了所有运行阶段, 其中最典型的属性就是跟踪当前节点的指针。它就是一个主线.
Execution---执行
一般情况下,一个流程实例是一个执行树的根节点, 当一个新的流程实例启动时,实际上流程实例就处于根节点的位置, 这时只有它的"子节点"才可以被激活。使用树状结构的原因在于, 这一概念只有一条执行路径, 使用起来更简单。 业务API不需要了解流程实例和执行之间功能的区别。 因此, API里只有一个执行类型来引用流程实例和执行。它相当于一个支线.
ProcessEngine最核心的对象,就像Hibernate中的SessionFactory一样,做任何事情都要通过它.ProcessEngine提供了获取Service的方法,以下是获取Service的方式:
RepositoryService repositoryService = processEngine.getRepositoryService();
ExecutionService executionService = processEngine.getExecutionService();
TaskService taskService = processEngine.getTaskService();
HistoryService historyService = processEngine.getHistoryService();
ManagementService managementService = processEngine.getManagementService();
IdentityService identityService = processEngine.getIdentityService();
各个Service的作用:
RepositoryService | 管理流程定义 |
ExecutionService | 执行管理,包括启动、推进、删除流程实例等操作 |
TaskService | 任务管理 |
HistoryService | 历史管理(执行完的数据管理) |
IdentityService | jBPM的用户、组管理 |
ManagementService |
有关查询的API:
功能说明 | 相应的查询API |
查询“流程定义” | ProcessDefinitionQuery processDefinitionQuery = processEngine.getRepositoryService() .createProcessDefinitionQuery(); |
查询“执行对象” (流程实例) | ProcessInstanceQuery processInstanceQuery = processEngine.getExecutionService() // .createProcessInstanceQuery(); |
查询“任务” | TaskQuery taskQuery = // processEngine.getTaskService()// .createTaskQuery(); |
查询“执行历史” (流程实例历史) | HistoryProcessInstanceQuery historyProcessInstanceQuery = processEngine.getHistoryService() .createHistoryProcessInstanceQuery(); |
查询“任务历史” | HistoryTaskQuery historyTaskQuery = processEngine.getHistoryService() .createHistoryTaskQuery(); |
jBPM的API风格都是方法调用链的形式.
创建一个流程定义
被添加的文件可以是一个文件:
private ProcessEngine processEngine = Configuration.getProcessEngine();
// 部署(添加)
// jbpm4_deployment, jbpm4_deployprop, jbpm4_lob
@Test
public void deploy() {
String deploymentId = processEngine.getRepositoryService()//
.createDeployment()//
.addResourceFromClasspath("aa/aa.jpdl.xml")// 必须要有一个 .jpdl.xml 文件
.addResourceFromClasspath("aa/aa.png")//
.deploy();
System.out.println("部署成功,deploymentId = " + deploymentId);
}
也可以是一组文件(Zip):
/**
* 创建一个流程定义
*/
@Test
public void createProcessDefinition() {
//获取当前类路径下的文件
ZipInputStream in = new ZipInputStream(CRD.class.getClassLoader()
.getResourceAsStream("JBPM.zip"));
processEngine.getRepositoryService()//
.createDeployment()//
.addResourcesFromZipInputStream(in)//
.deploy();
}
推荐使用Zip文件的形式.
查询所有的流程定义
// Deployment:一次部署的信息(包含多个文件)。
// ProcessDefinition(流程定义):是指解析xx.jpdl.xml后得到的流程信息。
@Test
public void findAll() {
// 查询
List list = processEngine.getRepositoryService()//
.createProcessDefinitionQuery()//
.list();
// 显示
for (ProcessDefinition pd : list) {
System.out.println("id=" + pd.getId()// 格式为:{key}-{version}
+ ", name=" + pd.getName()// .jpdl.xml中根元素的name属性的值
+ ", key=" + pd.getKey()// .jpdl.xml中根元素的key属性的值,如果没有指定,默认为name属性的值 。
+ ", version=" + pd.getVersion()// 版本,默认是1,或自动累加。
+ ", deploymentId=" + pd.getDeploymentId()); // 所属的Deployment记录的id
}
}
jBPM不支持已经部署的流程定义,这也是不能提供的操作,试想几种情况之后就不能发现这是很明智的选择:如果当前有流程实例,原流程有5个步骤要去,走到一半,流程被修改成了7个步骤,中间的活动可能已经被改变了,那么就可能出现问题;或者走到了第4步的时候,流程被修改成了只有3步,那怎么办?所以不支持修改流程定义不仅没有对工作造成影响,还很大程度地保障了原先流程实例的正确执行,以及不对历史中的流程实例造成影响.
只能重新部署一个流程,如果和已有的流程的名字有冲突,将自动把版本号加1,以视区别.在这种情况下,就要保证以后开始的流程实例都是最新的流程实例.
查询所有最新版本的流程定义
// OrderBy的属性名在ProcessDefinitionQuery中有常量的定义。
@Test
public void findAllLatestVersions() {
// 1,查询所有的流程定义
// 按version排序,以便让所有最大的版本都排在最后面
List allList = processEngine.getRepositoryService()//
.createProcessDefinitionQuery()//
.orderAsc(ProcessDefinitionQuery.PROPERTY_VERSION)//
.list();
// 2,过滤出所需的最新版本流程定义的集合
Map map = new HashMap();
for (ProcessDefinition pd : allList) {
// ProcessDefinition pdInMap = map.get(pd.getKey());
// if(pdInMap == null || pdInMap.getVersion() < pd.getVersion()){
// map.put(pd.getKey(), pd);
// }
map.put(pd.getKey(), pd);
}
// 显示
for (ProcessDefinition pd : map.values()) {
System.out.println("id=" + pd.getId()// 格式为:{key}-{version}
+ ", name=" + pd.getName()// .jpdl.xml中根元素的name属性的值
+ ", key=" + pd.getKey()// .jpdl.xml中根元素的key属性的值,如果没有指定,默认为name属性的值 。
+ ", version=" + pd.getVersion()// 版本,默认是1,或自动累加。
+ ", deploymentId=" + pd.getDeploymentId()); // 所属的Deployment记录的id
}
}
由于没有直接查询最新流程定义的语法,所以只能先全部查出,再进行筛选.上面的代码使用了两种方法,第一种(被注释的)每次都判断一下,以保证新的流程定义不被旧的覆盖;第二种在查询时增加了排序条件,保证新的流程定义都在后面,所以不用判断.
删除一个Deployment
@Test
public void deleteById() {
String deploymentId = "40001";
// // 删除一次部署的信息(删除相关的文件),如果有相关的运行信息,则报错
// processEngine.getRepositoryService().deleteDeployment(deploymentId);
// 删除一次部署的信息(删除相关的文件),会级联删除所有相关的运行信息
processEngine.getRepositoryService().deleteDeploymentCascade(deploymentId);
}
删除指定key的所有版本的流程定义(所属的Deployment)
@Test
public void deleteByKey() {
// 1,查询出指定key的所有版本的流程定义
String key = "test";
List list = processEngine.getRepositoryService()//
.createProcessDefinitionQuery()//
.processDefinitionKey(key)//
.list();
// 2,循环删除
for (ProcessDefinition pd : list) {
processEngine.getRepositoryService().deleteDeploymentCascade(pd.getDeploymentId());
}
}
查看流程图
@Test
public void getProcessImage() throws Exception {
String deploymentId = "60001";
String resourceName = "a/helloworld.png";
// 获取某Deployment中所有的文件资源的名称
Set set = processEngine.getRepositoryService().getResourceNames(deploymentId);
for (String s : set) {
System.out.println(s);
}
// 获取某Deployment中某资源的InputStream(内容)
InputStream in = processEngine.getRepositoryService().getResourceAsStream(deploymentId, resourceName);
// 保存到 c盘
FileOutputStream out = new FileOutputStream("c:/processImage.png");
for (int b = -1; (b = in.read()) != -1;) {
out.write(b);
}
in.close();
out.close();
}
流程变量
上面的流程在执行的时候,没有带任何有关受理人的信息以及意见,通常在流程执行的过程中,应该记录当前活动(Activity)的数据,比如同意的理由或者不同意的理由等,这些信息通过流程变量存进数据库.流程变量也是有作用域的,与流程实例的生命周期相同,流程变量可以在流程实例的各个活动中获取和设置,假设我现在有个请假的流程定义:
它就是一个大大的Map,存入的流程变量都会被存入数据库中.流程变量的设置大致可以分为两种:
- 顺便设置
ExecutionService.startProcessInstanceByKey(processDefinitionId, variables);
TaskService.completeTask(taskId, variables);
- 专门设置
// 通过Execution设置一个流程变量
ExecutionService.setVariable(executionId, name, value);
// 通过Execution设置多个流程变量
ExecutionService.setVariables(executionId, variables)
// 通过Task设置多个流程变量
processEngine.getTaskService().setVariables(taskId, variables);
不管是以何种方式设置,如果是以给定taskId,它将会根据taskId找到当前的execution,然后才设置.对应的获取也有两种方式:
// 通过Execution获取流程变量的信息
ExecutionService.getVariable(executionId, variableName);
ExecutionService.getVariableNames(executionId);
ExecutionService.getVariables(executionId, variableNames);
// 通过Task获取流程变量的信息
TaskService.getVariable(taskId, variableName);
TaskService.getVariableNames(taskId);
TaskService.getVariables(taskId, variableNames);
最后找到execution对应的流程变量.
总结一下一个工作流系统的工作流程:
- 申请模板可以自由定制。
- 申请模板对应的流程(步骤)可以自由定制。
- 让文件或表单等按预定的步骤进行流转。
第二步又可分为以下几步:
- 要有流程设计器(画图):需要把所有规则整理清楚,并实现,最终是想能覆盖尽可能多的情况。
- 设计完流程后,结果是:图片,给用户看的;xml,给计算机看的。