Springboot整合Activiti操作詳解
版本依賴
- 開發(fā)工具 IDEA
- SpringBoot 2.4.5(這里我試過SpringBoot 3.1.1版本,Activiti沒有啟動(dòng),應(yīng)該是依賴沖突了,后改成了2.4.5版本)
- Activiti 7.1.0.M6
父項(xiàng)目pom.xml
<dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.activiti.dependencies/activiti-dependencies -->
<dependency>
<groupId>org.activiti.dependencies</groupId>
<artifactId>activiti-dependencies</artifactId>
<version>7.1.0.M6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>子項(xiàng)目pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
username: root
password: 111111
url: jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&useInformationSchema=true
hikari:
#連接池做大連接數(shù)
maximum-pool-size: 30
#連接池空閑連接最小數(shù)量
#minimum-idle: 10
#允許連接在連接池中閑置最長時(shí)間
#idle-timeout: 30000
#池中連接最長生命周期
max-lifetime: 120000
#等待來自池的連接的最大毫秒數(shù)
connection-timeout: 30000
activiti:
#自動(dòng)更新數(shù)據(jù)庫結(jié)構(gòu)
#1.flase:默認(rèn)值。activiti在啟動(dòng)時(shí),對比數(shù)據(jù)庫表中保存的版本,如果沒有表或者版本不匹配,將拋出異常
#2.true: activiti會(huì)對數(shù)據(jù)庫中所有表進(jìn)行更新操作。如果表不存在,則自動(dòng)創(chuàng)建
#3.create_drop: 在activiti啟動(dòng)時(shí)創(chuàng)建表,在關(guān)閉時(shí)刪除表(必須手動(dòng)關(guān)閉引擎,才能刪除表)
#4.drop-create: 在activiti啟動(dòng)時(shí)刪除原來的舊表,然后在創(chuàng)建新表(不需要手動(dòng)關(guān)閉引擎)
database-schema-update: true
#activiti7默認(rèn)不生成歷史信息表,開啟歷史表
db-history-used: true
#記錄歷史等級 可配置的歷史級別有none, activity, audit, full
#none:不保存任何的歷史數(shù)據(jù),因此,在流程執(zhí)行過程中,這是最高效的。
#activity:級別高于none,保存流程實(shí)例與流程行為,其他數(shù)據(jù)不保存。
#audit:除activity級別會(huì)保存的數(shù)據(jù)外,還會(huì)保存全部的流程任務(wù)及其屬性。audit為history的默認(rèn)值。
#full:保存歷史數(shù)據(jù)的最高級別,除了會(huì)保存audit級別的數(shù)據(jù)外,還會(huì)保存其他全部流程相關(guān)的細(xì)節(jié)數(shù)據(jù),包括一些流程參數(shù)等。
history-level: full
#自動(dòng)檢查、部署流程定義文件
check-process-definitions: false
# asyncExecutorActivate是指activiti在流程引擎啟動(dòng)就激活A(yù)syncExecutor,異步:true-開啟(默認(rèn))、false-關(guān)閉
async-executor-activate: true配置文件這里注意要設(shè)置 database-sechema-update的屬性為true,才會(huì)自動(dòng)創(chuàng)建表。
需要注意的問題
在初次啟動(dòng)時(shí)可能會(huì)報(bào)錯(cuò),報(bào)錯(cuò)如下:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.core.userdetails.UserDetailsService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1790)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1346)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
... 142 more這里是需要一個(gè)UserDetailsService的Bean,需要?jiǎng)?chuàng)建一個(gè)類去實(shí)現(xiàn)UserDetailsService接口
UserDetailsServiceImpl.class
package org.example.config;
import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userService.findOneUserByName(username);
}
}UserService.class
package org.example.service;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
public User findOneUserByName(String username){
List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
return new User(username,"",authorities);
}
}啟動(dòng)成功后,數(shù)據(jù)庫會(huì)生成25張表

畫流程圖
在使用IDEA時(shí),需要下載一個(gè)插件

當(dāng)這個(gè)插件安裝好以后,新建文件會(huì)多一個(gè)選項(xiàng)

新建的xml文件可以通過view BPMN Diagram,轉(zhuǎn)化為圖表操作


如何通過圖表操作畫流程這里就不說了,可以自己摸索看看。
activiti服務(wù)類進(jìn)行編寫
package org.example.service;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
@Service
public class ActivitiService {
@Autowired
RepositoryService repositoryService;
@Autowired
RuntimeService runtimeService;
@Autowired
TaskService taskService;
@Autowired
HistoryService historyService;
/**
* 部署流程服務(wù)
*/
public void deployProcess(){
String file = "bpmn/test.bpmn20.xml";
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource(file)
.name("流程部署測試")
.deploy();
System.out.println("流程部署名稱:"+deployment.getName());
}
/**
* 查詢所有部署的流程定義
* @return
*/
public List<ProcessDefinition> getAllProcessDefinition(){
ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
return query.orderByProcessDefinitionVersion().desc().list();
}
/**
* 查詢所有的部署
* @return
*/
public List<Deployment> getAllDeployment(){
DeploymentQuery query = repositoryService.createDeploymentQuery();
return query.list();
}
/**
* 查詢所有流程實(shí)例
* @return
*/
public List<ProcessInstance> getAllProcessInstance(){
List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().list();
return list;
}
public List<Task> getAllProcessTask(){
List<Task> list = taskService.createTaskQuery().list();
return list;
}
/**
* 查詢用戶待完成和待認(rèn)領(lǐng)的任務(wù)
* @param username
* @return
*/
public List<Task> getAllProcessTaskByCandidateOrAssigned(String username){
List<Task> list = taskService.createTaskQuery().taskCandidateOrAssigned(username).list();
return list;
}
public List<Task> getAllProcessTaskByCandidate(String username){
List<Task> list = taskService.createTaskQuery().taskCandidateUser(username).list();
return list;
}
/**
* 查詢用戶的待完成任務(wù)
* @param username
* @return
*/
public List<Task> getAllProcessTaskByAssigned(String username){
List<Task> list = taskService.createTaskQuery().taskAssignee(username).list();
return list;
}
public void complateTask(Task task, Map<String, Object> map){
taskService.complete(task.getId(),map);
}
public void complateTask(Task task){
taskService.complete(task.getId());
}
public void claimTask(Task task,String username){
taskService.claim(task.getId(),username);
}
public ProcessInstance startProcess(String processDefinitionKey,String businessKey,Map<String,Object> map){
return runtimeService.startProcessInstanceById(processDefinitionKey,businessKey,map);
}
public String getProcessDefinitionXml(String processDefinitionId) {
ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
String resourceName = processDefinition.getResourceName();
InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
try {
return IOUtils.toString(resourceAsStream, StandardCharsets.UTF_8);
} catch (IOException e) {
// handle exception
}
return null;
}
public List<HistoricProcessInstance> getHistoryByBusinessKey(String businessKey){
List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).list();
return list;
}
}
上圖是,如果需要使用activiti中提供的服務(wù)類,直接注入即可。即springboot整合activiti啟動(dòng)過程中,已經(jīng)在容器中自動(dòng)裝配了對應(yīng)的服務(wù)類,需要的時(shí)候,僅僅需要取來用即可。如果啟動(dòng)以后報(bào)錯(cuò),容器沒有自動(dòng)裝配對應(yīng)的服務(wù)類,那很有可能是依賴沖突,activiti沒有正常啟動(dòng)。
流程部署
流程部署有多種方式,這是通過資源路徑部署的,也可以通過壓縮包的形式部署流程。那就需要用到DeploymentBuilder的addZipInputStream方法,這里也是用到了設(shè)計(jì)模式中的建造者模式。


流程定義
這是一個(gè)查詢所有部署的流程定義的方法

當(dāng)一個(gè)流程部署以后,就會(huì)生成一個(gè)流程定義,流程圖的xml信息也會(huì)存入數(shù)據(jù)庫。每次部署流程圖都會(huì)生成一個(gè)新的流程定義。

啟動(dòng)流程
啟動(dòng)流程可以通過流程定義的id或者key,如果想通過流程定義的id啟動(dòng)一個(gè)流程可以使用startProcessInstanceById方法,如果想通過流程定義的key啟動(dòng)一個(gè)流程可以使用startProcessInstanceByKey方法。

這里要明白流程定義id和key的區(qū)別,流程定義的key是在畫流程的時(shí)候可以設(shè)置的,通過設(shè)置流程的id,這個(gè)就是流程的key。


那流程的id呢,這是部署以后在數(shù)據(jù)庫表的id。可以根據(jù)需要自行選擇啟動(dòng)流程的方法。
流程實(shí)例
查詢所有啟動(dòng)的流程實(shí)例

每次開始一個(gè)流程以后,就會(huì)生成一個(gè)流程實(shí)例。通過這個(gè)方法可以查看所有的流程實(shí)例。
測試流程
package org.example;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.assertj.core.util.DateUtil;
import org.example.common.PrintUtil;
import org.example.entity.User;
import org.example.service.ActivitiService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;
@SpringBootTest
public class SpringBootApplicationTest {
@Resource
DataSource dataSource;
@Autowired
ActivitiService activitiService;
@Test
void context() throws SQLException {
System.out.println(dataSource.getConnection());
}
@Test
public void deployProcess(){
activitiService.deployProcess();
}
@Test
public void TestStartProcess(){
ProcessDefinition processDefinition = activitiService.getAllProcessDefinition().get(0);
String bussinessKey = this.getClass().getSimpleName();
Map<String , Object> map = new HashMap<String , Object>();
map.put("creator","Echo");
activitiService.startProcess(processDefinition.getId(),bussinessKey,map);
}
@Test
public void findAllDeployment(){
List<Deployment> list = activitiService.getAllDeployment();
PrintUtil.printTable(list);
}
@Test
public void findAllProcessDefinition(){
List<ProcessDefinition> list = activitiService.getAllProcessDefinition();
PrintUtil.printTable(list);
}
@Test
public void findAllProcessInstance(){
List<ProcessInstance> list = activitiService.getAllProcessInstance();
PrintUtil.printTable(list);
}
@Test
public void findAllProcessTask(){
List<Task> list = activitiService.getAllProcessTask();
PrintUtil.printTable(list);
}
@Test
public void findTaskByUserId(){
String userId = "keaizp";
List<Task> list = activitiService.getAllProcessTaskByCandidateOrAssigned(userId);
PrintUtil.printTable(list);
}
@Test
public void claimTask(){
String userId="keaizp";
List<Task> list = activitiService.getAllProcessTaskByCandidate(userId);
PrintUtil.printTable(list);
activitiService.claimTask(list.get(0),userId);
list = activitiService.getAllProcessTaskByCandidate("zengpei");
PrintUtil.printTable(list);
}
@Test
public void complateUserTask(){
String userId = "Echo";
List<Task> list = activitiService.getAllProcessTaskByAssigned(userId);
List<String> candidateUsers = new ArrayList<String>();
candidateUsers.add("keaizp");
candidateUsers.add("zengpei");
Map<String,Object> map = new HashMap<String,Object>();
map.put("approver",candidateUsers);
activitiService.complateTask(list.get(0),map);
}
@Test
public void processDefinitionXml(){
List<ProcessDefinition> list = activitiService.getAllProcessDefinition();
String xmlStr = activitiService.getProcessDefinitionXml(list.get(0).getId());
System.out.println(xmlStr);
}
@Test
public void findHistoryByBusinessKey(){
List<HistoricProcessInstance> list = activitiService.getHistoryByBusinessKey("2023-07-02T22:45:45");
PrintUtil.printTable(list);
}
@Test
public void printTest(){
List<User> users = new ArrayList<>();
users.add(new User("keaizpeeeeeeeee",24,"male"));
users.add(new User("Echo",24,"male"));
users.add(new User("nick",24,"male"));
users.add(new User("amy",24,"male"));
PrintUtil.printTableFixed(users,16);
}
}啟動(dòng)流程

這里我用的是一個(gè)格式化的日期做bussinessKey,實(shí)際業(yè)務(wù)中最好使用 流程作業(yè)類型 + 一個(gè)業(yè)務(wù)臺(tái)賬id,比如:請假申請要走審批流程, 離職申請也要走審批流程,如果只用id作為bussinessKey的話,bussinessKey就無法滿足唯一性。所以最好 前面加上作業(yè)類型。這里的map傳入的是流程圖里面的參數(shù)。

傳入受理人,這里就會(huì)產(chǎn)生一個(gè)Task,就是用戶的代辦任務(wù)。
完成任務(wù)

當(dāng)用戶登錄系統(tǒng)的時(shí)候,應(yīng)該給用戶提示,用戶有未完成的代辦事項(xiàng),然后給出用戶代辦事項(xiàng)列表,做完一切以后,就是完成用戶任務(wù),這個(gè)時(shí)候,可以傳入下一流程節(jié)點(diǎn)參與任務(wù)的人,當(dāng)然你也可以傳入幾個(gè)候選人,candidate Users就是 候選人??梢詡魅胍粋€(gè)用戶id的列表,然后這幾個(gè)人就會(huì)收到可以受理的任務(wù),可以選擇是否接受該任務(wù)。只要接受了任務(wù),其他候選人就無法再接受此任務(wù),同時(shí)這個(gè)任務(wù)的Assignee就會(huì)設(shè)置成受理人的id
受理任務(wù)

因?yàn)橛脩?code>“keaizp”成為了候選者,他就可以看到自己可以受理的這個(gè)任務(wù),用claim方法就可以受理該任務(wù),當(dāng)受理任務(wù)完成以后,再去看看另外一名候選者的受理任務(wù),會(huì)發(fā)現(xiàn)已經(jīng)沒有待受理的任務(wù)了。
以上就是Springboot整合Activiti操作詳解的詳細(xì)內(nèi)容,更多關(guān)于Springboot整合Activiti的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
URLConnection發(fā)送HTTP請求的方法_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了URLConnection發(fā)送HTTP請求的方法,主要介紹了如何通過Java(模擬瀏覽器)發(fā)送HTTP請求,有興趣的可以了解一下2017-07-07
java利用mybatis攔截器統(tǒng)計(jì)sql執(zhí)行時(shí)間示例
這篇文章主要介紹了java利用mybatis攔截器統(tǒng)計(jì)sql執(zhí)行時(shí)間示例,該攔截器攔截mybatis的query和update操作,能統(tǒng)計(jì)sql執(zhí)行時(shí)間2014-03-03
Java源碼解析阻塞隊(duì)列ArrayBlockingQueue功能簡介
今天小編就為大家分享一篇關(guān)于Java源碼解析阻塞隊(duì)列ArrayBlockingQueue功能簡介,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-01-01
Springboot 使用具體化類和配置來縮短單元測試時(shí)間
這篇文章主要介紹了Springboot 使用具體化類和配置來縮短單元測試時(shí)間,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
maven項(xiàng)目test執(zhí)行main找不到資源文件的問題及解決
這篇文章主要介紹了maven項(xiàng)目test執(zhí)行main找不到資源文件的問題及解決,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03

