之前学习activiti时买过《activiti实战》,感觉这本书有些关键的细节并没有讲到,于是找了activiti的官方文档,想照着官方的英文文档系统学习一下activiti。本次分享的就是照着官方文档在spring boot环境下集成。
首先添加maven依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>activiti</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.M9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
接着在resouces目录下新建一个processes目录,这个目录下放置流程文件one-task-process.bpmn20.xml,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:activiti="http://activiti.org/bpmn"
targetNamespace="Examples">
<process id="oneTaskProcess" name="The One Task Process">
<startEvent id="theStart" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" />
<userTask id="theTask" name="my task" />
<sequenceFlow id="flow2" sourceRef="theTask" targetRef="theEnd" />
<endEvent id="theEnd" />
</process>
</definitions>
添加后,我们可以加一个配置类,在应用启动后打印流程相关信息:
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
@Bean
public CommandLineRunner init(final RepositoryService repositoryService,
final RuntimeService runtimeService,
final TaskService taskService) {
return new CommandLineRunner() {
@Override
public void run(String... strings) throws Exception {
System.out.println("Number of process definitions : "
+ repositoryService.createProcessDefinitionQuery().count());
System.out.println("Number of tasks : " + taskService.createTaskQuery().count());
runtimeService.startProcessInstanceByKey("oneTaskProcess");
System.out.println("Number of tasks after process start: " + taskService.createTaskQuery().count());
}
};
}
}
启动应用后,结果如下:
Number of process definitions : 1
Number of tasks : 1
Number of tasks after process start: 1
可以看出已经自动找到流程文件,所以流程定义为1,启动后就创建了一个任务,说明环境搭建成功,接下来我们就可以继续学习activiti的相关概念了。
ProcessEngine
流程引擎(ProcessEngine)类是activiti进行流程相关api调用的中心点,通过ProcessEngine可以各种service类,结构如下:
流程引擎通过配置生成,而在spring boot 中引入activiti的依赖包后就可以自动生成流程引擎了。
流程引擎可以获取到的service有:
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
ManagementService managementService = processEngine.getManagementService();
IdentityService identityService = processEngine.getIdentityService();
HistoryService historyService = processEngine.getHistoryService();
FormService formService = processEngine.getFormService();
DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();
RepositoryService
这个类的功能涉及到下面几个方面:
- RepositoryService是首先会使用到的service,它提供了管理和操作deployments和process definitions的方法。其中process definition是一个bpmn2.0流程的java副本,它表示了流程每一步的行为和结构。deployment是流程引擎进行打包的单元,它包含了流程的xml文件和其他相关资源。当创建一个xml格式的流程文件后,我们将其部署后,就会在数据库生成一份流程定义,部署后的流程就可以被启动了。
- RepositoryService类基于流程定义启动流程实例 process instance。
- RepositoryService类可以进行保存和获取流程变量 process variables,流程变量保存的是某个具体流程实例在流程流转时需要的变量。
- RepositoryService可以查询流程实例和执行对象execution。关于流程实例和执行对象的区别可以自行百度,一般来说在没有分支的情况下,获取到的流程实例和执行对象的内容是一样的,而在有分支的情况下,获取到的流程实例和执行对象是有区别的。
- 暂挂和激活流程定义,这部分内容将在后面描述。
TaskService
TaskService则是围绕着任务task相关的操作,任务是由有权限的处理人来执行的。借助TaskService可以实现的功能有:
- 查询分配给某个人或组的任务
- 创建一个单独的任务,这个任务不和某个流程实例挂钩。
- 签收和完成任务。签收意味着某人已经决定要处理这个任务,其他人就不能再去做这个任务了。
IdentityService
IdentityService这个类则是用于对用户和组的管理。
FormService
FormService这个类不是必须的。它主要操作关于start form和task form两个观点。官网解释为:A start form is a form that is shown to the user before the process instance is started, while a task form is the form that is displayed when a user wants to complete a form.
HistoryService
HistoryService这个类则是拥有查询流程流转过程中保存的历史数据,从数据中可以得知每个节点的具体操作信息。
下面是一个获取历史数据例子:
HistoryService historyService = processEngine.getHistoryService();
HistoricProcessInstance historicProcessInstance =
historyService.createHistoricProcessInstanceQuery().processInstanceId(procId).singleResult();
System.out.println("Process instance end time: " + historicProcessInstance.getEndTime());
ManagementService和DynamicBpmnService
这两个类不常用到,这里就不再阐述了。
异常策略
activiti异常类的基类是org.activiti.engine.ActivitiException,这个异常类有几个子类,用于应对不同的异常,如果有某个异常被这几个子类范围内,就会归为ActivitiExceptions异常抛出。
启动流程实例
基于部署后生成的某个流程定义,我们可以启动多个流程实例。流程运行时的各种状态可以通过RuntimeService类来查询。在创建流程实例的时候,可以给特定的流程实例设置流程变量process variables,这也是不同流程实例的不同之处。创建流程实例有很多种方法,下面使用了RuntimeService类来创建,并且在启动的时候还添加了需要的流程参数。
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("employeeName", "Kermit");
variables.put("numberOfDays", new Integer(4));
variables.put("vacationMotivation", "I'm really tired!");
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("vacationRequest", variables);
// Verify that we started a new process instance
Log.info("Number of process instances: " + runtimeService.createProcessInstanceQuery().count());
完成任务
流程中的用户组和用户有一个自己需要处理的任务列表,他可以通过下面这种方法查询:
TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
for (Task task : tasks) {
Log.info("Task available: " + task.getName());
}
获取到指定的任务后,我们可以使用TaskService类的complete方法完成任务:
Task task = tasks.get(0);
Map<String, Object> taskVariables = new HashMap<String, Object>();
taskVariables.put("vacationApproved", "false");
taskVariables.put("managerMotivation", "We have a tight deadline!");
taskService.complete(task.getId(), taskVariables);
暂挂和激活流程
我们可以将部署好的流程定义进行暂挂操作,使用的类是RepositoryService。流程定义被赞挂后就不能再创建流程实例了,只有重新激活以后才能再次正常创建流程实例。
下面是暂挂后再次创建流程跑出异常的例子:
repositoryService.suspendProcessDefinitionByKey("vacationRequest");
try {
runtimeService.startProcessInstanceByKey("vacationRequest");
} catch (ActivitiException e) {
e.printStackTrace();
}
激活流程定义的方法为:repositoryService.activateProcessDefinitionXXX。
此外,流程实例也支持暂挂和激活,使用的方法为:
runtimeService.suspendProcessInstance();
runtimeService.activateProcessInstance();
查询流程信息
有两种查询流程引擎中的信息的方法:query API 和 native queries。前者是使用activiti提供的固定格式的查询方法,如下:
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("kermit")
.processVariableValueEquals("orderId", "0815")
.orderByDueDate().asc()
.list();
由于这种查询方法封装了查询格式,对于复杂的sql查询并不支持,为此复杂查询需要用到native queries,即自己写sql语句进行查询:
List<Task> tasks = taskService.createNativeTaskQuery()
.sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T WHERE T.NAME_ = #{taskName}")
.parameter("taskName", "gonzoTask")
.list();
变量
流程实例 process instance,执行对象execution 和 任务 task这三者都有各自作用范围的变量。这些变量存储在 ACT_RU_VARIABLE 表中。
- 流程实例添加变量,在RuntimeService类中启动流程实例时添加
ProcessInstance startProcessInstanceByKey(String processDefinitionKey, Map<String, Object> variables);
- execution中添加变量,RuntimeService类中设置:
void setVariable(String executionId, String variableName, Object value);
void setVariableLocal(String executionId, String variableName, Object value);
void setVariables(String executionId, Map<String, ? extends Object> variables);
void setVariablesLocal(String executionId, Map<String, ? extends Object> variables);
- TaskService获取变量
Map<String, Object> getVariables(String executionId);
Map<String, Object> getVariablesLocal(String executionId);
Map<String, Object> getVariables(String executionId, Collection<String> variableNames);
Map<String, Object> getVariablesLocal(String executionId, Collection<String> variableNames);
Object getVariable(String executionId, String variableName);
<T> T getVariable(String executionId, String variableName, Class<T> variableClass);
- execution获取变量
execution.getVariables();
execution.getVariables(Collection<String> variableNames);
execution.getVariable(String variableName);
execution.setVariables(Map<String, object> variables);
execution.setVariable(String variableName, Object value);
transient variable 局部变量
表达式
表达式有两种类型:value-expression 和 method-expression 。前者最终转化为值,如{func()}
Spring 集成
ProcessEngineFactoryBean
在Spring 框架中使用activiti 引擎从ProcessEngineFactoryBean这个bean开始。这个bean实现了流程引擎的配置和创建。
spring的配置文件内容如下:
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
</bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
</bean>
现在ProcessEngineFactoryBean配置类已经改为org.activiti.spring.SpringProcessEngineConfiguration这个类了。
事务 Transactions
流程部署
在流程部署的时候,工作流会给流程定义版本号+1;对同一个流程文件多次部署,流程定义的版本号就会从当前版本号最大值加1后更新。启动流程实例的时候,就会使用版本号最大的流程定义去发起流程。
流程图 process diagram
BPMN2.0 介绍
一个简单的流程文件如下:
<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>
根元素为definitions,其下可定义多个流程,为便于维护,一般定义一个流程。
process标签中的id元素就是我们使用runtimeService启动流程实例的时候指定的参数key:
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess");
而process标签中的name属性则不是必须的,只是显示看的。