Springboot整合Activiti操作詳解
版本依賴(lài)
- 開(kāi)發(fā)工具 IDEA
- SpringBoot 2.4.5(這里我試過(guò)SpringBoot 3.1.1版本,Activiti沒(méi)有啟動(dòng),應(yīng)該是依賴(lài)沖突了,后改成了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 #允許連接在連接池中閑置最長(zhǎng)時(shí)間 #idle-timeout: 30000 #池中連接最長(zhǎng)生命周期 max-lifetime: 120000 #等待來(lái)自池的連接的最大毫秒數(shù) connection-timeout: 30000 activiti: #自動(dòng)更新數(shù)據(jù)庫(kù)結(jié)構(gòu) #1.flase:默認(rèn)值。activiti在啟動(dòng)時(shí),對(duì)比數(shù)據(jù)庫(kù)表中保存的版本,如果沒(méi)有表或者版本不匹配,將拋出異常 #2.true: activiti會(huì)對(duì)數(shù)據(jù)庫(kù)中所有表進(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í)刪除原來(lái)的舊表,然后在創(chuàng)建新表(不需要手動(dòng)關(guān)閉引擎) database-schema-update: true #activiti7默認(rèn)不生成歷史信息表,開(kāi)啟歷史表 db-history-used: true #記錄歷史等級(jí) 可配置的歷史級(jí)別有none, activity, audit, full #none:不保存任何的歷史數(shù)據(jù),因此,在流程執(zhí)行過(guò)程中,這是最高效的。 #activity:級(jí)別高于none,保存流程實(shí)例與流程行為,其他數(shù)據(jù)不保存。 #audit:除activity級(jí)別會(huì)保存的數(shù)據(jù)外,還會(huì)保存全部的流程任務(wù)及其屬性。audit為history的默認(rèn)值。 #full:保存歷史數(shù)據(jù)的最高級(jí)別,除了會(huì)保存audit級(jí)別的數(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-開(kāi)啟(默認(rèn))、false-關(guān)閉 async-executor-activate: true
配置文件這里注意要設(shè)置 database-sechema-update
的屬性為true
,才會(huì)自動(dòng)創(chuàng)建表。
需要注意的問(wèn)題
在初次啟動(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è)類(lèi)去實(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ù)庫(kù)會(huì)生成25張表
畫(huà)流程圖
在使用IDEA時(shí),需要下載一個(gè)插件
當(dāng)這個(gè)插件安裝好以后,新建文件會(huì)多一個(gè)選項(xiàng)
新建的xml文件可以通過(guò)view BPMN Diagram,轉(zhuǎn)化為圖表操作
如何通過(guò)圖表操作畫(huà)流程這里就不說(shuō)了,可以自己摸索看看。
activiti服務(wù)類(lèi)進(jìn)行編寫(xiě)
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("流程部署測(cè)試") .deploy(); System.out.println("流程部署名稱(chēng):"+deployment.getName()); } /** * 查詢(xún)所有部署的流程定義 * @return */ public List<ProcessDefinition> getAllProcessDefinition(){ ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery(); return query.orderByProcessDefinitionVersion().desc().list(); } /** * 查詢(xún)所有的部署 * @return */ public List<Deployment> getAllDeployment(){ DeploymentQuery query = repositoryService.createDeploymentQuery(); return query.list(); } /** * 查詢(xún)所有流程實(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; } /** * 查詢(xún)用戶(hù)待完成和待認(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; } /** * 查詢(xún)用戶(hù)的待完成任務(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ù)類(lèi),直接注入即可。即springboot整合activiti啟動(dòng)過(guò)程中,已經(jīng)在容器中自動(dòng)裝配了對(duì)應(yīng)的服務(wù)類(lèi),需要的時(shí)候,僅僅需要取來(lái)用即可。如果啟動(dòng)以后報(bào)錯(cuò),容器沒(méi)有自動(dòng)裝配對(duì)應(yīng)的服務(wù)類(lèi),那很有可能是依賴(lài)沖突,activiti沒(méi)有正常啟動(dòng)。
流程部署
流程部署有多種方式,這是通過(guò)資源路徑部署的,也可以通過(guò)壓縮包的形式部署流程。那就需要用到DeploymentBuilder
的addZipInputStream
方法,這里也是用到了設(shè)計(jì)模式中的建造者模式。
流程定義
這是一個(gè)查詢(xún)所有部署的流程定義的方法
當(dāng)一個(gè)流程部署以后,就會(huì)生成一個(gè)流程定義,流程圖的xml信息也會(huì)存入數(shù)據(jù)庫(kù)。每次部署流程圖都會(huì)生成一個(gè)新的流程定義。
啟動(dòng)流程
啟動(dòng)流程可以通過(guò)流程定義的id或者key,如果想通過(guò)流程定義的id啟動(dòng)一個(gè)流程可以使用startProcessInstanceById
方法,如果想通過(guò)流程定義的key啟動(dòng)一個(gè)流程可以使用startProcessInstanceByKey
方法。
這里要明白流程定義id和key的區(qū)別,流程定義的key是在畫(huà)流程的時(shí)候可以設(shè)置的,通過(guò)設(shè)置流程的id,這個(gè)就是流程的key。
那流程的id呢,這是部署以后在數(shù)據(jù)庫(kù)表的id??梢愿鶕?jù)需要自行選擇啟動(dòng)流程的方法。
流程實(shí)例
查詢(xún)所有啟動(dòng)的流程實(shí)例
每次開(kāi)始一個(gè)流程以后,就會(huì)生成一個(gè)流程實(shí)例。通過(guò)這個(gè)方法可以查看所有的流程實(shí)例。
測(cè)試流程
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è)類(lèi)型 + 一個(gè)業(yè)務(wù)臺(tái)賬id,比如:請(qǐng)假申請(qǐng)要走審批流程, 離職申請(qǐng)也要走審批流程,如果只用id作為bussinessKey的話(huà),bussinessKey就無(wú)法滿(mǎn)足唯一性。所以最好 前面加上作業(yè)類(lèi)型。這里的map傳入的是流程圖里面的參數(shù)。
傳入受理人,這里就會(huì)產(chǎn)生一個(gè)Task,就是用戶(hù)的代辦任務(wù)。
完成任務(wù)
當(dāng)用戶(hù)登錄系統(tǒng)的時(shí)候,應(yīng)該給用戶(hù)提示,用戶(hù)有未完成的代辦事項(xiàng),然后給出用戶(hù)代辦事項(xiàng)列表,做完一切以后,就是完成用戶(hù)任務(wù),這個(gè)時(shí)候,可以傳入下一流程節(jié)點(diǎn)參與任務(wù)的人,當(dāng)然你也可以傳入幾個(gè)候選人,candidate Users
就是 候選人。可以傳入一個(gè)用戶(hù)id的列表,然后這幾個(gè)人就會(huì)收到可以受理的任務(wù),可以選擇是否接受該任務(wù)。只要接受了任務(wù),其他候選人就無(wú)法再接受此任務(wù),同時(shí)這個(gè)任務(wù)的Assignee
就會(huì)設(shè)置成受理人的id
受理任務(wù)
因?yàn)橛脩?hù)“keaizp”
成為了候選者,他就可以看到自己可以受理的這個(gè)任務(wù),用claim
方法就可以受理該任務(wù),當(dāng)受理任務(wù)完成以后,再去看看另外一名候選者的受理任務(wù),會(huì)發(fā)現(xiàn)已經(jīng)沒(méi)有待受理的任務(wù)了。
以上就是Springboot整合Activiti操作詳解的詳細(xì)內(nèi)容,更多關(guān)于Springboot整合Activiti的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
URLConnection發(fā)送HTTP請(qǐng)求的方法_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了URLConnection發(fā)送HTTP請(qǐng)求的方法,主要介紹了如何通過(guò)Java(模擬瀏覽器)發(fā)送HTTP請(qǐng)求,有興趣的可以了解一下2017-07-07java利用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-03Java源碼解析阻塞隊(duì)列ArrayBlockingQueue功能簡(jiǎn)介
今天小編就為大家分享一篇關(guān)于Java源碼解析阻塞隊(duì)列ArrayBlockingQueue功能簡(jiǎn)介,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01Springboot 使用具體化類(lèi)和配置來(lái)縮短單元測(cè)試時(shí)間
這篇文章主要介紹了Springboot 使用具體化類(lèi)和配置來(lái)縮短單元測(cè)試時(shí)間,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11如何在Java中判斷兩個(gè)Long類(lèi)型是否相等
這篇文章主要介紹了如何在Java中判斷兩個(gè)Long類(lèi)型是否相等,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的?參考價(jià)值,需要的小伙伴可以參考一下2022-09-09maven項(xiàng)目test執(zhí)行main找不到資源文件的問(wèn)題及解決
這篇文章主要介紹了maven項(xiàng)目test執(zhí)行main找不到資源文件的問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03