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

news2024/11/23 19:30:30

环境准备

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=true
    url: jdbc:mysql://127.0.0.1:3306/act?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
    username: root
    password: root
    hikari:
      mininum-idle: 5
      idle-timeout: 30000
      connection-timeout: 30000
      maxinum-pool-size: 10
      max-lifetime: 60000
      connect-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);

	@Autowired
	private RepositoryService repositoryService;

	@PostConstruct
	public 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);

	@Autowired
	private 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的ID
		String 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);

	@Override
	public 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);

	@Override
	public 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);

	@Override
	public 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);

	@Autowired
	private 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);
	
	@Override
	public 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);

	@Override
	public 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.coloradmin.cn/o/850381.html

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

相关文章

JVM基础篇-直接内存

JVM基础篇-直接内存 什么是直接内存? 直接内存( 堆外内存 ) 指的是 Java 应用程序通过直接方式从操作系统中申请的内存,这块内存不属于jvm 传统方式读取文件 首先会从用户态切换到内核态&#xff0c;调用操作系统函数从磁盘读取文件&#xff0c;读取一部分到操作系统缓冲区…

我的Python教程:使用Pyecharts画柱状图

Pyecharts是一个用于生成 Echarts 图表的 Python 库。Echarts 是一个基于 JavaScript 的数据可视化库&#xff0c;提供了丰富的图表类型和交互功能。通过 Pyecharts&#xff0c;你可以使用 Python 代码生成各种类型的 Echarts 图表&#xff0c;例如折线图、柱状图、饼图、散点图…

Linux学习之sed替换命令加强版

参考文章&#xff1a;《Shell 编程–Sed》 cat /etc/redhat-release看到操作系统是CentOS Linux release 7.6.1810&#xff0c;uname -r看到内核版本是3.10.0-957.el7.x86_64&#xff0c;bash --version可以看到bash版本是4.2.46(2)。 echo a : 1 : good :::: >> sedpl…

docker部署

docker部署 1.Docker2.mysql5.73.Redis4.ES&Kibana&IK分词器 1.Docker Docker 安装官方文档&#xff1a;https://docs.docker.com/install/linux/docker-ce/centos/ 1.卸载之前的docker sudo yum remove docker \ docker-client \ docker-client-latest \ docker-com…

springboot工程使用阿里云OSS传输文件

在application.yml文件中引入对应的配置&#xff0c;一个是对应的节点&#xff0c;两个是密钥和账号&#xff0c;还有一个是对应文件的名称&#xff1b; 采用这样方式进行解耦&#xff0c;便于后期修改。 然后需要设置一个properties类&#xff0c;去读对应的配置信息 用到了…

Vue2源码分析-day2

实现数组的响应式原理 首先我们在index.html中定义一个数组&#xff0c;并且打印实例 const vm new MVue({data() {return {name: "zhangsan",age: "16",hobby:[zhangsan,lisi]}} }) console.log(vm);我们会发现定义的数组每一项都有get和set方法虽然数…

14.3.4 【Linux】使用 LVM thin Volume 让 LVM 动态自动调整磁盘使用率

想像一个情况&#xff0c;你有个目录未来会使用到大约 5T 的容量&#xff0c;但是目前你的磁盘仅有 3T&#xff0c;问题是&#xff0c;接下来的两个月你的系统都还不会超过 3T 的容量&#xff0c; 不过你想要让用户知道&#xff0c;就是他最多有 5T 可以使用就是了&#xff01;…

并发多线程篇

线程的基础知识 面试题1&#xff1a;线程与进程的区别&#xff1f; 面试题2&#xff1a;并行和并发有什么区别&#xff1f; 面试题3&#xff1a;创建线程的方式有哪些&#xff1f; 面试题 4&#xff1a;runnable 和 callable 有什么区别&#xff1f; 面试题5&#xff1a;线程…

基于Centos7的Nginx源码安装

目录 1、准备安装环境 2、获取tar包&#xff1a; 3、解压创建软链接 4、创建用户和组 5、执行安装 6、创建服务脚本 7、开启nginx&#xff1a;​编辑​编辑 1、准备安装环境 yum insatall -y make gcc gcc-c pcre-devel #pcre-devel -- pcre库 #安装openssl-devel yum …

基于 CentOS 7 构建 LVS-DR 群集以及配置nginx负载均衡

目录 一、基于 CentOS 7 构建 LVS-DR 群集 1、前期准备 1、关闭防火墙 2、安装ifconfig 3、准备四台虚拟机 2、在DS上 2.1、配置LVS虚拟IP 2.2、手工执行配置添加LVS服务并增加两台RS 2.3、查看配置 3、在RS端&#xff08;第三台、第四台&#xff09; 上 3.1、配置W…

H7-TOOL的高速DAPLINK用于新版STM32CubeIDE V1.13及其以上版本的超简单实现方法(2023-08-08)

之前分享了一个方法&#xff0c;太繁琐了&#xff0c;H7-TOOL群的群友提供了一个方法&#xff0c;实现非常简单。1、使用STM32CubeMX或者自己创建一个STM32CubeIDE工程后&#xff0c;设置这两个地方即可&#xff1a; 配置调试器&#xff0c;设置完毕记得点击右下角的Apply 2、然…

【个人记录】CentOS7 编译安装最新版本Git

说明 使用yum install git安装的git版本是1.8&#xff0c;并不是最新版本&#xff0c;使用gitlab-runner托管时候会拉项目失败&#xff0c;这里使用编译源码方式安装最新版本的git。 基础环境安装 echo "nameserver 8.8.8.8" >> /etc/resolv.conf curl -o /…

Excel表格(一)

1.单一栏的宽度和高度设置 2.大标题的跨栏居中 3.让单元格内的文字------自动适应 4.序号递增 5.货币符号 6.日期格式的选择 选到单元格&#xff0c;选中对应的日期格式 7.自动求和的计算 然后在按住回车键即可求出当前行的金额 点击自动求和 8.冻结表格栏 9.排序 1.单栏排序 …

【性能类】—页面性能类

一、提升页面性能的方法有哪些&#xff1f; 1. 资源压缩合并&#xff0c;减少HTTP请求 图片、视频、js、css等资源压缩合并&#xff0c;开启HTTP压缩&#xff0c;把资源文件变小 2. 非核心代码异步加载 →异步加载的方式 → 异步加载的区别 异步加载的方式 ① 动态脚本加载…

【JavaWeb】 JavaScript 开发利器之 jQuery

&#x1f384;欢迎来到边境矢梦的csdn博文&#xff0c;本文主要讲解Java 中JavaScript 开发利器之 jQuery的相关知识&#x1f384; &#x1f308;我是边境矢梦&#xff0c;一个正在为秋招和算法竞赛做准备的学生&#x1f308; &#x1f386;喜欢的朋友可以关注一下&#x1faf0…

QT信号与槽的理解

文章目录 信号与槽的理解 信号与槽的理解 信号就是事件&#xff0c;比如button被点击的事件&#xff0c;ComboBox选项改变的事件&#xff0c;都是信号槽就是对信号进行响应的函数&#xff0c;可以是任意自定义函数一个信号可以对应多个槽多个信号可以对应一个槽信号的参数不能…

提升Element UI分页查询用户体验与交互:实现修改未保存提示

我实现的功能是在 element ui 的分页组件中进行分页查询时&#xff0c;如果当前有未保存的修改数据就提示用户&#xff0c;用户可以选择是否放弃未保存的数据。确认放弃就重新查询数据&#xff1b;选择不放弃&#xff0c;不重新查询&#xff0c;并且显示条数选择框保持原样&…

AWS中lambda与DynamoDB的集成

前言&#xff1a;我在整个集成过程中&#xff0c;存在最大的问题有两个&#xff0c; 1. 没有考虑到lambda函数的权限&#xff0c;即对DynamoDB或者其他如Kinesis的权限授权&#xff0c;导致无法写入或者读取。 2.最初使用了异步方式调用&#xff0c;导致无法写数据到DynamoDB…

STM32自带的DSP库的滤波初体验(一)

最近在弄STM32自带的DSP库里的滤波&#xff0c;记录一下&#xff1a; arm_fir_instance_q15 instance_q15_S; #define NUM_TAPS 16 //滤波系数的个数 #define BLOCK_SIZE 32 q15_t firStateF32[BLOCK_SIZE NUM_TAPS]; q15_t Fir_Coeff[NUM_TAPS] {-79, -136, 312, 6…

《合成孔径雷达成像算法与实现》Figure3.1

代码复现如下&#xff1a; clc close all clear all%参数设置 B 5.80e6; %信号带宽 T 7.26e-6; %脉冲持续时间 K B/T; %线性调频频率 alpha 5; %过采样率 F alpha*B; %采样频率 N F*T; %采样点数 dt T/N; …