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.mzph.cn/news/33767.shtml

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

相关文章

web前端之CSS操作

文章目录 一、CSS操作1.1 html元素的style属性1.2 元素节点的style属性1.3 cssText属性 二、事件2.1 事件处理程序2.1.1 html事件2.1.2 DOM0事件&#xff08;适合单个事件&#xff09;2.1.3 DOM2事件&#xff08;适合多个事件&#xff09; 2.2 事件之鼠标事件2.3 事件之Event事…

Python分享之 Spider

一、网络爬虫 网络爬虫又被称为网络蜘蛛&#xff0c;我们可以把互联网想象成一个蜘蛛网&#xff0c;每一个网站都是一个节点&#xff0c;我们可以使用一只蜘蛛去各个网页抓取我们想要的资源。举一个最简单的例子&#xff0c;你在百度和谷歌中输入‘Python&#xff0c;会有大量和…

选择最适合自己的笔记本

选择最适合自己的笔记本电脑 一、了解笔记本品牌一线品牌准一线品牌二线品牌三线品牌 二、笔记本入手渠道笔记本入手渠道 三、根据需求选择机型使用需求1.日常使用2.商务办公、财务3.轻度剪辑、ps4.代码5.创意设计6.游戏 四、笔记本电脑配置如何选1.cpu2.显卡&#xff08;GPU&a…

Vue响应式数据的原理

在 vue2 的响应式中&#xff0c;存在着添加属性、删除属性、以及通过下标修改数组&#xff0c;但页面不会自动更新的问题。而这些问题在 vue3 中都得以解决。 vue3 采用了 proxy 代理&#xff0c;用于拦截对象中任意属性的变化&#xff0c;包括&#xff1a;属性的读写、属性的…

UTONMOS:元宇宙在网络游戏领域得到充分运用

元宇宙到底是个啥&#xff1f;这个词大概意思应该就是人类能从真实世界进入一个虚拟世界体验另一种生活&#xff0c;这个虚拟的世界就叫“元宇宙”。 从科幻走入现实&#xff0c;元宇宙究竟有什么用途&#xff1f;它离我们到底还有多远&#xff1f;又将给我们的生活带来哪些变…

微服务——数据同步

问题分析 mysql和redis之间有数据同步问题&#xff0c;ES和mysql之间也有数据同步问题。 单体项目可以在crud时就直接去修改&#xff0c;但在微服务里面不同的服务不行。 方案一 方案二 方案三 总结 导入酒店管理项目 倒入完成功启动后可以看见数据成功获取到了 声明队列和…

idea中如何处理飘红提示

idea中如何处理飘红提示 在写sql时&#xff0c;总是会提示各种错误 查找资料&#xff0c;大部分都是说关提示&#xff0c;这里把错误提示选择为None即可 关掉以后&#xff0c;也确实不显示任何提示了&#xff0c;但总有一种掩耳盗铃的感觉 这个sms表明明存在&#xff0c;但是还…

探索极限:利用整数或字符串操作找出翻转后的最大数字

本篇博客会讲解力扣“1323. 6 和 9 组成的最大数字”的解题思路&#xff0c;这是题目链接。 对于这道题目&#xff0c;我会讲解2种解题思路&#xff0c;分别是直接操作整数&#xff0c;和利用字符串操作。希望大家通过本题学习关于整数和字符串的技巧。 显然&#xff0c;这道题…

2022深圳杯C题思路解析

题目描述&#xff1a; 继续更新 再更问题三 继续更新第一问、第四问 1.2 问题重述 在制定电动车调度方案时&#xff0c;必须考虑充、换电池的时间成本&#xff0c;从而提出了新 的车辆运输选址及调度问题。 1&#xff09; 已知自动驾驶电动物料车在取料点 P 和卸货点 D …

243. 一个简单的整数问题2(树状数组)

输入样例&#xff1a; 10 5 1 2 3 4 5 6 7 8 9 10 Q 4 4 Q 1 10 Q 2 4 C 3 6 3 Q 2 4输出样例&#xff1a; 4 55 9 15 解析&#xff1a; 一般树状数组都是单点修改、区间查询或者单点查询、区间修改。这道题都是区间操作。 1. 区间修改用数组数组维护差分数组 2. 区间查询&am…

golang 自定义exporter - 服务连接数 portConnCount_exporter 导出器

需求&#xff1a; 1、计算当前6379 、3306 服务的连接数 2、可prometheus 语法查询 下面代码可直接使用&#xff1a; 注&#xff1a; 1、windows 与linux的区分 第38行代码 localAddr : fields[1] //windows为fields[1] &#xff0c; linux为fields[3] 2、如需求 增加/修改/删除…

程序猿成长之路之密码学篇-分组密码加密模式及IV(偏移量)的详解

Cipher.getInstance("AES/ECB/PKCS5Padding"); Cipher cipher Cipher.getInstance("AES/CBC/PKCS5Padding"); 在进行加解密编程的时候应该有很多小伙伴接触过以上的语句&#xff0c;但是大伙儿在编码过程中是否了解过ECB/CBC的含义、区别以及PKCS5Padding…

Selenium之css怎么实现元素定位?

世界上最远的距离大概就是明明看到一个页面元素站在那里&#xff0c;但是我却定位不到&#xff01;&#xff01; Selenium定位元素的方法有很多种&#xff0c;像是通过id、name、class_name、tag_name、link_text等等&#xff0c;但是这些方法局限性太大&#xff0c; 随着自动…

指针进阶大冒险:解锁C语言中的奇妙世界!

目录 引言 第一阶段&#xff1a;&#x1f50d; 独特的字符指针 什么是字符指针&#xff1f; 字符指针的用途 演示&#xff1a;使用字符指针拷贝字符串 字符指针与字符串常量 小试牛刀 第二阶段&#xff1a;&#x1f3af; 玩转指针数组 指针数组是什么&#xff1f; 指针…

[SpringBoot3]基础篇

二、SpringBoot基础篇 2.1什么是SpringBoot SpringBoot是目前流行的微服务框架&#xff0c;倡导“约定优于配置”&#xff0c;其目的是用来简化新Spring应用的初始化搭建以及开发过程。SpringBoot提供了很多核心的功能&#xff0c;比如自动化配置starter&#xff08;启动器&a…

策略模式【Strategy Pattern】

刘备要到江东娶老婆了&#xff0c;走之前诸葛亮给赵云&#xff08;伴郎&#xff09;三个锦囊妙计&#xff0c;说是按天机拆开解决棘手问题&#xff0c; 嘿&#xff0c;还别说&#xff0c;真是解决了大问题&#xff0c;搞到最后是周瑜陪了夫人又折兵呀&#xff0c;那咱们先看看…

http、https笔记

目录 HTTP 基本概念状态码&#xff1a;get和post的区别&#xff1a;http 常⻅字段&#xff1a;http的缺点&#xff1a; HTTP/1.1HTTP/3HTTPSHTTPS和HTTP区别对称加密和⾮对称加密⾮对称加密 HTTP 基本概念 状态码&#xff1a; 1xx 中间状态&#xff0c;比如post的continue 20…

【设计模式】抽象工厂模式

抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 在抽象工厂模式中&#xff0c;接口是负责创建一个相关对象…

vim学习笔记(致敬vim作者)

vim cheat sheet 30. vim 删除大法 vim 删除某个字符之后改行的其他的字符&#xff1f;删除某行之后的其他行&#xff1f;删除某个字符之后的其他字符&#xff1f;【1】删除单个字符&#xff1f; 跳到要删除的字符位置 按下d键然后按下shift 4键 【2】删除某行之后的其他行…

【CheatSheet】Python、R、Julia数据科学编程极简入门

《Python、R、Julia数据科学编程极简入门》PDF版&#xff0c;是我和小伙伴一起整理的备忘清单&#xff0c;帮助大家10分钟快速入门数据科学编程。 另外&#xff0c;最近 TIOBE 公布了 2023 年 8 月的编程语言排行榜。 Julia 在本月榜单中实现历史性突破&#xff0c;成功跻身 …