Flowable整合SpringBoot實現(xiàn)的示例代碼
一、SringBoot整合Flowable
1.引入依賴
SpringBoot使用2.7.1,親測3.3.0不能用,JDK使用1.8
建議slf4j版本如下,不會報錯,太高了報錯
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter</artifactId> <version>6.7.2</version> <!--關閉自帶的權限認證--> <exclusions> <exclusion> <groupId>org.flowable</groupId> <artifactId>flowable-spring-security</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.21</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> <!-- 日志--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>2.0.0</version> </dependency>
2.安裝流程圖繪制插件
Flowable BPMN visualizer
3.yml配置
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver #nullCatalogMeansCurrent=true 設置為只查當前連接的schema庫 url: jdbc:mysql://localhost:3306/flowable?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true username: root password: root flowable: #關閉定時任務 async-executor-activate: true #數(shù)據(jù)庫表與flowable最新表不一致會進行更新 database-schema-update: true logging: level: org: flowable: debug
二、Spring環(huán)境下的應用
1.流程部署與啟動
@Autowired ProcessEngine processEngine; @Autowired RepositoryService repositoryService; @Autowired RuntimeService runtimeService; @Autowired TaskService taskService; @Test void deployFlow(){ //流程引擎的配置對象,關聯(lián)相關數(shù)據(jù)源 Deployment deploy = repositoryService.createDeployment() //一次部署所有processes文件夾內(nèi)的流程 .name("第一次部署") .deploy(); System.out.println("deploy.getId()="+deploy.getId()); } /** * 啟動流程實例 * 在流程定義表中動態(tài)維護 act_re_procdef */ @Test void startFlow(){ String processId="ask_for_leave.bpmn20:1:4"; String processKey="ask_for_leave.bpmn20"; //1.根據(jù)流程定義di啟動流程實例 runtimeService.startProcessInstanceById(processId); //2.根據(jù)流程定義key啟動流程實例 //runtimeService.startProcessInstanceByKey(processKey); } /** * 任務的審批 * 需要數(shù)據(jù):任務id */ @Test void compeleteTask(){ taskService.complete("2506"); }
每啟動一個流程,會在act_hi_procinst中維護一條數(shù)據(jù)。
啟動一個流程,可以在act_ru_task表中看到對應的記錄,該表記錄的都是當前待辦的記錄信息。
act_ru_execution記錄流程的分支
流程定義:相當于Java中的類
流程實例:相當于java中的對象
注意:在Spring環(huán)境下,Spring會自動掃描processes文件夾,若不指定文件路徑,則一次把所有bpmn流程全部部署,也就是一次創(chuàng)建所有流程定義。一個部署對應多個流程定義
@Test void deployFlow(){ Deployment deploy = repositoryService.createDeployment() //如果再添加部署文件,會部署兩次,導致流程定義中出現(xiàn)新版本的流程定義 .addClasspathResource("processes/ask_for_leave.bpmn20.xml") .name("部署名稱") .deploy(); System.out.println("deploy.getId()="+deploy.getId()); }
常用:1.通過bpmn部署。2.通過zip壓縮包部署。3.自己創(chuàng)建model模型,然后保存到數(shù)據(jù)庫中,再通過模型部署。
創(chuàng)建好model之后保存到act_re_model表中(上圖僅僅是舉個3的例子)。部署也是先從表中獲取model部署。
2.表結(jié)構
act_re: repository,包含流程定義和流程靜態(tài)資源(圖片,規(guī)則等)
act_ru: runtime,運行時的表,包含實例,任務,變量,異步任務,運行中的數(shù)據(jù)等
act_hi:history,流程的歷史數(shù)據(jù),如歷史流程實例,變量,任務。
- act_ge:general,通用數(shù)據(jù)。
act_ge_bytearray(重要),存放了流程定義的png圖片。
act_id:identity組織機構。包含標識的信息,如用戶,用戶組等。
CMMN 表示這都是跟 CMMN 協(xié)議相關的表。
CO(CONTENT)表示這都是跟內(nèi)容引擎相關的表。
DMN 表示這都是跟 DMN 協(xié)議相關的表。
FO(FORM)表示這都是跟表單相關的表。
3.流程掛起和激活
/** * 流程定義的掛起和激活 *act_re_procdef */ @Test void suspendedActivity(){ String processDefinitionId="ask_for_leave.bpmn20:1:4"; ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() .processDefinitionId(processDefinitionId) .singleResult(); //獲取當前流程定義的狀態(tài) boolean suspended = processDefinition.isSuspended(); if (suspended) { //掛起-->激活 System.out.println("激活流程"); repositoryService.activateProcessDefinitionById(processDefinitionId); }else { //激活-->掛起 System.out.println("掛起流程"); repositoryService.suspendProcessDefinitionById(processDefinitionId); } }
已經(jīng)掛起的流程定義,不允許啟動。
已經(jīng)啟動的流程,不受影響。
可以掛起已經(jīng)啟動的流程實例,該流程實例不允許審批。
4.任務分配表達式Assignee
Assignee也可以寫表達式
值表達式
${ assign1 },assign1是自己定義的變量。
方法表達式
${ myBean.getAssignee() }
/** * 任務的審批 * 需要數(shù)據(jù):任務id,hashMap "assign1","lisi" * 把當前任務分給lisi審批,運行后lisi可以查到待辦 */ @Test void compeleteAssign1(){ HashMap<String, Object> variables = new HashMap<>(); variables.put("assign1","lisi"); //完成任務審批,根據(jù)任務id綁定對應表達式的值 taskService.complete("taskId",variables); }
(act_ru_actinst)出現(xiàn)了lisi
@Test void findFlow(){ //任務實例通過TaskService來實現(xiàn) TaskService taskService = getEngine().getTaskService(); //獲取到 act_ru_task中 assignee是lisi的記錄 List<Task> tasks = taskService.createTaskQuery() .taskAssignee("lisi") .list(); tasks.forEach(System.out::println); //Task[id=15004, name=second] }
得到結(jié)果Task[id=15004, name=second],此時還需要審批。
------ 方法表達式${ myBean.getAssignee() } -------
//MyBean加入容器 @Component public class MyBean { public String getAssignee(){ System.out.println("getAssignee執(zhí)行..."); return "王五"; } } ?
@Test void compeleteAssign1(){ TaskService taskService = getEngine().getTaskService(); //再執(zhí)行審批時,會去MyBean中執(zhí)行該方法 taskService.complete("15004"); }
拿到id后再執(zhí)行審批。
5.流程變量
運行時變量
全局變量
局部變量
歷史變量
@Test void startFlow(){ String processId="firstFlow:2:637173cf-1ce6-11ef-8399-005056c00008"; //在流程啟動時就可以綁定對應表達式的值,因為第一個人就需要指定人 Map<String,Object> variables =new HashMap<>(); variables.put("var1","test1"); variables.put("var2","test2"); variables.put("var3","test3"); //全局變量存在了act_ru_variable runtimeService.startProcessInstanceById(processId,variables); } /** * 獲取流程全局變量 */ @Test //全局變量存在了act_ru_variable void getVariables(){ String execution ="06a934bf-1ce7-11ef-870f-005056c00008"; //還能直接設置 //runtimeService.setVariable(execution,"var4","test4"); //設置局部變量,和taskId相關,節(jié)點沒了變量就沒了 //runtimeService.setVariablesLocal(execution,"var4","test4"); Map<String, Object> variables = runtimeService.getVariables(execution); System.out.println(variables); }
輸出{var3=test3, var2=test2, var1=test1}
6.候選人
可以指定多個候選人,在啟動流程時進行賦值就好。
/** * 根據(jù)候選人查詢?nèi)蝿? * 候選人需要拾取任務才能變成審批人 *只有一個人能變?yōu)閷徟?,審批人還可以歸還,變成候選人 */ @Test void claimTask(){ //act_ru_task中 List<Task> tasks = taskService.createTaskQuery() //這里有改變 .taskCandidateUser("張三") .list(); for (Task task : tasks) { //拾取 taskService.claim(task.getId(),"張三"); //歸還unclaim(task.getId(),"張三") //指派taskService.setAssignee(task.getId(),"xxx") } } @Test void findFlow(){ //act_ru_task中 List<Task> tasks = taskService.createTaskQuery() //這里有改變 .taskCandidateUser("張三") .list(); tasks.forEach(System.out::println); }
7.候選人組
//先創(chuàng)建用戶 @Test void createUser(){ User user = identityService.newUser("zhangsan"); user.setEmail("zhansgan@qq.com"); user.setFirstName("zhang"); user.setLastName("san"); user.setPassword("123456"); identityService.saveUser(user); } /** * 用戶組 */ @Test void createGroup(){ Group group = identityService.newGroup("xsb"); group.setName("銷售部"); group.setType("type1"); identityService.saveGroup(group); } /** * 用戶與用戶組的關系 */ @Test void createMemberShip(){ Group group = identityService.createGroupQuery().groupId("xsb").singleResult(); List<User> users = identityService.createUserQuery().list(); users.forEach(user -> {identityService.createMembership(user.getId(), group.getId());}); }
act_id_group
act_id_membership
直接部署后啟動,act_ru_identitylink中就會出現(xiàn)候選組信息。
/** * 當前登錄用戶根據(jù)候選人組查詢?nèi)蝿? */ @Test void findGroupTask(){ //先查詢當前所在的組 如查張三 Group group = identityService.createGroupQuery() .groupMember("zhangsan").singleResult(); System.out.println("當前用戶所在組的id為:"+group.getId()); List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup(group.getId()).list(); for (Task task : tasks) { //拾取任務 taskService.claim(task.getId(),"zhangsan"); } } /** * 任務的審批 */ @Test void compeleteTask(){ Map<String,Object> variables =new HashMap<>(); taskService.complete("ae543ec1-1d5f-11ef-a409-005056c00008"); }
8.網(wǎng)關
排他網(wǎng)關
并行網(wǎng)關
包容網(wǎng)關
事件網(wǎng)關
排他網(wǎng)關
在審批的時候加入day天數(shù)就能完成請假步驟,從而轉(zhuǎn)向不同的審批人。
@Test void compeleteTask(){ Map<String,Object> variables =new HashMap<>(); variables.put("day",3); taskService.complete("fae74a9a-1d6c-11ef-9a09-005056c00008"); }
注意:條件盡量包含所有情況,否則報錯。
并行網(wǎng)關
提交申請之后,task表中將會出現(xiàn)兩條審批,分別是zhangsan和lisi。
包含網(wǎng)關
可以看成并行和排他的結(jié)合體
如num=2走三條,num=5走兩條,num=8走三條
Tips
每次重新部署一個id相同的bpmn時,正在執(zhí)行的流程會被自動刪除。
流程定義與流程部署是不一樣的,一個流程部署可以對應多個流程定義。流程部署的表是act_re_deployment,流程定義的表是act_re_procdef??梢岳斫鉃閍ct_re_procdef是act_re_deployment的從表。
流程定義不需要刪除,當你不要這個流程定義時,就把流程部署給刪除好了。
刪除流程部署,默認級聯(lián)刪除,正在執(zhí)行的流程實例也會被刪除。
與Activiti7的區(qū)別
1.Activiti默認不開啟數(shù)據(jù)庫的歷史記錄,flowable默認開啟
2.Activiti23張表,flowable79張表。
3.Flowable是Activiti的繼任者,因此Flowable包含了Activiti的所有功能,并且在原有功能的基礎上進行了進一步的改進和優(yōu)化。
4.支持 CMMN 和 DMN 標準
三、SpringBoot項目中引入flowable
1.配置flowable獨立數(shù)據(jù)源
flowable: async-executor-activate: false #第一次生成后關閉 database-schema-update: true #保存歷史數(shù)據(jù)級別 history-level: full #解決亂碼 activity-font-name: "宋體" annotation-font-name: "宋體" label-font-name: "宋體" #配置flowable數(shù)據(jù)源 flow: username: root password: root url: jdbc:mysql://localhost:3306/flowable2?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true driver-class-name: com.mysql.cj.jdbc.Driver maxPoolSize: 30
2.創(chuàng)建配置類
import com.alibaba.druid.pool.DruidDataSource; import lombok.extern.slf4j.Slf4j; import org.flowable.app.spring.SpringAppEngineConfiguration; import org.flowable.spring.boot.EngineConfigurationConfigurer; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; ? import javax.sql.DataSource; import java.beans.PropertyVetoException; ? /** * 一共有兩個配置類,一個是ProcessEngineConfiguration,一個是SpringAppEngineConfiguration * 他們里面都需要重寫configure方法來進行配置 * 配置數(shù)據(jù)源應該在SpringAppEngineConfiguration中設定 * 前者是配置ProcessEngine的,如自動生成表,設置中文,在yml文件中配置的屬性便是在此類中讀取 */ @Configuration @PropertySource("classpath:application-dev.yml") @Slf4j ? public class FlowableConfig implements EngineConfigurationConfigurer<SpringAppEngineConfiguration> { ? //讀取配置 @Value("${flow.username}") private String user; @Value("${flow.password}") String password; @Value("${flow.url}") String jdbcUrl; @Value("${flow.driver-class-name}") String driverClass; @Value("${flow.maxPoolSize}") int maxPoolSize; ? // @Bean(name = "processEngine") // public ProcessEngine processEngineConfiguration() throws PropertyVetoException { // ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration(); // cfg.setDataSource(dataSource2()); // cfg.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_FALSE); // cfg.setActivityFontName("宋體"); // cfg.setLabelFontName("宋體"); // cfg.setAnnotationFontName("宋體"); // cfg.setAsyncExecutorActivate(false); // return cfg.buildProcessEngine(); // } ? //配置數(shù)據(jù)源 public DataSource dataSource2() throws PropertyVetoException { DruidDataSource dataSource = new DruidDataSource(); dataSource.setUsername(user); dataSource.setPassword(password); dataSource.setUrl(jdbcUrl); dataSource.setDriverClassName(driverClass); dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolSize); return dataSource; } ? @Override public void configure(SpringAppEngineConfiguration engineConfiguration) { try { //把數(shù)據(jù)源設置進來 engineConfiguration.setDataSource(dataSource2()); log.info("配置flowable數(shù)據(jù)源成功"); } catch (PropertyVetoException e) { throw new RuntimeException(e); } } }
注意這里配置的是SpringAppEngineConfiguration,而不是ProcessEngine,否則將出現(xiàn)報錯,或者設置單獨的數(shù)據(jù)源失敗。
項目啟動成功就可以看到自動創(chuàng)建的79張表了。然后把表的自動更新關閉,否則會影響性能。
到此這篇關于Flowable整合SpringBoot實現(xiàn)的示例代碼的文章就介紹到這了,更多相關Flowable整合SpringBoot內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java中字符串與日期轉(zhuǎn)換常見方法總結(jié)
這篇文章主要給大家介紹了關于Java中字符串與日期轉(zhuǎn)換常見方法的相關資料,在Java編程中經(jīng)常需要將字符串表示的日期轉(zhuǎn)換為日期對象進行處理,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2023-11-11SpringBoot中的異常處理與參數(shù)校驗的方法實現(xiàn)
這篇文章主要介紹了SpringBoot中的異常處理與參數(shù)校驗的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04如何通過Java監(jiān)聽MySQL數(shù)據(jù)的變化
對于二次開發(fā)來說,很大一部分就找找文件和找數(shù)據(jù)庫的變化情況,下面這篇文章主要給大家介紹了關于如何通過Java監(jiān)聽MySQL數(shù)據(jù)的變化的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2023-03-03spring?boot?導出數(shù)據(jù)到excel的操作步驟(demo)
這篇文章主要介紹了spring?boot?導出數(shù)據(jù)到excel的實現(xiàn)步驟,文中通過打開一個平時練習使用的springboot的demo給大家詳細介紹,需要的朋友可以參考下2022-03-03Java web spring異步方法實現(xiàn)步驟解析
這篇文章主要介紹了Java web spring異步方法實現(xiàn)步驟解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08