Springboot整合Activiti操作詳解
版本依賴
- 開發(fā)工具 IDEA
- SpringBoot 2.4.5(這里我試過SpringBoot 3.1.1版本,Activiti沒有啟動,應(yīng)該是依賴沖突了,后改成了2.4.5版本)
- Activiti 7.1.0.M6
父項目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>
子項目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 #允許連接在連接池中閑置最長時間 #idle-timeout: 30000 #池中連接最長生命周期 max-lifetime: 120000 #等待來自池的連接的最大毫秒數(shù) connection-timeout: 30000 activiti: #自動更新數(shù)據(jù)庫結(jié)構(gòu) #1.flase:默認(rèn)值。activiti在啟動時,對比數(shù)據(jù)庫表中保存的版本,如果沒有表或者版本不匹配,將拋出異常 #2.true: activiti會對數(shù)據(jù)庫中所有表進(jìn)行更新操作。如果表不存在,則自動創(chuàng)建 #3.create_drop: 在activiti啟動時創(chuàng)建表,在關(guān)閉時刪除表(必須手動關(guān)閉引擎,才能刪除表) #4.drop-create: 在activiti啟動時刪除原來的舊表,然后在創(chuà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ù)據(jù)不保存。 #audit:除activity級別會保存的數(shù)據(jù)外,還會保存全部的流程任務(wù)及其屬性。audit為history的默認(rèn)值。 #full:保存歷史數(shù)據(jù)的最高級別,除了會保存audit級別的數(shù)據(jù)外,還會保存其他全部流程相關(guān)的細(xì)節(jié)數(shù)據(jù),包括一些流程參數(shù)等。 history-level: full #自動檢查、部署流程定義文件 check-process-definitions: false # asyncExecutorActivate是指activiti在流程引擎啟動就激活A(yù)syncExecutor,異步:true-開啟(默認(rèn))、false-關(guān)閉 async-executor-activate: true
配置文件這里注意要設(shè)置 database-sechema-update
的屬性為true
,才會自動創(chuàng)建表。
需要注意的問題
在初次啟動時可能會報錯,報錯如下:
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
這里是需要一個UserDetailsService
的Bean,需要創(chuàng)建一個類去實現(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); } }
啟動成功后,數(shù)據(jù)庫會生成25張表
畫流程圖
在使用IDEA時,需要下載一個插件
當(dā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(); } /** * 查詢所有流程實例 * @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啟動過程中,已經(jīng)在容器中自動裝配了對應(yīng)的服務(wù)類,需要的時候,僅僅需要取來用即可。如果啟動以后報錯,容器沒有自動裝配對應(yīng)的服務(wù)類,那很有可能是依賴沖突,activiti沒有正常啟動。
流程部署
流程部署有多種方式,這是通過資源路徑部署的,也可以通過壓縮包的形式部署流程。那就需要用到DeploymentBuilder
的addZipInputStream
方法,這里也是用到了設(shè)計模式中的建造者模式。
流程定義
這是一個查詢所有部署的流程定義的方法
當(dāng)一個流程部署以后,就會生成一個流程定義,流程圖的xml信息也會存入數(shù)據(jù)庫。每次部署流程圖都會生成一個新的流程定義。
啟動流程
啟動流程可以通過流程定義的id或者key,如果想通過流程定義的id啟動一個流程可以使用startProcessInstanceById
方法,如果想通過流程定義的key啟動一個流程可以使用startProcessInstanceByKey
方法。
這里要明白流程定義id和key的區(qū)別,流程定義的key是在畫流程的時候可以設(shè)置的,通過設(shè)置流程的id,這個就是流程的key。
那流程的id呢,這是部署以后在數(shù)據(jù)庫表的id??梢愿鶕?jù)需要自行選擇啟動流程的方法。
流程實例
查詢所有啟動的流程實例
每次開始一個流程以后,就會生成一個流程實例。通過這個方法可以查看所有的流程實例。
測試流程
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); } }
啟動流程
這里我用的是一個格式化的日期做bussinessKey,實際業(yè)務(wù)中最好使用 流程作業(yè)類型 + 一個業(yè)務(wù)臺賬id,比如:請假申請要走審批流程, 離職申請也要走審批流程,如果只用id作為bussinessKey的話,bussinessKey就無法滿足唯一性。所以最好 前面加上作業(yè)類型。這里的map傳入的是流程圖里面的參數(shù)。
傳入受理人,這里就會產(chǎn)生一個Task,就是用戶的代辦任務(wù)。
完成任務(wù)
當(dāng)用戶登錄系統(tǒng)的時候,應(yīng)該給用戶提示,用戶有未完成的代辦事項,然后給出用戶代辦事項列表,做完一切以后,就是完成用戶任務(wù),這個時候,可以傳入下一流程節(jié)點參與任務(wù)的人,當(dāng)然你也可以傳入幾個候選人,candidate Users
就是 候選人??梢詡魅胍粋€用戶id的列表,然后這幾個人就會收到可以受理的任務(wù),可以選擇是否接受該任務(wù)。只要接受了任務(wù),其他候選人就無法再接受此任務(wù),同時這個任務(wù)的Assignee
就會設(shè)置成受理人的id
受理任務(wù)
因為用戶“keaizp”
成為了候選者,他就可以看到自己可以受理的這個任務(wù),用claim
方法就可以受理該任務(wù),當(dāng)受理任務(wù)完成以后,再去看看另外一名候選者的受理任務(wù),會發(fā)現(xiàn)已經(jīng)沒有待受理的任務(wù)了。
以上就是Springboot整合Activiti操作詳解的詳細(xì)內(nèi)容,更多關(guān)于Springboot整合Activiti的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
URLConnection發(fā)送HTTP請求的方法_動力節(jié)點Java學(xué)院整理
這篇文章主要介紹了URLConnection發(fā)送HTTP請求的方法,主要介紹了如何通過Java(模擬瀏覽器)發(fā)送HTTP請求,有興趣的可以了解一下2017-07-07java利用mybatis攔截器統(tǒng)計sql執(zhí)行時間示例
這篇文章主要介紹了java利用mybatis攔截器統(tǒng)計sql執(zhí)行時間示例,該攔截器攔截mybatis的query和update操作,能統(tǒng)計sql執(zhí)行時間2014-03-03Java源碼解析阻塞隊列ArrayBlockingQueue功能簡介
今天小編就為大家分享一篇關(guān)于Java源碼解析阻塞隊列ArrayBlockingQueue功能簡介,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01maven項目test執(zhí)行main找不到資源文件的問題及解決
這篇文章主要介紹了maven項目test執(zhí)行main找不到資源文件的問題及解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03