以前
在没有工作流引擎的时候,要实现流程控制,我们需要在数据库中定义表,然后采用状态字段去跟踪流程的变化:比如是否到下一个流程;
然后到下一个角色执行的时候,我们需要判断用户是否具有审批的权限,如果可以就讲状态设置为一个值。
缺点:
当流程发生变更,代码变化较大
现在
Activiti
可以做到业务流程变化之后,我们的程序可以不用改变,如果可以实现这样的效果,那么我们的业务系统的适应能力就得到了极大提升
1.部署
使用activiti
提供的api把流程定义内容存储起来,在Activiti执行过程中可以查询定义的内容
Activiti
执行把流程定义内容存储在数据库中
1.我们首先先定义一个afg.xml作为工作流的存储的位置信息
<?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 class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration" id="processEngineConfiguration">
<property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///activiti2?characterEncoding=utf-8&nullCatalogMeansCurrent=true&serverTimezone=UTC&useSSL=false" />
<property name="jdbcUsername" value="root" />
<property name="jdbcPassword" value="123" />
<property name="databaseSchemaUpdate" value="true" />
<!--<property name="dataSource" ref="dataSource" />-->
</bean>
<!--<bean class="org.apache.commons.dbcp.BasicDataSource" id="dataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///activiti2?characterEncoding=utf-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="maxActive" value="3" />
<property name="maxIdle" value="2" />
</bean>-->
</beans>
然后还得创建一个log4j.properties
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r[%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\log\act\activiti.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r[%15.15t] %-5p %30.30c %x - %m\n
2.然后我们通过Java代码创建我们的工作流程引擎
/**
* 1.生成Activiti的相关的表结构
*/
@Test
public void test01(){
// 使用classpath下的activiti.cfg.xml中的配置来创建 ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
System.out.println(engine);
}
会得到很多表结构
表结构分析
Service服务接口
Service是工作流引擎提供用于进行工作流部署、执行、管理的服务接口,我们使用这些接口可以就是操作服务对应的数据表
1.创建方式:
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
2.Service总览:
RepositioryService
:activiti的资源管理类
RuntimeService
: activiti的流程运行管理类
TaskService
:activiti的任务管理类
ManagerService
:activiti的引擎管理类
Repository
是activiti的资源管理类,提供了管理和控制流程发布包和流程定义的操作。使用工作流建模工具设计的
业务流程图需要使用此service将流程定义文件的内容部署到计算机。
除了部署流程定义以外还可以:查询引擎中的发布包和流程定义。
暂停或激活发布包,对应全部和特定流程定义。 暂停意味着它们不能再执行任何操作了,激活是对应的
反向操作。获得多种资源,像是包含在发布包里的文件, 或引擎自动生成的流程图。
获得流程定义的pojo版本, 可以用来通过java解析流程,而不必通过xml。
RuntimeService
Activiti的流程运行管理类。可以从这个服务类中获取很多关于流程执行相关的信息
- 启动流程及对流程数据的控制。
- 流程实例【ProcessInstance】和执行流【Execution】查询。
- 触发流程操作、接受消息和信号。
流程实例与执行流的概念
-
在Activiti总,启动一个流程就会创建一个流程实例(ProcessInstance),每个流程实例至少会有一个执行流(Execution),当流程实例没有流程分支时,一般情况下只会存在一个执行流;假设流程出现两个分支,此时Activiti将会有三个执行流,第一个为原来流程的主执行流,而其余两个为子执行流
-
Processlnstance是一个接口,一个Processlnstance实例表示一个流程实例,Processlnstance实际上是执行流(Execution)的子接口,流程实例也是一个执行流。Processlnstance中有Execution没有的属性,例如流程定义和业务主键。当得到的是一个Processlnstance实例时,就将其看作一个流程实例;当得到一个Execution实例时,它就是一个执行流。流程实例与执行流的数据保存在执行表ACT_RU_EXECUTION中,对应的映射实体为ExecutionEntitylmpl
TaskService
Activiti的任务管理类。可以从这个类中获取任务的信息。
HistoryService
Activiti的历史管理类,可以查询历史信息,执行流程时,引擎会保存很多数据(根据配置),比如流程
实例启动时间,任务的参与者, 完成任务的时间,每个流程实例的执行路径,等等。 这个服务主要通过
查询功能来获得这些数据。
ManagementService
Activiti的引擎管理类,提供了对 Activiti 流程引擎的管理和维护功能,这些功能不在工作流驱动的应用
程序中使用,主要用于 Activiti 系统的日常维护
流程的一些概念
- ProcessDefinition
流程定义类。可以从这里获得资源文件等。 - ProcessInstance
代表流程定义的执行实例。如范冰冰请了一天的假,她就必须发出一个流程实例的申请。一个流程实例包括了所有的运行节点。我们可以利用这个对象来了解当前流程实例的进度等信息。流程实例就表示一个流程从开始到结束的最大的流程分支,即一个流程中流程实例只有一个。 - Execution
Activiti用这个对象去描述流程执行的每一个节点。在没有并发的情况下,Execution就是同ProcessInstance。流程按照流程定义的规则执行一次的过程,就可以表示执行对象Execution。
操作数据表
流程部署后,最关键的是三张表
1.act_re_deployment:流程定义部署表,每部署一次就增加一条记录
2.act_re_procdef:流程定义表,部署每个新的流程定义都会在这张表中增加一条记录
3.act_ge_bytearray :流程资源表,流程部署的 bpmn文件和png图片会保存在该表中
1.启动流程实例
启动一个流程表示发起一个新的出差申请单,这就相当于Java类和Java对象的关系,类定义好了后需要new创建一个对象使用,当然可以new出多个对象来,对于出差申请流程,张三可以发起一个出差申请单需要启动一个流程实例.
流程实例代表流程定义的执行实例,一个流程实例包含了所有的运行节点,我们可以利用这个对象来了解当前流程实例的具体进度等信息
如:用户或程序按照流程定义内容发起一个流程,这就是一个流程实例。
/**
* 5.启动一个流程实例
*/
@Test
public void test05(){
// 1.创建ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 2.获取RuntimeService对象
RuntimeService runtimeService = engine.getRuntimeService();
// 3.根据流程定义的id启动流程
String id= "evection";
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(id);
// 4.输出相关的流程实例信息
System.out.println("流程定义的ID:" + processInstance.getProcessDefinitionId());
System.out.println("流程实例的ID:" + processInstance.getId());
System.out.println("当前活动的ID:" + processInstance.getActivityId());
}
2.启动流程实例 并添加Businesskey(业务标识)
流程定义部署在activiti后,就可以在系统中通过activiti去管理该流程的执行,执行流程表示流程的一次执行。
比如部署系统出差流程后,如果某用户要申请出差这时就需要执行这个流程,如果另外一个用户也要申请出差则也需要执行该流程,每个执行互不影响,每个执行是单独的流程实例
启动流程实例时,指定的businesskey,就会在act_ru_execution #流程实例的执行表中存储businesskey。
**Businesskey:**业务标识,通常为业务表的主键,业务标识和流程实例一一对应。业务标识来源于业务系统。存储业务标识就是根据业务标识来关联查询业务系统的数据。
比如:出差流程启动一个流程实例,就可以将出差单的id作为业务标识存储到activiti中,将来查询activiti的流程实例信息就可以获取出差单的id从而关联查询业务系统数据库得到出差单信息
/**
* 启动流程实例,添加businessKey
*/
@Test
public void addBusinessKey(){
// 1、得到ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、得到RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 3、启动流程实例,同时还要指定业务标识businessKey,也就是出差申请单id,这里是1001
ProcessInstance processInstance = runtimeService.
startProcessInstanceByKey("myEvection","1001");
// 4、输出processInstance相关属性
System.out.println("业务id=="+processInstance.getBusinessKey());
}
3.任务查找
流程启动后,任务的负责人就可以查询自己当前能够处理的任务了,查询出来的任务都是当前用户的待办任务
/**
* 任务查询
*/
@Test
public void test06(){
//1.负责人
String assignee ="zhansan";
//2.得到工作流对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
//3.任务查询 需要获取一个 TaskService 对象
TaskService taskService = engine.getTaskService();
//4.根据流程的key和任务负责人 查询任务
List<Task> list = taskService.createTaskQuery()
.processDefinitionKey("evection")
.taskAssignee(assignee)
.list();
//5.输出当前用户具有的任务
for (Task task : list) {
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
}
4.流程任务的处理
通过taskService.createTaskQuery().processDefinitionKey().taskAssingee().list():根据流程定义id和人员查询人员代办任务,我们可以选择任务进行处理
/**
* 流程任务的处理
*/
@Test
public void test07(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = engine.getTaskService();
Task task = taskService.createTaskQuery()
.processDefinitionKey("evection")
.taskAssignee("zhansan")
.singleResult();
// 完成任务
taskService.complete(task.getId());
}
关键在于taskService.complete(task.getId())
能够根据任务的id执行任务,执行完后,流程就会转向下一步
6.流程处理后再查询
/**
* 查询流程的定义
*/
@Test
public void test08(){
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
// 获取一个 ProcessDefinitionQuery对象 用来查询操作
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
List<ProcessDefinition> list = processDefinitionQuery.processDefinitionKey("evection")
.orderByProcessDefinitionVersion() // 安装版本排序
.desc() // 倒序
.list();
// 输出流程定义的信息
for (ProcessDefinition processDefinition : list) {
System.out.println("流程定义的ID:" + processDefinition.getId());
System.out.println("流程定义的name:" + processDefinition.getName());
System.out.println("流程定义的key:" + processDefinition.getKey());
System.out.println("流程定义的version:" + processDefinition.getVersion());
System.out.println("流程部署的id:" + processDefinition.getDeploymentId());
}
}
7.删除流程
/**
* 删除流程
*/
@Test
public void test09(){
//1.先得到工作流的引擎
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = engine.getRepositoryService();
repositoryService.deleteDeployment("12501");
}
8.读取数据库中act_bytearray的资源文件
老套路,首先得到ProcessEngine对象(负责管理所有流程引擎,完成流程实例对象的注册获取,注销等操作)
/**
* 读取数据库中的资源文件
*/
@Test
public void test10() throws Exception{
// 1.得到ProcessEngine对象
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 2.获取RepositoryService对象
RepositoryService repositoryService = engine.getRepositoryService();
// 3.得到查询器
ProcessDefinition definition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("evection")
.singleResult();
// 4.获取流程部署的id
String deploymentId = definition.getDeploymentId();
// 5.通过repositoryService对象的相关方法 来获取图片信息和bpmn信息
// png图片
InputStream pngInput = repositoryService
.getResourceAsStream(deploymentId, definition.getDiagramResourceName());
// bpmn 文件的流
InputStream bpmnInput = repositoryService
.getResourceAsStream(deploymentId, definition.getResourceName());
// 6.文件的保存
File filePng = new File("d:/evection.png");
File fileBpmn = new File("d:/evection.bpmn");
OutputStream pngOut = new FileOutputStream(filePng);
OutputStream bpmnOut = new FileOutputStream(fileBpmn);
IOUtils.copy(pngInput,pngOut);
IOUtils.copy(bpmnInput,bpmnOut);
pngInput.close();
pngOut.close();
bpmnInput.close();
bpmnOut.close();
}