BPMN 建模
在前面两节,我们介绍了如何部署 activiti 三个应用以及如何使用第三方数据库,如果你还没阅读前两章也不影响本文的阅读,如果有兴趣了解下,可以点击以下链接
- SpringBoot Activiti6 系列教程(一)-activiti-app 部署
- SpringBoot Activiti6 系列教程(二)- 基于 mysql 数据库初始化
从本章开始,就正式开始 activiti 程序开发,我们先从一个最简单的 activiti 流程开发,流程图如下:
流程很简单,发起后,由管理员 admin
审批,然后结束。
bpmn 源文件代码如下
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 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" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/processdef">
<process id="SimpleProcess" name="SimpleProcess" isExecutable="true">
<startEvent id="startEvent1"></startEvent>
<userTask id="sid-2B2AA039-82D2-479C-8FE9-6F48E4478BD8" name="Admin Approver" activiti:assignee="admin">
<extensionElements>
<modeler:activiti-idm-assignee xmlns:modeler="http://activiti.com/modeler"><![CDATA[true]]></modeler:activiti-idm-assignee>
<modeler:assignee-info-email xmlns:modeler="http://activiti.com/modeler"><![CDATA[admin]]></modeler:assignee-info-email>
<modeler:assignee-info-lastname xmlns:modeler="http://activiti.com/modeler"><![CDATA[Administrator]]></modeler:assignee-info-lastname>
<modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
</extensionElements>
</userTask>
<sequenceFlow id="sid-F506EEC7-0411-4A97-930D-87500FF1414F" sourceRef="startEvent1" targetRef="sid-2B2AA039-82D2-479C-8FE9-6F48E4478BD8"></sequenceFlow>
<endEvent id="sid-2C31753A-3AE2-4C91-B4F8-3B5EED2B4FC5"></endEvent>
<sequenceFlow id="sid-9FC989EE-BBC3-4EB2-A7AF-DA4F2649A68F" sourceRef="sid-2B2AA039-82D2-479C-8FE9-6F48E4478BD8" targetRef="sid-2C31753A-3AE2-4C91-B4F8-3B5EED2B4FC5"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_SimpleProcess">
<bpmndi:BPMNPlane bpmnElement="SimpleProcess" id="BPMNPlane_SimpleProcess">
<bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
<omgdc:Bounds height="30.0" width="30.0" x="100.0" y="163.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-2B2AA039-82D2-479C-8FE9-6F48E4478BD8" id="BPMNShape_sid-2B2AA039-82D2-479C-8FE9-6F48E4478BD8">
<omgdc:Bounds height="80.0" width="100.0" x="255.0" y="138.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-2C31753A-3AE2-4C91-B4F8-3B5EED2B4FC5" id="BPMNShape_sid-2C31753A-3AE2-4C91-B4F8-3B5EED2B4FC5">
<omgdc:Bounds height="28.0" width="28.0" x="465.0" y="164.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sid-F506EEC7-0411-4A97-930D-87500FF1414F" id="BPMNEdge_sid-F506EEC7-0411-4A97-930D-87500FF1414F">
<omgdi:waypoint x="130.0" y="178.0"></omgdi:waypoint>
<omgdi:waypoint x="255.0" y="178.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-9FC989EE-BBC3-4EB2-A7AF-DA4F2649A68F" id="BPMNEdge_sid-9FC989EE-BBC3-4EB2-A7AF-DA4F2649A68F">
<omgdi:waypoint x="355.0" y="178.0"></omgdi:waypoint>
<omgdi:waypoint x="465.0" y="178.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
你可以使用任何一个支持 BPMN2.0 的工具进行流程的建模,如果你是用 eclipse 开发,activiti 在 eclipse 下有一个非常好用的插件,并且 activit 也提供一个 web 版的建模工具,就在 activiti-app 这个应用下,关于如何部署该应用,你可以查看之前的文章 SpringBoot Activiti6 系列教程(一)-activiti-app 部署。以上模型就是通过 activiti-app 创建导出的。
SpringBoot 开发
SpringBoot 介绍
Spring Boot 是一个应用开发框架,基于 spring,相比 spring 开发,spring boot 极大简化了配置,并且遵守约定优于配置的原则即使 0 配置也能正常运行,这在 spring 中是难以想象的。spring boot 应用程序可以独立运行,框架内嵌 web 容器,使得 web 应用程序可以像本地程序一样启动和调试,十分的方便,这种设计方式也使得 spring boot 应用程序非常适合容器化进行大规模部署,而且 spring boot 提供了非常丰富的组件,流行的 java web 框架基本都有 spring boot 版本,生态十分庞大,是目前 java web 开发最好的方案。
创建 spring boot 的工程
如果你已经对 spring boot 非常熟悉,可以跳过该章节。
本次教程代码都是通过IntelliJ IDEA
进行开发,基于Spring Boot 2.1.2
版本, 如果你还没用过 IntelliJ IDEA,建议你尝试下。
-
1.
创建一个maven
工程 -
2.
设置项目 parent 并且加入 spring boot 的依赖和 activiti 相关依赖,参考 pom.xml 如下
pom.xml
<?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.definesys.tutorial.activiti</groupId>
<artifactId>activiti-tutorial</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>activiti-tutorial1</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/>
</parent>
<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>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.185</version>
</dependency>
</dependencies>
</project>
spring-boot-starter-web
是 spring boot 用来创建 rest 服务的组件,h2 是一个内存数据库,如果你没有配置数据库,那么加入 h2 后,无需配置第三方数据库即可运行 activit 应用。
-
3.
在resource
文件夹下创建文件夹processes
(文件夹名字不能修改),并且将第一步创建的模型文件拷贝至该文件下。 -
4.
创建两个接口,/acitiviti/start
可以发起该流程,/activiti/task?uid=admin
可以查询 admin 的流程待办,依次创建 appliaction,dto,service,controller 相关代码文件,如下
本文所有代码已上传至 github,仓库地址为 https://github.com/wls1036/springboot-activiti6-tutorial 欢迎 star
ActivititiApplication.java
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class ActivititiApplication {public static void main(String[] args) {SpringApplication.run(ActivititiApplication.class);
}
}
之所以要加
exclude = SecurityAutoConfiguration.class
,原因是不加的话启动会报Caused by: java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
错误,可能是 spring boot 的版本原因引起的。
TaskRepresentation.java
public class TaskRepresentation {
private String id;
private String name;
public TaskRepresentation(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {return id;}
public void setId(String id) {this.id = id;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
ActivitiService.java
@Service
public class ActivitiService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
/**
* start activiti process
*
* @return instance id
*/
public String start() {ProcessInstance instance = runtimeService.startProcessInstanceByKey("SimpleProcess");
return instance.getId();}
/**
* get user task list
*
* @param uid
* @return user task list
*/
public List<Task> getTask(String uid) {List<Task> tasks = taskService.createTaskQuery().taskAssignee(uid).list();
return tasks;
}
}
RuntimeService
包含了 activiti 运行时接口,可以通过点击进入查看源文件,acitivi api 设计的相当友好,很多接口看名称就差不多知道功能,startProcessInstanceByKey 是根据流程编号发起流程,参数为 bpmn 文件中 process id="SimpleProcess"
中的 id 字段。
TaskService
包含任务相关 api,createTaskQuery 可以创建一个任务查询,activiti 的 api 使用方法基本都一样,比如查询类的,都是 createXXXQuery 创建一个查询类后执行查询条件,list()是返回一个 list 数组。这里是指定审批人,在 bpmn 文件中,我们指定了审批人为admin
<userTask id="sid-2B2AA039-82D2-479C-8FE9-6F48E4478BD8" name="Admin Approver" activiti:assignee="admin">
ActivitiController
@RestController
@RequestMapping(value = "activiti")
public class ActivitiController {
@Autowired
private ActivitiService service;
@RequestMapping(value = "start", method = RequestMethod.GET)
public String start() {return service.start();
}
@RequestMapping(value = "task", method = RequestMethod.GET)
public List<TaskRepresentation> getTask(@RequestParam(value = "uid") String uid) {List<Task> tasks = service.getTask(uid);
List<TaskRepresentation> dtos = new ArrayList<>();
for (Task task : tasks) {dtos.add(new TaskRepresentation(task.getId(), task.getName()));
}
return dtos;
}
}
应用测试
- 流程发起测试
curl http://localhost:8080/activiti/start
65
- 获取待办测试
curl http://localhost:8080/activiti/task?uid=admin
[{"id":"65","name":"Admin Approver"}]
更改数据源
现在的数据都保存到内存数据库 h2 中,程序重新启动就会丢失,这显然不是我们想要的结果,我们需要将数据保存到外部数据库,关于如何初始化外部数据库,可以参考之前的文章 SpringBoot Activiti6 系列教程(二)- 基于 mysql 数据库初始化,这里不在赘述。假设你已经初始化完数据库。
- 新建一个数据源配置类
@Configuration
public class ActivitiConfiguration {
@Bean
public DataSource database() {return DataSourceBuilder.create()
.url("jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf8")
.username("activiti")
.password("definesys")
.driverClassName("com.mysql.jdbc.Driver")
.build();}
}
这里为了简单把连接信息写死在代码里,不是一种好的方式,你可以将连接信息放到 application.properties 里。
- 修改 pom 文件去掉 h2 的依赖增加 mysql 驱动
....
<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>
</dependency>
</dependencies>
- 重新运行程序
重新调用 start
接口后,流程发起后,会在数据库表 ACT_RU_TASK
中创建记录。
总结
本文介绍了一个最简单的 activiti 流程在 spring boot 中的应用。
本文所有代码已经上传至 github,仓库地址为 https://github.com/wls1036/springboot-activiti6-tutorial 欢迎 star