SpringBoot 2.1.7.RELEASE + Activiti 5.18.0 喂饭级练习手册

环境准备

win10

eclipse 2023-03

eclipse Activiti插件

Mysql 5.x

Activiti的作用等不再赘叙,直接上代码和细节

POM

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.7.RELEASE</version><relativePath /> <!-- lookup parent from repository -->
</parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter-basic</artifactId><version>5.18.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId></dependency>
</dependencies>

启动类

import org.activiti.spring.boot.SecurityAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class App {public static void main(String[] args) {SpringApplication.run(App.class, args);}
}

配置文件 application.yml

首次启动时,会自动创建activiti的表

spring:datasource:driveClassName: com.mysql.cj.jdbc.Driver#&nullCatalogMeansCurrent=trueurl: jdbc:mysql://127.0.0.1:3306/act?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTCusername: rootpassword: roothikari:mininum-idle: 5idle-timeout: 30000connection-timeout: 30000maxinum-pool-size: 10max-lifetime: 60000connect-test-query: select 1

BPMN

囊括了 系统任务、用户任务、执行监听器、任务监听器、排他网关,常用的都有了

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test"><process id="testP" name="测试一把" isExecutable="true"><startEvent id="startevent1" name="Start"><extensionElements><activiti:executionListener event="start" delegateExpression="${startListener}"></activiti:executionListener></extensionElements></startEvent><endEvent id="endevent1" name="End"><extensionElements><activiti:executionListener event="end" delegateExpression="${endListener}"></activiti:executionListener></extensionElements></endEvent><serviceTask id="servicetask1" name="Service Task1" activiti:delegateExpression="${myServiceTask1}"></serviceTask><serviceTask id="servicetask2" name="Service Task2" activiti:delegateExpression="${myServiceTask2}"></serviceTask><sequenceFlow id="flow1" sourceRef="startevent1" targetRef="servicetask1"></sequenceFlow><sequenceFlow id="flow2" sourceRef="servicetask1" targetRef="servicetask2"></sequenceFlow><userTask id="usertask1" name="User Task"><extensionElements><activiti:taskListener event="create" delegateExpression="${userTaskListener}"></activiti:taskListener></extensionElements></userTask><sequenceFlow id="flow3" sourceRef="servicetask2" targetRef="usertask1"></sequenceFlow><exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway><serviceTask id="servicetask3" name="Service Task3" activiti:delegateExpression="${myServiceTask3}"></serviceTask><serviceTask id="servicetask4" name="Service Task4" activiti:delegateExpression="${myServiceTask4}"></serviceTask><sequenceFlow id="flow4" sourceRef="usertask1" targetRef="exclusivegateway1"></sequenceFlow><sequenceFlow id="flow5" sourceRef="exclusivegateway1" targetRef="servicetask3"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${age > 18}]]></conditionExpression></sequenceFlow><sequenceFlow id="flow6" sourceRef="exclusivegateway1" targetRef="servicetask4"><conditionExpression xsi:type="tFormalExpression"><![CDATA[${age <= 18}]]></conditionExpression></sequenceFlow><sequenceFlow id="flow7" sourceRef="servicetask3" targetRef="endevent1"></sequenceFlow><sequenceFlow id="flow8" sourceRef="servicetask4" targetRef="endevent1"></sequenceFlow></process><bpmndi:... 这些不展示了/>
</definitions>

部署流程

import org.activiti.engine.RepositoryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** 部署流程* * @author wusc* @since 2023年8月8日16:16:40*/
@Component
public class ProcessDeployConfig {private final Logger logger = LoggerFactory.getLogger(ProcessDeployConfig.class);@Autowiredprivate RepositoryService repositoryService;@PostConstructpublic void deploy() {logger.info("部署流程");repositoryService.createDeployment().name("我今天来测试一把").addClasspathResource("testP.bpmn")// 我这个就放在src/main/resources目录下.deploy();}
}

启动流程

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class ActivitiController {private final Logger logger = LoggerFactory.getLogger(ActivitiController.class);@Autowiredprivate RuntimeService runtimeService;// 直接引用@RequestMapping("/act/start")public String start(@RequestParam(value = "name") String name, @RequestParam(value = "phone") String phone) {// 全局变量,可以放订单信息、用户信息、流程状态、流程步骤等Map<String, Object> paramMap = new HashMap<>();paramMap.put("name", name);paramMap.put("phone", phone);// 任务唯一KEY,这里一般用orderId/orderNo 等唯一键来代替,后面处理任务时有用到String uuid = UUID.randomUUID().toString();logger.info("uuid:{}", uuid);// 流程KEY,BPMN XML的IDString processKey = "testP";runtimeService.startProcessInstanceByKey(processKey, uuid, paramMap);return "success";}
}

Start 节点

start/end 不设置监听器也可以 

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;/*** 执行监听器,流程启动时触发* * @author wusc* @since 2023年8月8日16:01:48*/
@Component("startListener")
public class StartListener implements ExecutionListener {private static final long serialVersionUID = 422573009417731884L;private final Logger logger = LoggerFactory.getLogger(StartListener.class);@Overridepublic void notify(DelegateExecution execution) throws Exception {logger.info("[{}]流程开始", execution.getEngineServices().getRepositoryService().getProcessDefinition(execution.getProcessDefinitionId()).getKey());}
}

 

 系统任务

系统任务是自动化处理的,需要指定哪个类来执行逻辑

Service Task1、Task2处理方式相同,execute()方法里面写自己的逻辑即可

import java.util.Map;import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;/*** 系统自动任务,需要实现JavaDelegate,execute()执行完毕,当前任务完成,自动进入下一任务* * myServiceTask1,实例名 = 上图Main config里面的变量* * @author wusc* @since 2023年8月8日16:28:55**/
@Component("myServiceTask1")
public class MyServiceTask1 implements JavaDelegate {private final Logger logger = LoggerFactory.getLogger(MyServiceTask1.class);@Overridepublic void execute(DelegateExecution execution) throws Exception {logger.info("activitiId:{}", execution.getCurrentActivityId());logger.info("activitiName:{}", execution.getCurrentActivityName());Map<String, Object> paramMap = execution.getVariables();logger.info("获取全局变量,name={}", paramMap.get("name"));logger.info("获取全局变量,phone={}", paramMap.get("phone"));// 一套增删改查}
}

用户任务 

与系统任务不同,用户任务不指定哪个类来执行逻辑;

1. 设置监听器,给任务绑定用户;

2. 通过controller接收http请求触发,经唯一键和用户标识查到当前任务,手动Complete;

 

import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;/*** 用户任务就是具体某个人的任务,所以需要先分配给具体用户,然后该用户来处理* * @author wusc* @since 2023年8月8日16:13:58**/
@Component("userTaskListener")
public class UserTaskListener implements TaskListener {private static final long serialVersionUID = -3300154910592367754L;private final Logger logger = LoggerFactory.getLogger(UserTaskListener.class);@Overridepublic void notify(DelegateTask delegateTask) {logger.info("分配用户任务到具体用户身上");delegateTask.setOwner("Pony");// 这个任务属于Pony(这里可以用 用户ID来填充)delegateTask.setAssignee("wusc");// 这个任务Pony没空处理,wusc可以帮他处理,也就是二人都可以处理}
}

处理用户任务,taskService.complete


import java.util.HashMap;
import java.util.Map;
import java.util.UUID;import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class ActivitiController {private final Logger logger = LoggerFactory.getLogger(ActivitiController.class);@Autowiredprivate TaskService taskService;@RequestMapping("/complete")public String complete(@RequestParam(value = "uuid") String uuid, @RequestParam(value = "age") int age) {logger.info("age={}", age);Task task = taskService.createTaskQuery().processInstanceBusinessKey(uuid)// 通过唯一键定位.taskAssignee("wusc")// 查自己的任务.list().get(0);logger.info("完成任务,taskId={}", task.getId());Map<String, Object> userMap = new HashMap<>();userMap.put("age", age);taskService.complete(task.getId(), userMap);return "success";}}

 排他网关

排他网关通过age变量确定走哪一条路

 

 系统任务

同Service Task1一样,在execute()方法内些自己的逻辑即可,方法执行完自动到下一个节点

import java.util.Map;import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;@Component("myServiceTask3")
public class MyServiceTask3 implements JavaDelegate {private final Logger logger = LoggerFactory.getLogger(MyServiceTask3.class);@Overridepublic void execute(DelegateExecution execution) throws Exception {logger.info("activitiId:{}", execution.getCurrentActivityId());logger.info("activitiName:{}", execution.getCurrentActivityName());Map<String, Object> paramMap = execution.getVariables();logger.info("获取全局变量,age={}", paramMap.get("age"));}}

 End 节点

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;@Component("endListener")
public class EndListener implements ExecutionListener {private static final long serialVersionUID = 422573009417731884L;private final Logger logger = LoggerFactory.getLogger(EndListener.class);@Overridepublic void notify(DelegateExecution execution) throws Exception {logger.info("[{}]流程结束", execution.getEngineServices().getRepositoryService().getProcessDefinition(execution.getProcessDefinitionId()).getKey());}
}

执行日志

通过日志可以看到,启动流程后,Service Task1、Service Task2自动执行了,执行到User Task时,触发了任务监听器,给用户任务分配了具体用户

业务KEY=78bc0087-79a9-4205-bdc5-7144d936ffa9,调用完成任务接口

因为age=18,经过排他网关判断,进入Service Task4节点,最后流程结束

 

 思考题

看日志,调用完成任务接口,Service Task4的线程ID与接口处理业务时的线程ID是相同的,这是为啥?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/58661.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#之控制台版本得贪吃蛇

贪吃蛇小时候大家都玩过&#xff0c;具体步骤如下: 1.给游戏制造一个有限得空间。 2.生成墙壁&#xff0c;小蛇碰撞到墙壁或者咬到自己的尾巴&#xff0c;游戏结束。 3.生成随机的食物。 4.吃掉食物&#xff0c;增加自身的体长&#xff0c;并生成新的食物。 具体代码如下&…

数据结构 二叉树(一篇基本掌握)

绪论 雄关漫道真如铁&#xff0c;而今迈步从头越。 本章将开始学习二叉树&#xff08;全文共一万两千字&#xff09;&#xff0c;二叉树相较于前面的数据结构来说难度会有许多的攀升&#xff0c;但只要跟着本篇博客深入的学习也可以基本的掌握基础二叉树。 话不多说安全带系好&…

SQL力扣练习(十)

目录 1.体育馆的人流量(501) 示例 1 解法一&#xff08;row_number&#xff08;&#xff09;&#xff09; 解法二&#xff08;自定义变量&#xff09; 解法三 2.好友申请&#xff08;602&#xff09; 示例 解法一&#xff08;union all&#xff09; 解法二 3.销售员&…

re学习(27)攻防世界 re1-100

参考文章&#xff1a;攻防世界逆向高手题之re1-100_沐一 林的博客-CSDN博客 查壳&#xff1a; 用IDA打开&#xff0c;分析 编写脚本&#xff1a; d"{daf29f59034938ae4efd53fc275d81053ed5be8c}" d1d[1:11] d2d[11:21] d3d[21:31] d4d[31:41] print(d3d4d1d2) #…

golang内存对齐

为什么要内存对齐&#xff1f; CPU访问内存时&#xff0c;以CPU的位数为单位进行访问。 如果访问未对齐的内存&#xff0c;处理器需要做两次内存访问&#xff0c;对齐的内存的访问可能仅需要一次&#xff0c;利用内存对齐后提升读取速度。 golang结构体内存对齐规则 在代码编译…

基于身份的安全威胁正在迅速增长

根据端点安全和威胁情报供应商 CrowdStrike 发布的一份报告&#xff0c;目前最危险的网络安全威胁是能够访问给定系统合法身份信息的攻击者。 根据该报告&#xff0c;交互式入侵&#xff08;该公司将其定义为攻击者积极工作以在受害者系统上实现某种非法目的的入侵&#xff09;…

【Linux】深入理解进程概念

个人主页&#xff1a;&#x1f35d;在肯德基吃麻辣烫 我的gitee&#xff1a;Linux仓库 个人专栏&#xff1a;Linux专栏 分享一句喜欢的话&#xff1a;热烈的火焰&#xff0c;冰封在最沉默的火山深处 文章目录 前言浅谈进程概念1. 进程和操作系统的联系2.描述进程的对象——PCB …

SQL ASNI where from group order 顺序 where和having,SQL底层执行原理

SQL语句执行顺序&#xff1a; from–>where–>group by -->having — >select --> order 第一步&#xff1a;from语句&#xff0c;选择要操作的表。 第二步&#xff1a;where语句&#xff0c;在from后的表中设置筛选条件&#xff0c;筛选出符合条件的记录。 …

51单片机学习-AT24C02数据存储秒表(定时器扫描按键数码管)

首先编写I2C模块&#xff0c;根据下面的原理图进行位声明&#xff1a; sbit I2C_SCL P2^1; sbit I2C_SDA P2^0;再根据下面的时序结构图编写函数&#xff1a; /*** brief I2C开始* param 无* retval 无*/ void I2C_Start(void) {I2C_SDA 1; I2C_SCL 1; I2C_SDA 0;I2C_S…

安防监控视频汇聚平台EasyCVR分发的FLV视频流在VLC中无法播放是什么原因?

众所周知&#xff0c;TSINGSEE青犀视频汇聚平台EasyCVR可支持多协议方式接入&#xff0c;包括主流标准协议国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。在视频流的处理与分发上&#xff0c;视频监控…

安全防御(3)

1.总结当堂NAT与双机热备原理&#xff0c;形成思维导图 2.完成课堂nat与双机热备试验 引用IDS是指入侵检测系统&#xff0c;它可以在网络中检测和防御入侵行为。IDS的签名是指根据已知入侵行为的特征制定的规则&#xff0c;用于检测和警告可能存在的入侵行为。签名过滤器可以根…

人工智能编程软件 python,python人工智能编程入门

大家好&#xff0c;给大家分享一下人工智能python编程具体做什么&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; 有不少同学学习 Python 的原因是对人工智能感兴趣&#xff0c;有志于从事相关行业。今天我们来聊聊这个方向所需要的一些技能…