Flowable整合SpringBoot实现的示例代码
作者:C和弦与炊烟
一、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 #数据库表与flowable最新表不一致会进行更新 database-schema-update: true logging: level: org: flowable: debug
二、Spring环境下的应用
1.流程部署与启动
@Autowired ProcessEngine processEngine; @Autowired RepositoryService repositoryService; @Autowired RuntimeService runtimeService; @Autowired TaskService taskService; @Test void deployFlow(){ //流程引擎的配置对象,关联相关数据源 Deployment deploy = repositoryService.createDeployment() //一次部署所有processes文件夹内的流程 .name("第一次部署") .deploy(); System.out.println("deploy.getId()="+deploy.getId()); } /** * 启动流程实例 * 在流程定义表中动态维护 act_re_procdef */ @Test void startFlow(){ String processId="ask_for_leave.bpmn20:1:4"; String processKey="ask_for_leave.bpmn20"; //1.根据流程定义di启动流程实例 runtimeService.startProcessInstanceById(processId); //2.根据流程定义key启动流程实例 //runtimeService.startProcessInstanceByKey(processKey); } /** * 任务的审批 * 需要数据:任务id */ @Test void compeleteTask(){ taskService.complete("2506"); }
每启动一个流程,会在act_hi_procinst中维护一条数据。
启动一个流程,可以在act_ru_task表中看到对应的记录,该表记录的都是当前待办的记录信息。
act_ru_execution记录流程的分支
流程定义:相当于Java中的类
流程实例:相当于java中的对象
注意:在Spring环境下,Spring会自动扫描processes文件夹,若不指定文件路径,则一次把所有bpmn流程全部部署,也就是一次创建所有流程定义。一个部署对应多个流程定义
@Test void deployFlow(){ Deployment deploy = repositoryService.createDeployment() //如果再添加部署文件,会部署两次,导致流程定义中出现新版本的流程定义 .addClasspathResource("processes/ask_for_leave.bpmn20.xml") .name("部署名称") .deploy(); System.out.println("deploy.getId()="+deploy.getId()); }
常用:1.通过bpmn部署。2.通过zip压缩包部署。3.自己创建model模型,然后保存到数据库中,再通过模型部署。
创建好model之后保存到act_re_model表中(上图仅仅是举个3的例子)。部署也是先从表中获取model部署。
2.表结构
act_re: repository,包含流程定义和流程静态资源(图片,规则等)
act_ru: runtime,运行时的表,包含实例,任务,变量,异步任务,运行中的数据等
act_hi:history,流程的历史数据,如历史流程实例,变量,任务。
- act_ge:general,通用数据。
act_ge_bytearray(重要),存放了流程定义的png图片。
act_id:identity组织机构。包含标识的信息,如用户,用户组等。
CMMN 表示这都是跟 CMMN 协议相关的表。
CO(CONTENT)表示这都是跟内容引擎相关的表。
DMN 表示这都是跟 DMN 协议相关的表。
FO(FORM)表示这都是跟表单相关的表。
3.流程挂起和激活
/** * 流程定义的挂起和激活 *act_re_procdef */ @Test void suspendedActivity(){ String processDefinitionId="ask_for_leave.bpmn20:1:4"; ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery() .processDefinitionId(processDefinitionId) .singleResult(); //获取当前流程定义的状态 boolean suspended = processDefinition.isSuspended(); if (suspended) { //挂起-->激活 System.out.println("激活流程"); repositoryService.activateProcessDefinitionById(processDefinitionId); }else { //激活-->挂起 System.out.println("挂起流程"); repositoryService.suspendProcessDefinitionById(processDefinitionId); } }
已经挂起的流程定义,不允许启动。
已经启动的流程,不受影响。
可以挂起已经启动的流程实例,该流程实例不允许审批。
4.任务分配表达式Assignee
Assignee也可以写表达式
值表达式
${ assign1 },assign1是自己定义的变量。
方法表达式
${ myBean.getAssignee() }
/** * 任务的审批 * 需要数据:任务id,hashMap "assign1","lisi" * 把当前任务分给lisi审批,运行后lisi可以查到待办 */ @Test void compeleteAssign1(){ HashMap<String, Object> variables = new HashMap<>(); variables.put("assign1","lisi"); //完成任务审批,根据任务id绑定对应表达式的值 taskService.complete("taskId",variables); }
(act_ru_actinst)出现了lisi
@Test void findFlow(){ //任务实例通过TaskService来实现 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] }
得到结果Task[id=15004, name=second],此时还需要审批。
------ 方法表达式${ myBean.getAssignee() } -------
//MyBean加入容器 @Component public class MyBean { public String getAssignee(){ System.out.println("getAssignee执行..."); return "王五"; } }
@Test void compeleteAssign1(){ TaskService taskService = getEngine().getTaskService(); //再执行审批时,会去MyBean中执行该方法 taskService.complete("15004"); }
拿到id后再执行审批。
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相关,节点没了变量就没了 //runtimeService.setVariablesLocal(execution,"var4","test4"); Map<String, Object> variables = runtimeService.getVariables(execution); System.out.println(variables); }
输出{var3=test3, var2=test2, var1=test1}
6.候选人
可以指定多个候选人,在启动流程时进行赋值就好。
/** * 根据候选人查询任务 * 候选人需要拾取任务才能变成审批人 *只有一个人能变为审批人,审批人还可以归还,变成候选人 */ @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.候选人组
//先创建用户 @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中就会出现候选组信息。
/** * 当前登录用户根据候选人组查询任务 */ @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.网关
排他网关
并行网关
包容网关
事件网关
排他网关
在审批的时候加入day天数就能完成请假步骤,从而转向不同的审批人。
@Test void compeleteTask(){ Map<String,Object> variables =new HashMap<>(); variables.put("day",3); taskService.complete("fae74a9a-1d6c-11ef-9a09-005056c00008"); }
注意:条件尽量包含所有情况,否则报错。
并行网关
提交申请之后,task表中将会出现两条审批,分别是zhangsan和lisi。
包含网关
可以看成并行和排他的结合体
如num=2走三条,num=5走两条,num=8走三条
Tips
每次重新部署一个id相同的bpmn时,正在执行的流程会被自动删除。
流程定义与流程部署是不一样的,一个流程部署可以对应多个流程定义。流程部署的表是act_re_deployment,流程定义的表是act_re_procdef。可以理解为act_re_procdef是act_re_deployment的从表。
流程定义不需要删除,当你不要这个流程定义时,就把流程部署给删除好了。
删除流程部署,默认级联删除,正在执行的流程实例也会被删除。
与Activiti7的区别
1.Activiti默认不开启数据库的历史记录,flowable默认开启
2.Activiti23张表,flowable79张表。
3.Flowable是Activiti的继任者,因此Flowable包含了Activiti的所有功能,并且在原有功能的基础上进行了进一步的改进和优化。
4.支持 CMMN 和 DMN 标准
三、SpringBoot项目中引入flowable
1.配置flowable独立数据源
flowable: async-executor-activate: false #第一次生成后关闭 database-schema-update: true #保存历史数据级别 history-level: full #解决乱码 activity-font-name: "宋体" annotation-font-name: "宋体" label-font-name: "宋体" #配置flowable数据源 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.创建配置类
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方法来进行配置 * 配置数据源应该在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(); // } //配置数据源 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 { //把数据源设置进来 engineConfiguration.setDataSource(dataSource2()); log.info("配置flowable数据源成功"); } catch (PropertyVetoException e) { throw new RuntimeException(e); } } }
注意这里配置的是SpringAppEngineConfiguration,而不是ProcessEngine,否则将出现报错,或者设置单独的数据源失败。
项目启动成功就可以看到自动创建的79张表了。然后把表的自动更新关闭,否则会影响性能。
到此这篇关于Flowable整合SpringBoot实现的示例代码的文章就介绍到这了,更多相关Flowable整合SpringBoot内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!