Spring Boot + Activiti 完美结合,快速实现工作流~
Posted 肥肥技术宅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot + Activiti 完美结合,快速实现工作流~相关的知识,希望对你有一定的参考价值。
概念
工作流。通过计算机对业务流程自动化执行管理,主要解决的是“使在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使此目标的实现”。
Activiti7
介绍
Activiti是一个工作流引擎,Activiti可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN2.0进行定义,业务流程按照预先定义的流程进行执行,实现了系统的流程由Activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。
在使用activiti之前,首先需要编写activiti.cfg.xml
配置文件。并且引入相关依赖。
<dependencies>
<!--activiti的核心包-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-converter</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-layout</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.activiti.cloud</groupId>
<artifactId>activiti-cloud-services-api</artifactId>
<version>7-201710-EA</version>
</dependency>
<dependency>
<groupId>aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<scope>provided</scope>
</dependency>
</dependencies>
activiti.cfg.xml
activiti的引擎配置文件,包括:ProcessEngineConfiguration
的定义、数据源定义、事务管理器等。其实就是一个Spring配置文件。
<?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">
<!--dbcp连接池-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/activiti"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="maxActive" value="3"/>
<property name="maxIdle" value="1"/>
</bean>
<!--在默认方式下,bean的id固定为processEngineConfiguration-->
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!--配置数据库相关信息-->
<property name="dataSource" ref="dataSource"/>
<!--
activiti数据库表处理策略
false(默认值):检查数据库的版本和依赖库的版本,如果不匹配就抛出异常
true:构建流程引擎时,执行检查,如果需要就执行更新。如果表不存在,就创建。
create-drop:构建流程引擎时创建数据库报表,关闭流程引擎时就删除这些表。
drop-create:先删除表再创建表。
create:构建流程引擎时创建数据库表,关闭流程引擎时不删除这些表
-->
<property name="databaseSchemaUpdate" value="true"/>
<property name="asyncExecutorActivate" value="false"/>
<property name="mailServerHost" value="mail.my-corp.com"/>
<property name="mailServerPort" value="5025"/>
</bean>
</beans>
Activiti流程框架,在前期主要需要了解的就是数据库表的创建、流程的部署、流程的启动和各个阶段任务的完成。
流程引擎配置类
流程引擎配置类(ProcessEngineConfiguration
),通过ProcessEngineConfiguration
可以创建工作流引擎 ProceccEngine。
工作流引擎的创建
工作流引擎的创建主要有两种方式:默认创建方式和一般创建方式
默认创建方式
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);
一般创建方式
//使用自定义方式创建
ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
//获取流程引擎对象:通过 ProcessEngineConfiguration 创建 ProcessEngine,此时会创建数据库
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
当创建好工作流引擎后,对应的数据库中会自动生成25张数据库表。
ACT_GE_PROPERTY
中会先展示下一次流程的ID(next.dbid
),并且在下一次流程部署的时候,对下一次流程的ID进行赋值。
Activiti表说明
这里以表名的前缀进行说明:
Service服务接口
Activiti中还有许多的Service服务接口。这些Service 是工作流引擎提供用于进行工作流部署、执行、管理的服务接口,我们可以使用这些接口操作服务对应的数据表。
Service创建方式
通过ProcessEngine
创建Service方式:
Runtimeservice runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
Service总览
Activiti 的资源管理类,提供了管理和控制流程发布包和流程定义的操作。使用工作流建模工具设计的业务流程图需要使用此service将流程定义文件的内容部署到计算机。除了部署流程定义以外,还可以查询引擎中的发布包和流程定义。
暂停或激活发布包,对应全部和特定流程定义。暂停意味着它们不能再执行任何操作了,激活是对应的反向操作。获得多种资源,像是包含在发布包里的文件,或引擎自动生成的流程图。获得流程定义的pojo版本,可以用来通过java解析流程,而不必通过xml。
Activiti的流程运行管理类。可以从这个服务类中获取很多关于流程执行相关的信息
Activiti的任务管理类。可以从这个类中获取任务的信息。
Activiti的历史管理类,可以查询历史信息,执行流程时,引擎会保存很多数据(根据配置),比如流程实例启动时间,任务的参与者,完成任务的时间,每个流程实例的执行路径,等等。这个服务主要通过查询功能来获得这些数据。
Activiti的引擎管理类,提供了对Activiti流程引擎的管理和维护功能,这些功能不在工作流驱动的应用程序中使用,主要用于Activiti 系统的日常维护。
流程图符号说明
BPMN插件
使用IDEA进行开发,建议下载一个插件。actiBPM插件,直接搜索下载。
流程符号、画流程图
流程符号:事件Event,活动Activity,网关Gateway,流向
使用流程设计器画出流程图
注意:当前任务流程的ID不能是数字开头。
找到本地的文件,选择notepad打开
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1611283406582" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="myEvection" isClosed="false" isExecutable="true" name="出差申请" processType="None">
<startEvent id="_2" name="StartEvent"/>
<userTask activiti:assignee="zhangsan" activiti:exclusive="true" id="_3" name="创建出差申请"/>
<userTask activiti:assignee="jerry" activiti:exclusive="true" id="_4" name="经理审批"/>
<userTask activiti:assignee="jack" activiti:exclusive="true" id="_5" name="总经理审批"/>
<userTask activiti:assignee="rose" activiti:exclusive="true" id="_6" name="财务审批"/>
<endEvent id="_7" name="EndEvent"/>
<sequenceFlow id="_8" sourceRef="_2" targetRef="_3"/>
<sequenceFlow id="_9" sourceRef="_3" targetRef="_4"/>
<sequenceFlow id="_10" sourceRef="_4" targetRef="_5"/>
<sequenceFlow id="_11" sourceRef="_5" targetRef="_6"/>
<sequenceFlow id="_12" sourceRef="_6" targetRef="_7"/>
</process>
<bpmndi:BPMNDiagram documentation="background=#FFFFFF;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
<bpmndi:BPMNPlane bpmnElement="myEvection">
<bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
<omgdc:Bounds height="32.0" width="32.0" x="185.0" y="0.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
<omgdc:Bounds height="55.0" width="85.0" x="160.0" y="85.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_4" id="Shape-_4">
<omgdc:Bounds height="55.0" width="85.0" x="160.0" y="185.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5">
<omgdc:Bounds height="55.0" width="85.0" x="160.0" y="285.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_6" id="Shape-_6">
<omgdc:Bounds height="55.0" width="85.0" x="160.0" y="390.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_7" id="Shape-_7">
<omgdc:Bounds height="32.0" width="32.0" x="185.0" y="475.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12" sourceElement="_6" targetElement="_7">
<omgdi:waypoint x="201.0" y="445.0"/>
<omgdi:waypoint x="201.0" y="475.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_2" targetElement="_3">
<omgdi:waypoint x="201.0" y="32.0"/>
<omgdi:waypoint x="201.0" y="85.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_9" id="BPMNEdge__9" sourceElement="_3" targetElement="_4">
<omgdi:waypoint x="202.5" y="140.0"/>
<omgdi:waypoint x="202.5" y="185.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_11" id="BPMNEdge__11" sourceElement="_5" targetElement="_6">
<omgdi:waypoint x="202.5" y="340.0"/>
<omgdi:waypoint x="202.5" y="390.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10" sourceElement="_4" targetElement="_5">
<omgdi:waypoint x="202.5" y="240.0"/>
<omgdi:waypoint x="202.5" y="285.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
流程的操作
部署流程
使用 Activiti 提供的 API 把流程图的内容写入到数据库中
属于资源操作类,使用 RepositoryService
/
* 流程部署
*/
public void deployment()
// 创建 ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取 RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 使用 service 进行流程的部署,定义一个流程的名字,把bpmn和png部署到数据中
Deployment deployment = repositoryService.createDeployment()
.name("出差申请流程") //流程图标的名字
.addClasspathResource("bpmn/evection.bpmn") //bpmn文件
.addClasspathResource("bpmn/evection.png") //bpmn文件生成的
.deploy();
// 输出部署信息
System.out.println("流程部署ID:" + deployment.getId());
System.out.println("流程部署名字:" + deployment.getName());
有时候我们会有多个流程,需要创建多个bpmn流程文件,这个时候想要同时部署,我们可以对bpmn文件进行打包压缩,使用Zip包进行批量的部署
/
* 使用Zip包进行批量的部署
*/
@Test
public void deployProcessByZip()
// 获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取 RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 流程部署
// 读取资源包文件,构造成 InputStream
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("bpmn/evection.zip");
// 使用 InputStream 构造 ZipInputStream
ZipInputStream zipInputStream = new ZipInputStream(inputStream);
// 使用压缩包的流,进行流程的部署
Deployment deploy = repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.deploy();
// 输出
System.out.println("流程部署的ID:" + deploy.getId());
System.out.println("流程部署的名称:" + deploy.getName());
操作的数据库表:
-
act_ge_bytearray
-
act_ge_property
-
act_re_deployment
-
act_re_procdef
启动流程实例
流程部署完成以后,需要启动流程实例。使用 RuntimeService
根据流程定义的 key进行启动。
核心代码:
/
* 启动流程
*/
public void starProcess()
// 创建 ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取 RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 根据流程定义的ID启动流程
ProcessInstance instance = runtimeService.startProcessInstanceByKey("myEvection");
// 输出内容
System.out.println("流程定义ID:" + instance.getProcessDefinitionId());
System.out.println("流程实例的ID:" + instance.getId());
System.out.println("当前活动的ID:" + instance.getActivityId());
任务查询
使用 TaskService
,根据流程定义的 key ,任务负责人来进行查询
核心代码:
/
* 查询个人待执行的任务
*/
@Test
public void findPersonalTaskList()
// 获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取TaskService
TaskService taskService = processEngine.getTaskService();
// 根据流程的key和任务的负责人去查询任务
List<Task> taskList = taskService.createTaskQuery()
.processDefinitionKey("myEvection") // 流程的key
.includeProcessVariables()
.taskAssignee("zhangsan") // 要查询的负责人
.list();
// 输出
for (Task task : taskList)
System.out.println("流程实例的ID:" + task.getProcessInstanceId());
System.out.println("任务的ID:" + task.getId());
System.out.println("任务的负责人:" + task.getAssignee());
System.out.println("任务的名称:" + task.getName());
任务完成
使用 TaskService
,用任务 ID 直接完成任务。
核心代码:
/
* 完成个人任务
*/
@Test
public void completTask()
String key = "testCandidiate";
String assignee = "张三1"; //任务的负责人
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery()
.processDefinitionKey(key)
.taskAssignee(assignee)
.singleResult();
if (task != null)
taskService.complete(task.getId());
关于流程实例的挂起和激活
全部流程实例的挂起和激活
/
* 全部流程实例的挂起和激活
*/
@Test
public void suspendAllProcessInstance()
// 1.获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.获取 RepositoryService
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3.查询流程定义
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("myEvection")
.singleResult();
// 4.获取当前流程定义的实例是否都是挂起状态
boolean flag = processDefinition.isSuspended();
// 5.获取流程定义的ID
String id = processDefinition.getId();
// 6.判断是否挂起状态。是:改为激活;否:改为挂起
if (flag)
// 改为激活. 参数1:流程定义的ID,参数2:是否激活,参数3:激活时间
repositoryService.activateProcessDefinitionById(id, true, null);
System.out.println("流程定义ID:" + id + "已激活");
else
// 改为挂起. 参数1:流程定义的ID;参数2:是否挂起;参数3:挂起时间
repositoryService.suspendProcessDefinitionById(id, true, null);
System.out.println("流程定义ID:" + id + "已挂起");
单个流程实例的挂起和激活
/
* 单个流程实例的挂起和激活
*/
@Test
public void suspendSingleProcessInstance()
// 1.获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2.获取 RuntimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 3.通过 RuntimeService 获取流程实例对象
ProcessInstance instance = runtimeService.createProcessInstanceQuery()
.processInstanceId("17501")
.singleResult();
// 4.得到当前流程实例的暂停状态
boolean flag = instance.isSuspended();
// 5.获取流程实例的ID
String instanceId = instance.getId();
// 6.判断是否暂停。是:改为激活;否:改为暂停
if (flag)
runtimeService.activateProcessInstanceById(instanceId);
System.out.println("流程实例ID:" + instanceId + "已激活");
else
runtimeService.suspendProcessInstanceById(instanceId);
System.out.println("流程实例ID:" + instanceId + "已暂停");
注意:流程实例在挂起的状态下是无法进行下一步操作的。
流程变量
我们在使用流程变量的时候。如果我们将一个对象存储到一个流程变量中,那么这个对象需要实现Serializable接口。
/
* 出差申请中的流程变量对象
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Evection implements Serializable
private Long id; //主键ID
private Integer days; //出差天数
private String evectionName; //出差单名字
private Date startTime; //出差开始时间
private Date endTime; //出差结束时间
private String address; //目的地
private String reason; //出差原因
流程变量的作用域
-
整个流程实例、任务、执行实例。
-
默认:整个流程实例。
使用方法
在属性上使用UEL表达式 $assignee
,assignee
就是一个流程变量的名称。
在连线上使用UEL表达式 $days<=3
,days就是一个流程变量名称,返回结果为true或者false。
Activiti有很多种方式设置流程变量,这里简单介绍两种:
启动流程时设置流程变量
/
* 启动流程
*/
@Test
public void startProcess()
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RuntimeService runtimeService = processEngine.getRuntimeService();
// 流程变量map
Map<String, Object> map = new HashMap<>();
// 设置流程变量
Evection evection = new Evection();
evection.setDays(2);
// 把流程变量的pojo放入map
map.put("evection", evection);
map.put("assignee0", "张三");
map.put("assignee1", "李经理");
map.put("assignee2", "王财务");
map.put("assignee3", "赵总经理");
runtimeService.startProcessInstanceByKey("myProcess_1", map);
任务办理时设置
/
* 完成任务
*/
@Test
public void completTask()
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
TaskService taskService = processEngine.getTaskService();
Evection evection = new Evection();
evection.setDays(2);
Map<String, Object> map = new HashMap<>();
map.put("evection", evection);
Task task = taskService.createTaskQuery()
.processDefinitionKey("myProcess_2")
.taskAssignee("王财务0")
.singleResult();
if (task != null)
String taskId = task.getId();
// 完成任务
taskService.complete(taskId, map);
网关
用来控制流程的走向
排他网关——ExclusiveGateway
用来在流程中实现决策,当流程执行到这个网关,所有的分支都会判断条件是否为true,如果为true,则执行该分支。
注意:排他网关只会选择一个作为true的分支执行,如果有两个分支都为true,排他网关会选择ID值比较小的一条分支去执行。
如果从排他网关出去的流程所有的条件都不满足,则会抛出异常。
并行网关——ParallelGateway
并行网关,允许流程分成多条分支,也可以把多分支汇聚到一起,并行网关的功能是基于进入和外出顺序流的:
注意:如果同一个并行网关有多个进入和多个外出顺序流,它就同时具有分支和汇聚功能,这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支。
与其他网关的主要区别是:并行网关不会解析条件,即使顺序流中定义了条件,也会被忽略。
并行网关需要所有分支的全部运行完了,才会汇聚,继续向下执行。
包含网关——InclusiveGateway
包含网关可以看成是排他网关和并行网关的结合体,和排他网关一样,可以在外出顺序流上定义条件,包含网关会解析它们,但是主要的区别是:包含网关可以选择多于一条顺序流,这和并行网关一样。
包含网关的功能是基于进入和外出顺序流的。
事件网关——EventGateway
Activiti和Spring的整合开发
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--工作流引擎配置对象-->
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
<!--数据源-->
<property name="dataSource" ref="dataSource"/>
<!--使用Spring的事务管理器-->
<property name="transactionManager" ref="transactionManager"/>
<!--
数据库策略:
false:默认值。activiti在启动时,会对比数据库表中保存的版本。如果没有表或者版本不匹配,将抛出 异常。
true:activiti会对数据库中所有表进行更新操作,如果表不存在,则会自动创建。
create_drop:在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)。
drop-create:在activiti启动时删除原来的旧表,然后再创建新表(不需要手动关闭引擎)。
-->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
<!--配置数据源-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/actspring"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
<property name="maxActive" value="3"/>
<property name="maxIdle" value="1"/>
</bean>
<!-- 流程引擎对象 -->
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration"/>
</bean>
<!--资源服务-->
<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="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--传播行为-->
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
</beans>
Activiti和SpringBoot的整合开发
配置文件:
spring:
application:
name: actspringboot
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/actspring?useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconnect=true&serverTimezone=UTC
username: root
password: root
activiti:
# false:默认值。activiti在启动时,会对比数据库表中保存的版本。如果没有表或者版本不匹配,将抛出异常
# true:activiti会对数据库中所有表进行更新操作,如果表不存在,则会自动创建
# create_drop:在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
# drop-create:在activiti启动时删除原来的旧表,然后再创建新表(不需要手动关闭引擎)
# 线上一般使用false,开发中使用true
database-schema-update: true
# 自动部署验证设置:true-开启(默认)、false-关闭
check-process-definitions: false
# 开启历史表
db-history-used: true
# 历史记录存储等级
history-level: full
server:
port: 8082
Activiti学习之spring boot 与activiti整合
声明:本文是springboot2.0的多项目构建,springboot2.0和spingboot1.5的配置是有出入的,构建项目之前请规范您的springboot版本,选择2.0以上。
一、在IDEA中使用工具创建SpringBoot + Gradle的父工程
new -> project ->gradle
二、在父工程下新建叁个模块 dao service web
右键单击父工程 new -> module -> Spring Initializr -> type选项选中Gradle Project(其他视情况填写)
创建module的最后一步要注意,子模块最好在父工程的目录下,避免不必要的麻烦
创建每个模块的时候 有一个让你选择加载jar包的过程 你可以选也可以不选 我建议什么都不用 项目创建完毕 根据项目需求 手动在build.gradle目录下加入你需要的jar包
三、以此类推创建service模块和web模块 作为项目的子模块
四、重要的事情说三遍(很重要)
修改父项目 也就是sys(第一个创建的gradle项目)下的setting.gradle文件,加入
include ‘dao‘,‘service‘,"web"
此代码表示 dao ,service ,web 这三个项目是他的子项目 如果不加入 后面在父项目中定义的所有规范将毫无意义
五、父项目下bulid.gradle代码
allprojects
apply plugin: ‘java‘
apply plugin: ‘idea‘
group = ‘com.huyuqiang‘
version = ‘0.0.1-SNAPSHOT‘
//jvm(java虚拟机版本号)第一个是你项目使用的jdk版本 第二个是你项目运行的jdk版本
sourceCompatibility = 1.8
targetCompatibility = 1.8
subprojects
ext
//版本号定义
springBootVersion = ‘2.0.3.RELEASE‘
// java编译的时候缺省状态下会因为中文字符而失败
[compileJava, compileTestJava, javadoc]*.options*.encoding = ‘UTF-8‘
repositories
mavenLocal()
maven url "http://maven.aliyun.com/nexus/content/groups/public"
maven url "https://repo.spring.io/libs-release"
mavenCentral()
jcenter()
configurations
all*.exclude module: ‘commons-logging‘
//定义子项目dao的配置
project(‘:dao‘)
dependencies
compile(
//redis缓存框架随便加的 自己项目中需要什么加什么
‘org.springframework.boot:spring-boot-starter-data-redis‘
//‘org.springframework.boot:spring-boot-starter-jdbc‘,
//‘org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.2‘
)
testCompile(
‘org.springframework.boot:spring-boot-starter-test‘,
"junit:junit:4.12"
)
//定义子项目service的配置
project(‘:service‘)
dependencies
//service依赖于dao
compile project(":dao")
compile(
‘org.springframework.boot:spring-boot-starter-web‘,
)
testCompile(
‘org.springframework.boot:spring-boot-starter-test‘,
"junit:junit:4.12"
)
project(‘:web‘)
apply plugin: "war"
dependencies
//web依赖于service
compile project(":service")
compile(
‘org.springframework.boot:spring-boot-starter-web‘
)
testCompile(
‘org.springframework.boot:spring-boot-starter-test‘,
"junit:junit:4.12"
)
// providedCompile(
// "javax.servlet:javax.servlet-api:3.1.0",
// "javax.servlet.jsp:jsp-api:2.2.1-b03",
// "javax.servlet.jsp.jstl:javax.servlet.jsp.jstl-api:1.2.1"
// )
processResources
/* 从‘$projectDir/src/main/java‘目录下复制文件到‘WEB-INF/classes‘目录下覆盖原有同名文件*/
from("$projectDir/src/main/java")
/*自定义任务用于将当前子项目的java类打成jar包,此jar包不包含resources下的文件*/
def jarArchiveName="$project.name-$version.jar"
task jarWithoutResources(type: Jar)
from sourceSets.main.output.classesDir
archiveName jarArchiveName
/*重写war任务:*/
war
dependsOn jarWithoutResources
/* classpath排除sourceSets.main.output.classesDir目录,加入jarWithoutResources打出来的jar包 */
classpath = classpath.minus(files(sourceSets.main.output.classesDir)).plus(files("$buildDir/$libsDirName/$jarArchiveName"))
/*打印编译运行类路径*/
task jarPath <<
println configurations.compile.asPath
/*从子项目拷贝War任务生成的压缩包到根项目的build/explodedDist目录*/
task explodedDist(type: Copy)
into "$buildDir/explodedDist"
subprojects
from tasks.withType(War)
六、子项目dao中bulid.gradle代码
buildscript
ext
springBootVersion = ‘2.0.3.RELEASE‘
repositories
mavenCentral()
dependencies
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
apply plugin: ‘java‘
apply plugin: ‘idea‘
apply plugin: ‘org.springframework.boot‘
apply plugin: ‘io.spring.dependency-management‘
group = ‘com.huyuqiang‘
version = ‘0.0.1-SNAPSHOT‘
sourceCompatibility = 1.8
repositories
mavenLocal()
maven url "http://maven.aliyun.com/nexus/content/groups/public"
maven url "https://repo.spring.io/libs-release"
mavenCentral()
jcenter()
dependencies
testCompile(‘org.springframework.boot:spring-boot-starter-test‘)
七、子项目service中bulid.gradle代码
buildscript
ext
springBootVersion = ‘2.0.3.RELEASE‘
repositories
mavenCentral()
dependencies
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
apply plugin: ‘java‘
apply plugin: ‘idea‘
apply plugin: ‘org.springframework.boot‘
apply plugin: ‘io.spring.dependency-management‘
group = ‘com.huyuqiang‘
version = ‘0.0.1-SNAPSHOT‘
sourceCompatibility = 1.8
repositories
mavenLocal()
maven url "http://maven.aliyun.com/nexus/content/groups/public"
maven url "https://repo.spring.io/libs-release"
mavenCentral()
jcenter()
dependencies
testCompile(‘org.springframework.boot:spring-boot-starter-test‘)
八、子项目web中bulid.gradle代码
buildscript
ext
springBootVersion = ‘2.0.3.RELEASE‘
repositories
mavenCentral()
dependencies
classpath("org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion")
apply plugin: ‘java‘
apply plugin: ‘idea‘
apply plugin: ‘org.springframework.boot‘
apply plugin: ‘io.spring.dependency-management‘
apply plugin: ‘war‘
group = ‘com.huyuqiang‘
version = ‘0.0.1-SNAPSHOT‘
sourceCompatibility = 1.8
repositories
mavenLocal()
maven url "http://maven.aliyun.com/nexus/content/groups/public"
maven url "https://repo.spring.io/libs-release"
mavenCentral()
jcenter()
configurations
providedRuntime
dependencies
providedRuntime(‘org.springframework.boot:spring-boot-starter-tomcat‘)
九、在web子模块下创建controller文件夹,文件夹下创建一个controller类,请看清楚我截图的包结构,如果包结构不对,启动项目的时候访问网页会报错(Whiteable Error Page) ),这是springboot有默认的包结构,如果你的包结构不对spring启动的时候是找不到
你的controller类的
启动web子模块包下的的webapplication启动类,访问localhost:8080
重要提示:
如果你在项目中加入了data框架引用 例如 mybatis jdbc 等 你在第一次启动项目的时候如果没有配置数据源,tomcat会报错无法启动,提示你需要配置数据源。
十、打包:
在父工程目录下输入命令 gradle build
取出 web子模块下 build -> libs -> web-1.0.jar
java -jar 执行即可访问
十一、整合mybatis
逆向工程
一、添加配置文件
新建一个空的XML配置文件,这里以generatorConfig.xml为名,放在resources目录下mybatis文件中,没有mybatis文件夹自己建一个,顺便创建一个mapper文件夹等下要用来放xm映射文件。具体内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<commentGenerator>
<property name="suppressAllComments" value="true"></property>
<property name="suppressDate" value="true"></property>
<property name="javaFileEncoding" value="utf-8"/>
</commentGenerator>
<jdbcConnection driverClass="$driverClass"
connectionURL="$connectionURL"
userId="$userId"
password="$password">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<javaModelGenerator targetPackage="$modelPackage" targetProject="$src_main_java">
<property name="enableSubPackages" value="true"></property>
<property name="trimStrings" value="true"></property>
</javaModelGenerator>
<sqlMapGenerator targetPackage="$sqlMapperPackage" targetProject="$src_main_resources" >
<property name="enableSubPackages" value="true"></property>
</sqlMapGenerator>
<javaClientGenerator targetPackage="$mapperPackage" targetProject="$src_main_java" type="XMLMAPPER">
<property name="enableSubPackages" value="true"/>
</javaClientGenerator>
<!-- sql占位符,表示所有的表 -->
<table tableName="%">
</table>
</context>
</generatorConfiguration>
创建一个gradle.properties文件 代码如下:
# JDBC 驱动类名
jdbc.driverClassName=com.mysql.jdbc.Driver
# JDBC URL: jdbc:mysql:// + 数据库主机地址 + 端口号 + 数据库名
jdbc.url=jdbc:mysql://10.0.0.88:3306/taotaodb?useUnicode=true&characterEncoding=utf8
# JDBC 用户名及密码
jdbc.username=root
jdbc.password=huyuqiang
# 生成实体类所在的包
package.model=com.huyuqiang.po
# 生成 mapper 类所在的包
package.mapper=com.huyuqiang.dao
# 生成 mapper xml 文件所在的包
package.xml=mapper
注意文件路径结构:
bulid.gradle文件代码如下:
//逆向工程方法
configurations
mybatisGenerator
dependencies
//逆向工程jar包
mybatisGenerator ‘org.mybatis.generator:mybatis-generator-core:1.3.2‘
mybatisGenerator ‘mysql:mysql-connector-java:5.1.38‘
mybatisGenerator ‘tk.mybatis:mapper:3.3.1‘
testCompile(‘org.springframework.boot:spring-boot-starter-test‘)
def getDbProperties =
def properties = new Properties()
file("src/main/resources/mybatis/gradle.properties").withInputStream inputStream ->
properties.load(inputStream)
properties
//建立task并应用ant
task mybatisGenerate <<
def properties = getDbProperties()
ant.properties[‘targetProject‘] = projectDir.path
ant.properties[‘driverClass‘] = properties.getProperty("jdbc.driverClassName")
ant.properties[‘connectionURL‘] = properties.getProperty("jdbc.url")
ant.properties[‘userId‘] = properties.getProperty("jdbc.username")
ant.properties[‘password‘] = properties.getProperty("jdbc.password")
ant.properties[‘src_main_java‘] = sourceSets.main.java.srcDirs[0].path
ant.properties[‘src_main_resources‘] = sourceSets.main.resources.srcDirs[0].path
ant.properties[‘modelPackage‘] = properties.getProperty("package.model")
ant.properties[‘mapperPackage‘] = properties.getProperty("package.mapper")
ant.properties[‘sqlMapperPackage‘] = properties.getProperty("package.xml")
ant.taskdef(
name: ‘mbgenerator‘,
classname: ‘org.mybatis.generator.ant.GeneratorAntTask‘,
classpath: configurations.mybatisGenerator.asPath
)
ant.mbgenerator(overwrite: true,
configfile: ‘src/main/resources/mybatis/generatorConfig.xml‘, verbose: true)
propertyset
propertyref(name: ‘targetProject‘)
propertyref(name: ‘userId‘)
propertyref(name: ‘driverClass‘)
propertyref(name: ‘connectionURL‘)
propertyref(name: ‘password‘)
propertyref(name: ‘src_main_java‘)
propertyref(name: ‘src_main_resources‘)
propertyref(name: ‘modelPackage‘)
propertyref(name: ‘mapperPackage‘)
propertyref(name: ‘sqlMapperPackage‘)
gradle重建一下,Tasks下的other中会出现mybatiGenerate
右键run,完成逆向工程。
1.父项目build.gradle文件里定义dao模块处添加依赖
project(‘:dao‘)
dependencies
compile(
//redis缓存框架随便加的 自己项目中需要什么加什么
‘org.springframework.boot:spring-boot-starter-data-redis‘,
//jdbc
‘org.springframework.boot:spring-boot-starter-jdbc‘,
//mybatis
‘org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.2‘,
//阿里巴巴连接池
‘com.alibaba:druid-spring-boot-starter:1.1.0‘,
‘mysql:mysql-connector-java:5.1.38‘
)
testCompile(
‘org.springframework.boot:spring-boot-starter-test‘,
"junit:junit:4.12"
)
2.web子项目模块resources文件夹下applicationproperties文件加入以下内容,也可以不用properties文件,用yml文件,yml文件的格式会让人感觉很舒服,这里我就不多说了差距不大:
spring.datasource.url=jdbc\\:mysql\\://10.0.0.8\\:3306/taotaodb?useUnicode\\=true&characterEncoding\\=gbk&zeroDateTimeBehavior\\=convertToNull
spring.datasource.username=root
spring.datasource.password=huyuqiang
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=60000
3.java配置代替传统的xml配置
创建一个类,此处名为mybatiConfig目录如下:
代码如下:
package com.huyuqiang.web.tools;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
@Configuration
/*告诉spring mybatis生成的dao所在的位置*/
@MapperScan("com.huyuqiang.dao")
public class mybatisConfig
@Bean
public SqlSessionFactory sqlSessionFactoryBean() throws Exception
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
///告诉spring mybatis生成的mapper.xml所在的位置
sqlSessionFactoryBean.setMapperLocations(resolver
.getResources("classpath:/mapper/*.xml"));
return sqlSessionFactoryBean.getObject();
@Bean
//配置druid阿里的连接池
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource dataSource()
return new DruidDataSource();
注:scanBasePackages = "com.huyuqiang.*" 启动的时候
@SpringBootApplication 只会扫描当前包下的文件 因为是多模块项目 springboot并不知道你的service在什么地方,所以必须加。
@SpringBootApplication(scanBasePackages = "com.huyuqiang.*")
public class WebApplication
public static void main(String[] args)
SpringApplication.run(WebApplication.class, args);
启动web子模块包下的的webapplication启动类,访问localhost:8080。
十二:整合activiti
我整合是把activiti生成的28张表和项目数据库的表放在一个数据库中
至于如何分开放在两个数据库中,说实在的能不能做到,可以,无非是两个数据源,配置多数据源切换
多数据源的切换springboot不是不能做,实现起来也不难,可是用起来就很不舒服,而且在管理事物上也
会出现问题,事物无法同步,其实我还是觉得多数据源这种问题尽量不要在spring中解决 还是应该放到
数据库那边 交给dba做分布式来的比较舒服,也更加正统,毕竟我是做代码层的,数据库仅限于一些sql
语言,对分布式等一些数据库层的定义语言知之甚少 也不敢妄自菲薄 只是说一下个人感想
言归正传 看看springboot2.0和activiti6.0的整合
父项目build.gradle加入jar包依赖
‘org.activiti:activiti-spring-boot-starter-basic:6.0.0‘
web子模块resources文件夹下创建文件夹processes文件夹,里面放入一个需要部署的bpmn流程文件,因为项目启动的时候springboot会去加载这个文件夹下的bpmn文件
完成自动部署
如果没有就会报错 如果不想让springboot自动部署流程
application.properties文件里加入一下两行代码:
spring.activiti.check-process-definitions=false
spring.activiti.database-schema-update=true
webapplication启动类里面代码如下
@SpringBootApplication(scanBasePackages = "com.huyuqiang.*",exclude = SecurityAutoConfiguration.class)
public class WebApplication
public static void main(String[] args)
SpringApplication.run(WebApplication.class, args);
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
剔除安全检查类,这个代码必须有不然会报一个“Error creating bean with name ‘requestMappingHandlerMapping‘”的错误
好了 就这么简单搞定了 启动直接可以用。
原文地址:https://blog.csdn.net/chq1988/article/details/75699792
以上是关于Spring Boot + Activiti 完美结合,快速实现工作流~的主要内容,如果未能解决你的问题,请参考以下文章
Spring boot 整合Activiti中遇到的问题
Activiti学习之spring boot 与activiti整合
spring boot2.x和activiti如何配置
Activiti + Spring Boot + Gradle 构建挂起,而 gradle clean 测试
spring boot activiti vue工作流,权限管理框架
关于spring boot 2.1.2集成 activiti6.0遇到的问题