1. 版本问题

1.1. Activiti版本

7.1.0-M6是最后一个支持JDK1.8的版本,此后的版本都要求JDK11以上

目前,Activiti最新版本是7.6.0,它是用JDK11编译的,因此要想使用最新版7.6.0必须升级JDK版本,不能再用1.8

同时,7.6.0依赖的SpringBoot版本是2.7.5

1.2. SpringBoot版本

最新的SpringBoot版本是3.0.0,这个版本不支持JDK1.8,对JDK的最小版本是17

目前可用比较多的Java版本是Java 17 和 Java 19

综合来看,我们采用Java 17+SpringBoot 2.7.5+Activiti 7.6.1

补充:52 = Java 8  55 = Java 11

2. Maven仓库设置

首先要添加Activiti组件的仓库,不然找不到jar包,可用配置在全局的settings.xml文件中,也可以配置在项目pom.xml中

<repositories>
<repository>
<id>activiti-releases</id>
<url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases</url>
</repository>
</repositories>

如果settings.xml中有镜像,并且所有镜像都使用一个仓库的话(即,morrorOf配置的是*),要注意将新加的这个仓库排除

https://maven.apache.org/guides/mini/guide-mirror-settings.html

举个例子:

3. 依赖管理

Activiti依赖管理

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-dependencies</artifactId>
<version>7.6.1</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>

Dubbo依赖管理

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>3.1.3</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>

完整的父POM如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<!--<version>3.0.0</version>-->
<version>2.7.5</version>
<relativePath/>
</parent>
<groupId>com.cjs.example</groupId>
<artifactId>activiti7-sample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging> <name>activiti7-sample</name> <modules>
<module>activiti7-sample-provider-api</module>
<module>activiti7-sample-provider</module>
</modules> <properties>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies> <dependencyManagement>
<dependencies>
<!--
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M6</version>
</dependency>
-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-dependencies</artifactId>
<version>7.6.1</version>
<type>pom</type>
<scope>import</scope>
</dependency> <dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>3.1.3</version>
<type>pom</type>
<scope>import</scope>
</dependency> <dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
</dependencies>
</dependencyManagement> <repositories>
<repository>
<id>activiti-releases</id>
<url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases</url>
</repository>
</repositories>
</project>

4. Activiti  API使用

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.cjs.example</groupId>
<artifactId>activiti7-sample</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.cjs.example</groupId>
<artifactId>activiti7-sample-provider</artifactId>
<version>${parent.version}</version> <name>activiti7-sample-provider</name> <properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.cjs.example</groupId>
<artifactId>activiti7-sample-provider-api</artifactId>
<version>${project.version}</version>
</dependency> <dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency> <dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency> <dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build> </project>

application.yml

server:
port: 8080
servlet:
context-path: /activiti7
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/activiti7?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&nullCatalogMeansCurrent=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
activiti:
check-process-definitions: false
database-schema-update: "false" # 第一次运行时设置为true,待表生成完以后改成false,以后就不用再更新表结构了
db-history-used: true
history-level: full
deployment-mode: "never-fail" # org.activiti.spring.autodeployment.AbstractAutoDeploymentStrategy
dubbo:
application:
name: activiti7-sample-provider
protocol:
name: dubbo
port: -1
registry:
address: nacos://127.0.0.1:8848
config-center:
address: nacos://127.0.0.1:8848
metadata-report:
address: nacos://127.0.0.1:8848

启动项目后,生成的表结构如图:

也可以自己执行SQL脚本,脚本在源码包activiti-engine-7.6.1.jar里面

由于Activit7集成了SpringSecurity,它用SpringSecurity来做权限管理,因此它需要一个UserDetailsService,不配做的话启动会报错。当然网上也有一种解决办法就是排除SpringSecurity相关的某些类,没试过,应该也是可以的吧。

按照Spring Security 5.7以后的新写法,我们来配置一下WebSecurity

https://docs.spring.io/spring-security/reference/servlet/configuration/java.html

https://github.com/spring-projects/spring-security-samples/tree/6.0.x/servlet/spring-boot/java/oauth2

package com.cjs.example.provider.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain; /**
* https://docs.spring.io/spring-security/reference/servlet/configuration/java.html
* https://github.com/spring-projects/spring-security-samples/tree/6.0.x/servlet/spring-boot/java/oauth2
*
* @Author: ChengJianSheng
* @Date: 2022/12/5
*/
@Configuration
@EnableWebSecurity
public class MyWebSecurityConfiguration { @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeRequests(authorize -> authorize.anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
return http.build();
} @Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring()
// Spring Security should completely ignore URLs starting with /resources/
.antMatchers("/resources/**");
} @Bean
public UserDetailsService userDetailsService() {
UserDetails admin = User.withUsername("tom")
.password("$2a$10$tk29HhXGXCZDOSvn.VZlFeBsLmtJrKE2Uv6zLrRpTyvZMu3ipQLgC")
.roles("ACTIVITI_USER", "ACTIVITI_ADMIN", "APPLICATION_MANAGER")
.build();
UserDetails user = User.withUsername("jerry")
.password("$2a$10$tk29HhXGXCZDOSvn.VZlFeBsLmtJrKE2Uv6zLrRpTyvZMu3ipQLgC")
.roles("ACTIVITI_USER", "GROUP_BUSINESS_MANAGER")
.build();
UserDetails zhangsan = User.withUsername("zhangsan")
.password("$2a$10$tk29HhXGXCZDOSvn.VZlFeBsLmtJrKE2Uv6zLrRpTyvZMu3ipQLgC")
.roles("ACTIVITI_USER")
.build();
UserDetails lisi = User.withUsername("lisi")
.password("$2a$10$tk29HhXGXCZDOSvn.VZlFeBsLmtJrKE2Uv6zLrRpTyvZMu3ipQLgC")
.roles("ACTIVITI_USER")
.build();
return new InMemoryUserDetailsManager(admin, user, zhangsan, lisi);
} @Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
} public static void main(String[] args) {
System.out.println(new BCryptPasswordEncoder().encode("123456"));
}
}

在activiti-spring-boot-starter中新提供了ProcessRuntime 和 TaskRuntime用于流程和任务处理的API,调用它们需要当前操作的用户具有ACTIVITI_USER权限

因此,在使用这两个类之前要保证当前登录用户有这个权限。为了模拟登录,我们来写个登录方法。

package com.cjs.example.provider.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component; import java.util.Collection; /**
* @Author: ChengJianSheng
* @Date: 2022/12/6
*/
@Component
public class SecurityUtil { @Autowired
private UserDetailsService userDetailsService; public void logInAs(String username) {
UserDetails user = userDetailsService.loadUserByUsername(username);
if (null == user) {
throw new IllegalStateException(String.format("用户【%s】不存在", username));
}
SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
} @Override
public Object getCredentials() {
return user.getPassword();
} @Override
public Object getDetails() {
return user;
} @Override
public Object getPrincipal() {
return user;
} @Override
public boolean isAuthenticated() {
return true;
} @Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException { } @Override
public String getName() {
return user.getUsername();
}
}));
org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
}
}

现在可以开始调用API操作流程了

package com.cjs.example.provider.service;

import com.cjs.example.provider.api.WorkflowService;
import com.cjs.example.provider.util.SecurityUtil;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
import org.activiti.api.process.model.payloads.GetProcessInstancesPayload;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.api.runtime.shared.query.Order;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.runtime.TaskRuntime;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired; import java.util.Map; /**
* @Author: ChengJianSheng
* @Date: 2022/12/6
*/
@DubboService
public class WorkflowServiceImpl implements WorkflowService { @Autowired
private ProcessRuntime processRuntime; @Autowired
private TaskRuntime taskRuntime; @Autowired
private RuntimeService runtimeService; @Autowired
private RepositoryService repositoryService; @Autowired
private SecurityUtil securityUtil; @Override
public void deploy() {
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("processes/leave.bpmn20.xml")
.name("请假")
.key("leave")
.category("AAA")
.tenantId("QingJia")
.deploy();
} @Override
public void start() {
securityUtil.logInAs("tom");
ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder.start()
.withProcessDefinitionKey("leave")
.withVariable("hello", "world")
.withVariable("apple", "orange")
.withBusinessKey("1")
.build());
} @Override
public void startProcessInstance(String processDefinitionKey, String businessKey, Map<String, Object> variables, String tenantId) {
runtimeService.startProcessInstanceByKeyAndTenantId(processDefinitionKey, businessKey, variables, tenantId);
} @Override
public void processInstancePage(int pageNo, int pageSize) {
securityUtil.logInAs("tom");
GetProcessInstancesPayload payload = new GetProcessInstancesPayload();
payload.setActiveOnly(true);
Page<ProcessInstance> page = processRuntime.processInstances(Pageable.of(pageNo-1, pageSize, Order.by("start_time_", Order.Direction.DESC)), payload);
for (ProcessInstance ps : page.getContent()) {
System.out.println(ps);
}
} @Override
public void taskList() {
securityUtil.logInAs("zhangsan");
Page<Task> taskPage = taskRuntime.tasks(Pageable.of(0, 10));
System.out.println(taskPage.getTotalItems()); securityUtil.logInAs("lisi");
taskPage = taskRuntime.tasks(Pageable.of(0, 10));
System.out.println(taskPage.getTotalItems());
} @Override
public void claimTask() {
securityUtil.logInAs("zhangsan");
String taskId = "429ad159-754c-11ed-aaf8-84a9386654d8";
Task task = taskRuntime.task(taskId);
taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(taskId).build());
} @Override
public void completeTask() {
securityUtil.logInAs("zhangsan");
String taskId = "429ad159-754c-11ed-aaf8-84a9386654d8";
taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(taskId).build());
}
}

注意:可以把tenantId看成是某个业务,businessKey当做业务ID,比如:放款流程001。或者建一张中间表用于关联流程ID和业务ID。

最后,配置流程监听器

package com.cjs.example.provider.config;

import lombok.extern.slf4j.Slf4j;
import org.activiti.api.process.runtime.events.ProcessCompletedEvent;
import org.activiti.api.process.runtime.events.listener.ProcessRuntimeEventListener;
import org.activiti.api.task.runtime.events.TaskAssignedEvent;
import org.activiti.api.task.runtime.events.TaskCompletedEvent;
import org.activiti.api.task.runtime.events.listener.TaskRuntimeEventListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /**
* @Author: ChengJianSheng
* @Date: 2022/12/6
*/
@Slf4j
@Configuration
public class ListenerConfig { @Bean
public ProcessRuntimeEventListener<ProcessCompletedEvent> processCompletedListener() {
return processCompleted -> log.info(">>> Process Completed: '"
+ processCompleted.getEntity().getName() +
"' We can send a notification to the initiator: " + processCompleted.getEntity().getInitiator());
} @Bean
public TaskRuntimeEventListener<TaskAssignedEvent> taskAssignedListener() {
return taskAssigned -> log.info(">>> Task Assigned: '"
+ taskAssigned.getEntity().getName() +
"' We can send a notification to the assginee: " + taskAssigned.getEntity().getAssignee());
} @Bean
public TaskRuntimeEventListener<TaskCompletedEvent> taskCompletedListener() {
return taskCompleted -> log.info(">>> Task Completed: '"
+ taskCompleted.getEntity().getName() +
"' We can send a notification to the owner: " + taskCompleted.getEntity().getOwner());
}
}

代码:https://gitee.com/chengjiansheng/activiti7-sample

Activiti7+SpringBoot的更多相关文章

  1. Activiti7整合SpringBoot(十二)

    1 SpringBoot 整合 Activiti7 的配置 为了能够实现 SpringBoot 与 Activiti7 整合开发,首先我们要引入相关的依赖支持.所以,我们在工程的 pom.xml 文件 ...

  2. Activiti7工作流+SpringBoot

    文章目录 一. Activiti相关概念 1. Activiti介绍 2. 核心类 2.1 ProcessEngine 2.2 服务(Service)类 2.2.1 TaskService 2.2.2 ...

  3. SpringBoot系列——Activiti7工作流引擎

    前言 工作流程是我们日常开发项目中常见的功能,本文记录springboot整合activiti7. Activiti介绍 官网:https://www.activiti.org 数据库表 act_hi ...

  4. Spring-boot整合Activiti7

    Spring-boot整合Activiti7 pom.xml    <properties>        <maven.compiler.source>15</mave ...

  5. activiti7 导出bpmn文件

    最近在学习springboot+activiti7整合,想做一个导出bpmn文件的功能,查了相关资料,最后没有实现.最后查看了一下代码 找到了方法 如下所示 @GetMapping("exp ...

  6. springboot2整合activiti7具体步骤

    写在前面 需要提前了解的内容有 springboot.springSecurity.activiti基本使用 关于activiti Activiti项目是一项新的基于Apache许可的开源BPM平台, ...

  7. 解决 Springboot Unable to build Hibernate SessionFactory @Column命名不起作用

    问题: Springboot启动报错: Caused by: org.springframework.beans.factory.BeanCreationException: Error creati ...

  8. 【微框架】Maven +SpringBoot 集成 阿里大鱼 短信接口详解与Demo

    Maven+springboot+阿里大于短信验证服务 纠结点:Maven库没有sdk,需要解决 Maven打包找不到相关类,需要解决 ps:最近好久没有写点东西了,项目太紧,今天来一篇 一.本文简介 ...

  9. Springboot搭建web项目

    最近因为项目需要接触了springboot,然后被其快速零配置的特点惊呆了.关于springboot相关的介绍我就不赘述了,大家自行百度google. 一.pom配置 首先,建立一个maven项目,修 ...

  10. Java——搭建自己的RESTful API服务器(SpringBoot、Groovy)

    这又是一篇JavaWeb相关的博客,内容涉及: SpringBoot:微框架,提供快速构建服务的功能 SpringMVC:Struts的替代者 MyBatis:数据库操作库 Groovy:能与Java ...

随机推荐

  1. SV 设计特性

    过程语句块特性 ABC 过程块语句 always_comb 防止多驱动的问题:赋值块左侧的语句无法被另一个过程块赋值 if语句没有写else,sv会提示警告,sv认为是latch always不会再仿 ...

  2. js - 异步加载图片到 dom

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. GB18030-2022 标准学习

    GB18030-2022 标准学习 下载 https://openstd.samr.gov.cn/bzgk/gb/newGbInfo?hcno=A1931A578FE14957104988029B08 ...

  4. [转帖]RAC环境下误操作将数据文件添加到本地存储

    https://www.cnblogs.com/jyzhao/p/7986729.html 今天碰到个有意思的事情,有客户在Oracle RAC环境,误操作将新增的数据文件直接创建到了其中一个节点的本 ...

  5. [转帖]9.1 TiDB HTAP 的特点

    HTAP 是 Hybrid Transactional / Analytical Processing 的缩写.这个词汇在 2014 年由 Gartner 提出.传统意义上,数据库往往专为交易或者分析 ...

  6. [转帖]手摸手搭建简单的jmeter+influxdb+grafana性能监控平台

    我安装的机器是阿里云的centos8机器,其他的系统暂未验证 1.安装influxdb influxdb 下载地址https://portal.influxdata.com/downloads/,也可 ...

  7. MySQL数据库页存储结构学习与了解

    MySQL数据库页存储结构学习与了解 背景 MySQL总是出现奇奇怪怪的问题. 想着自己能够学习与提高一下. 最近看了很多文档.关于MySQL数据库相关的. 想着总结和提炼一下, 希望能够给未来的工作 ...

  8. Java进程 OOM的多种情况

    Java进程 OOM的多种情况 摘要 OOM 其实有多种: 第一类是JVM原生自发处理的, 这种也分为多种情况. 1. 堆区使用了比较多,并且大部分对象都还有引用, GC不出来可用内存, 这是要给对象 ...

  9. [转帖]kubelet 原理解析四:probeManager

    https://segmentfault.com/a/1190000022163835 概述 在Kubernetes 中,系统和应用程序的健康检查任务是由 kubelet 来完成的,本文主要讨论kub ...

  10. Ant Design Vue分页Pagination

    <template> <div> <a-pagination show-quick-jumper v-model:current="current1" ...