一、Activiti6簡介
??Activiti是由Alfresco軟件在2010年5月17日發(fā)布的業(yè)務(wù)流程管理(BPM)框架,它是覆蓋了業(yè)務(wù)流程管理,工作流,服務(wù)協(xié)作等領(lǐng)域的一個開源,靈活的,易擴展的可執(zhí)行流程語言框架。
官網(wǎng):https://www.activiti.org/before-you-start
二、Maven依賴
<!--activiti-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>6.0.0</version>
</dependency>
<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
三、添加Processes目錄
SpringBoot集成activiti默認會從classpath下的processes目錄下讀取流程定義文件,所以需要在src/main/resources目錄下添加processes目錄,并在目錄中創(chuàng)建流程文件,添加目錄后,目錄結(jié)構(gòu)變?yōu)椋?/p>
如果沒有processes目錄,則需要修改配置spring.activiti.process-definition-location-prefix,指定流程文件存放目錄。
Spring集成Activiti6默認支持.bpmn20.xml和.bpmn格式的流程定義文件,修改支持的文件格式,通過配置spring.activiti.process-definition-location-suffixes修改:
server.port=8888
# 自動檢查、部署流程定義文件
spring.activiti.check-process-definitions=true
# 流程定義文件存放目錄
spring.activiti.process-definition-location-prefix=classpath:/processes
# 流程文件格式后綴 **.bpmn20.xml **.bpmn 自定義(**.bpmn.zip)
#spring.activiti.process-definition-location-suffixes=**.bpmn.zip
# JDBC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
spring.datasource.username=root
spring.datasource.password=1234
# 數(shù)據(jù)庫連接池
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
# 最小空閑連接數(shù)量
spring.datasource.hikari.minimum-idle=5
# 連接池最大連接數(shù),默認是10
spring.datasource.hikari.maximum-pool-size=15
#此屬性控制從池返回的連接的默認自動提交行為,默認值:true
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=UserHikariCP
# 此屬性控制池中連接的最長生命周期,值0表示無限生命周期,默認1800000即30分鐘
spring.datasource.hikari.max-lifetime=1800000
#數(shù)據(jù)庫連接超時時間,默認30秒,即30000
spring.datasource.hikari.connection-timeout=30000
#指定校驗連接合法性執(zhí)行的sql語句
spring.datasource.hikari.connection-test-query=SELECT 1
# 配置JPA,用于初始化數(shù)據(jù)結(jié)構(gòu)
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.show-sql=true
# 每次應(yīng)用啟動不檢查Activiti數(shù)據(jù)表是否存在及版本號是否匹配,提升應(yīng)用啟動速度
spring.activiti.database-schema-update=false
spring.activiti.database-schema-update
配置項可以設(shè)置流程引擎啟動和關(guān)閉時數(shù)據(jù)庫執(zhí)行的策略,可以選擇四種模式
false:false為默認值,設(shè)置為該值后,Activiti在啟動時,會對比數(shù)據(jù)庫表中保存的版本,如果沒有表或者版本不匹配時,將在啟動時拋出異常。
true:設(shè)置為該值后,Activiti會對數(shù)據(jù)庫中所有的表進行更新,如果表不存在,則Activiti會自動創(chuàng)建。
create-drop:Activiti啟動時,會執(zhí)行數(shù)據(jù)庫表的創(chuàng)建操作,在Activiti關(guān)閉時,執(zhí)行數(shù)據(jù)庫表的刪除操作。
drop-create:Activiti啟動時,執(zhí)行數(shù)據(jù)庫表的刪除操作在Activiti關(guān)閉時,會執(zhí)行數(shù)據(jù)庫表的創(chuàng)建操作。
spring.activiti.history-level 配置項
對于歷史數(shù)據(jù),保存到何種粒度,Activiti提供了history-level屬性對其進行配置。history-level屬性有點像log4j的日志輸出級別,該屬性有以下四個值:
none:不保存任何的歷史數(shù)據(jù),因此,在流程執(zhí)行過程中,這是最高效的。
activity:級別高于none,保存流程實例與流程行為,其他數(shù)據(jù)不保存。
audit:除activity級別會保存的數(shù)據(jù)外,還會保存全部的流程任務(wù)及其屬性。audit為history的默認值。
full:保存歷史數(shù)據(jù)的最高級別,除了會保存audit級別的數(shù)據(jù)外,還會保存其他全部流程相關(guān)的細節(jié)數(shù)據(jù),包括一些流程參數(shù)等。
四、數(shù)據(jù)庫表
Activiti的后臺是有數(shù)據(jù)庫的支持,所有的表都以ACT_開頭。
第二部分是表示表的用途的兩個字母標識。用途也和服務(wù)的API對應(yīng)。
act_re_: 'RE’表示repository。 這個前綴的表包含了流程定義和流程靜態(tài)資源(圖片,規(guī)則,等等)。
act_ru_: 'RU’表示runtime。 這些運行時的表,包含流程實例,任務(wù),變量,異步任務(wù),等運行中的數(shù)據(jù)。 Activiti只在流程實例執(zhí)行過程中保存這些數(shù)據(jù),在流程結(jié)束時就會刪除這些記錄。 這樣運行時表可以一直很小速度很快。
act_id_: 'ID’表示identity。 這些表包含身份信息,比如用戶,組等等。
act_hi_: 'HI’表示history。 這些表包含歷史數(shù)據(jù),比如歷史流程實例,變量,任務(wù)等等。
act_ge_*: 通用數(shù)據(jù),用于不同場景下,如存放資源文件。
資源庫流程規(guī)則表
act_re_deployment 部署信息表
act_re_model 流程設(shè)計模型部署表
act_re_procdef 流程定義數(shù)據(jù)表
運行時數(shù)據(jù)庫表
act_ru_execution 運行時流程執(zhí)行實例表
act_ru_identitylink 運行時流程人員表,主要存儲任務(wù)節(jié)點與參與者的相關(guān)信息
act_ru_task 運行時任務(wù)節(jié)點表
act_ru_variable 運行時流程變量數(shù)據(jù)表
歷史數(shù)據(jù)庫表
act_hi_actinst 歷史節(jié)點表
act_hi_attachment 歷史附件表
act_hi_comment 歷史意見表
act_hi_identitylink 歷史流程人員表
act_hi_detail 歷史詳情表,提供歷史變量的查詢
act_hi_procinst 歷史流程實例表
act_hi_taskinst 歷史任務(wù)實例表
act_hi_varinst 歷史變量表
組織機構(gòu)表
act_id_group 用戶組信息表
act_id_info 用戶擴展信息表
act_id_membership 用戶與用戶組對應(yīng)信息表
act_id_user 用戶信息表
這四張表很常見,基本的組織機構(gòu)管理,關(guān)于用戶認證方面建議還是自己開發(fā)一套,組件自帶的功能太簡單,使用中有很多需求難以滿足
通用數(shù)據(jù)表
act_ge_bytearray 二進制數(shù)據(jù)表
act_ge_property 屬性數(shù)據(jù)表存儲整個流程引擎級別的數(shù)據(jù),初始化表結(jié)構(gòu)時,會默認插入三條記錄

五、工作流常用service
RepositoryService
Activiti 中每一個不同版本的業(yè)務(wù)流程的定義都需要使用一些定義文件,部署文件和支持數(shù)據(jù) ( 例如 BPMN2.0 XML 文件,表單定義文件,流程定義圖像文件等 ),這些文件都存儲在 Activiti 內(nèi)建的 Repository 中。Repository Service 提供了對 repository 的存取服務(wù)。
RuntimeService
Activiti 中,每當一個流程定義被啟動一次之后,都會生成一個相應(yīng)的流程對象實例。Runtime Service 提供了啟動流程、查詢流程實例、設(shè)置獲取流程實例變量等功能。此外它還提供了對流程部署,流程定義和流程實例的存取服務(wù)。
TaskService
在 Activiti 中業(yè)務(wù)流程定義中的每一個執(zhí)行節(jié)點被稱為一個 Task,對流程中的數(shù)據(jù)存取,狀態(tài)變更等操作均需要在 Task 中完成。Task Service 提供了對用戶 Task 和 Form相關(guān)的操作。它提供了運行時任務(wù)查詢、領(lǐng)取、完成、刪除以及變量設(shè)置等功能。
IdentityService
Activiti 中內(nèi)置了用戶以及組管理的功能,必須使用這些用戶和組的信息才能獲取到相應(yīng)的 Task。Identity Service 提供了對 Activiti 系統(tǒng)中的用戶和組的管理功能。
ManagementService
Management Service 提供了對 Activiti 流程引擎的管理和維護功能,這些功能不在工作流驅(qū)動的應(yīng)用程序中使用,主要用于 Activiti 系統(tǒng)的日常維護。
HistoryService
History Service 用于獲取正在運行或已經(jīng)完成的流程實例的信息,與 Runtime Service 中獲取的流程信息不同,歷史信息包含已經(jīng)持久化存儲的永久信息,并已經(jīng)被針對查詢優(yōu)化。
FormService
Activiti 中的流程和狀態(tài) Task 均可以關(guān)聯(lián)業(yè)務(wù)相關(guān)的數(shù)據(jù)。通過使用 Form Service可以存取啟動和完成任務(wù)所需的表單數(shù)據(jù)并且根據(jù)需要來渲染表單。
DynamicBpmnService
一個新增的服務(wù),用于動態(tài)修改流程中的一些參數(shù)信息等,是引擎中的一個輔助的服務(wù)
FormRepositoryService
部署表單等
五、安裝activiti
-
下載
https://www.activiti.org
文檔地址:https://www.activiti.org/userguide/
下載
下載 -
布署
需要將以下war包,布署到tomcat中。
布署
解壓之后,將wars文件夾下的activiti-admin.war、activiti-app.war拷貝到tomcat的webapps下:
cp activiti-6.0.0/wars/activiti-admin.war apache-tomcat-8.0.36/webapps/
cp activiti-6.0.0/wars/activiti-app.war apache-tomcat-8.0.36/webapps/
通過tomcat的bin目錄下startup.sh啟動tomcat:
瀏覽器訪問http://192.168.11.126:8080/activiti-app訪問activiti,初始賬號密碼是admin/test
六、idea安裝Activiti插件
-
安裝插件
點擊菜單【File】-->【Settings...】打開【Settings】窗口。
點擊左側(cè)【Plugins】按鈕,在右側(cè)輸出"actiBPM",點擊下面的【Search in repositories】鏈接會打開【Browse Repositories】窗口。
搜索插件
安裝插件 -
創(chuàng)建BPMN文件
創(chuàng)建BPMN文件
創(chuàng)建BPMN文件

-
查看流程圖代碼
重構(gòu)
重構(gòu)
<?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="m1569472781925" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="myProcess_1" isClosed="false" isExecutable="true" processType="None">
<startEvent id="_2" name="開始"/>
<endEvent id="_3" name="結(jié)束"/>
<userTask activiti:exclusive="true" id="_4" name="學生請假"/>
<userTask activiti:exclusive="true" id="_5" name="老師申批"/>
<sequenceFlow id="_6" sourceRef="_2" targetRef="_4"/>
<sequenceFlow id="_7" sourceRef="_4" targetRef="_5"/>
<sequenceFlow id="_8" sourceRef="_5" targetRef="_3"/>
</process>
<bpmndi:BPMNDiagram documentation="background=#3C3F41;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="myProcess_1">
<bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
<omgdc:Bounds height="32.0" width="32.0" x="190.0" y="55.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="32.0" width="32.0" x="195.0" y="335.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.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="140.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="165.0" y="225.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6" sourceElement="_2" targetElement="_4">
<omgdi:waypoint x="206.0" y="87.0"/>
<omgdi:waypoint x="206.0" y="140.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_7" id="BPMNEdge__7" sourceElement="_4" targetElement="_5">
<omgdi:waypoint x="205.0" y="195.0"/>
<omgdi:waypoint x="205.0" y="225.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="_5" targetElement="_3">
<omgdi:waypoint x="211.0" y="280.0"/>
<omgdi:waypoint x="211.0" y="335.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>
-
生成圖片
生成圖片
生成圖片
生成圖片
生成圖片 -
部署流程
部署之后就可以在act_re_procdef表中看到對相應(yīng)的流程信息
獲取流程ID
/**
* 數(shù)據(jù)存儲服務(wù)
*/
@Resource
private RepositoryService repositoryService;
/**
* 1. 部署流程
* 部署之后就可以在act_re_procdef表中看到對相應(yīng)的流程信息
*/
@Test
public void deployProcess() {
DeploymentBuilder builder = repositoryService.createDeployment();
// bpmn文件的名稱
builder.addClasspathResource("processes/test.bpmn");
// 設(shè)置key
builder.key("myProcess_1");
// 設(shè)定名稱,也可以在圖中定義
builder.name("請假流程");
// 進行布署
Deployment deployment = builder.deploy();
// 獲取deployment的ID
String deploymentId = deployment.getId();
// 根據(jù)deploymentId來獲取流程定義對象
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deploymentId).singleResult();
log.info("流程定義文件 [{}] , 流程ID [{}]", processDefinition.getName(), processDefinition.getId());
}
注意:
默認,必須為.bpmn文件或 bpmn20.xml文件,可通過配置修改。
- 啟動流程
/**
* 運行服務(wù)
*/
@Resource
private RuntimeService runtimeService;
/**
* 2. 啟動流程
* 啟動流程之后就會有相應(yīng)的任務(wù)產(chǎn)生,存在act_ru_task表中,可以查看任務(wù)節(jié)點
*/
@Test
public void startProcess() {
// 流程的名稱,也可以使用ByID來啟動流程
// key為流程圖的ID,
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_1");
log.info("流程啟動成功,流程id:" + processInstance.getId());
}
-
查看任務(wù)節(jié)點
指定用戶
/**
* 任務(wù)服務(wù)
*/
@Resource
private TaskService taskService;
/**
* 3. 查看任務(wù)節(jié)點
*/
@Test
public void queryTask() {
// 根據(jù)assignee(代理人)查詢?nèi)蝿?wù)
String assignee = "admin";
// 注意所在包。
List<Task> tasks = taskService.createTaskQuery().taskAssignee(assignee).list();
int size = tasks.size();
for (int i = 0; i < size; i++) {
Task task = tasks.get(i);
}
// 首次運行的時候這個沒有輸出,因為第一次運行的時候掃描act_ru_task的表里面是空的,
// 但第一次運行完成之后里面會添加一條記錄,之后每次運行里面都會添加一條記錄
for (Task task : tasks) {
log.info("taskId:" + task.getId() +
",taskName:" + task.getName() +
",assignee:" + task.getAssignee() +
",createTime:" + task.getCreateTime());
}
}

-
完成流程(審核通過)
審批數(shù)據(jù)
/**
* 4. 完成流程,admin通過審核
*/
@Test
public void completeTasks() {
// 審批后,任務(wù)列表數(shù)據(jù)減少
Map<String,Object> vars = new HashMap<>();
// 按配置的任務(wù)id填寫
vars.put("_4","true");
taskService.complete("2505", vars);
}
再次進行查詢admin的任務(wù),已經(jīng)有了。act_ru_task表中已沒有該記錄。
- 申請駁回
//審批不通過,結(jié)束流程
runtimeService.deleteProcessInstance(vacationAudit.getProcessInstanceId(), auditId);
- 轉(zhuǎn)辦
- 簽收
- 歷史任務(wù)查詢
/**
* 歷史服務(wù)
*/
@Resource
private HistoryService historyService;
/**
* 歷史任務(wù)查詢
*
* @throws ParseException
*/
@Test
public void findHistoricTasks() throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery() // 創(chuàng)建歷史任務(wù)實例查詢
.taskAssignee("admin") // 指定辦理人
// .finished() // 查詢已經(jīng)完成的任務(wù)
.list();
for (HistoricTaskInstance hti : list) {
log.info("任務(wù)ID:" + hti.getId());
log.info("流程實例ID:" + hti.getProcessInstanceId());
log.info("班里人:" + hti.getAssignee());
log.info("創(chuàng)建時間:" + sdf.format(hti.getCreateTime()));
log.info("結(jié)束時間:" + sdf.format(hti.getEndTime()));
log.info("===========================");
}
}
歷史活動查詢
/**
* 歷史活動查詢
* 指定流程實例id,啟動流程時,獲取的實例ID
*/
@Test
public void historyActInstanceList(){
List<HistoricActivityInstance> list = historyService // 歷史任務(wù)Service
.createHistoricActivityInstanceQuery() // 創(chuàng)建歷史活動實例查詢
.processInstanceId("2501") // 指定流程實例id
// .finished() // 查詢已經(jīng)完成的任務(wù)
.list();
for (HistoricActivityInstance hai : list) {
log.info("任務(wù)ID:" + hai.getId());
log.info("流程實例ID:" + hai.getProcessInstanceId());
log.info("活動名稱:" + hai.getActivityName());
log.info("辦理人:" + hai.getAssignee());
log.info("開始時間:" + hai.getStartTime());
log.info("結(jié)束時間:" + hai.getEndTime());
log.info("===========================");
}
}
八、常見問題:
- caused by: java.lang.ArrayStoreException: sun.reflect.annotation.TypeNotPresentExceptionProxy
解決:
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
- 不能自動創(chuàng)建表結(jié)構(gòu)
- 從mysql-connector-java 5.x 到 6.x,nullCatalogMeansCurrent屬性由原來的默認true改為了false。
- true 使用指定的數(shù)據(jù)庫進行查詢。優(yōu)先取當前傳入的數(shù)據(jù)庫名,其次取當前鏈接的數(shù)據(jù)庫名。
將連接字符串追加:
nullCatalogMeansCurrent=true
jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&characterEncoding=UTF-8&useSSL=false&autoReconnect=true&failOverReadOnly=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
- idea activiti報錯no processes deployed with key 'leave'
出錯原因:processes目錄下
leave.xml改為leave.bpmn問題解決
那就是 activiti 的模版必須以 bpmn20.xml 或者 bpmn結(jié)尾















