SpringBoot 集成 Activiti 7 工作流引擎
一. 版本信息
- IntelliJ IDEA 2023.3.6
- JDK 17
- Activiti 7
二. IDEA依赖插件安装
安装BPM流程图插件,如果IDEA的版本超过2020,则不支持actiBPM插件。我的IDEA是2023版本我装的是 Activiti BPMN visualizer 插件。
- 在Plugins 搜索 Activiti BPMN visualizer 安装
- 创建BPMN文件
- 使用视图模式打开bpmn.xml
三. 创建SpringBoot 集成 activiti7
- 使用 IDEA 创建SpringBoot项目
- 设置项目参数
- 在 pom.xml 依赖配置文件中添加(Mysql,Lombok,activiti7)依赖
<?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.3.4</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com</groupId><artifactId>activiti-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>activiti-demo</name><description>activiti-demo</description><properties><java.version>17</java.version></properties><dependencies><!-- web依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- mysql依赖 --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.2.0</version></dependency><!-- lombok依赖 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- mybatis数据访问层 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.7</version></dependency><!-- activiti7 工作流引擎依赖 --><dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter</artifactId><version>7.1.0.M6</version></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>
- 创建本地连接的数据库
创建数据库 activiti
创建数据库表 userCREATE DATABASE `activiti` /*!40100 DEFAULT CHARACTER SET utf8 */;
添加一条测试数据-- activiti.`user` definitionCREATE TABLE `user` (`ID` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`NAME` varchar(100) DEFAULT NULL COMMENT '名称',`AGE` varchar(100) DEFAULT NULL COMMENT '年龄',`CREATED_BY` varchar(32) DEFAULT NULL COMMENT '创建人名称',`CREATED_TIME` datetime DEFAULT NULL COMMENT '创建时间',`CREATED_ID` varchar(32) DEFAULT NULL COMMENT '创建人ID',`UPDATED_BY` varchar(32) DEFAULT NULL COMMENT '更新人名称',`UPDATED_TIME` datetime DEFAULT NULL COMMENT '更新时间',`UPDATED_ID` varchar(32) DEFAULT NULL COMMENT '更新人ID',PRIMARY KEY (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';
INSERT INTO activiti.`user` (ID, NAME, AGE, CREATED_BY, CREATED_TIME, CREATED_ID, UPDATED_BY, UPDATED_TIME, UPDATED_ID) VALUES(1, '小王', '24', NULL, NULL, NULL, NULL, NULL, NULL);
- 添加 application.yml 配置文件
spring:application:name: activiti-demodatasource:#url切换数据库之后如果对应数据库名称和路径有变动,需要修改urlurl: jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf-8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver
- 添加 activiti.cfg.xml 配置文件(文件名不能随便改)
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans [<!ELEMENT beans (bean)*><!ATTLIST beansxmlns CDATA #REQUIREDxmlns:xsi CDATA #REQUIREDxsi:schemaLocation CDATA #REQUIRED><!ELEMENT bean (property)*><!ATTLIST beanid CDATA #REQUIREDclass CDATA #REQUIRED><!ELEMENT property (#PCDATA)><!ATTLIST propertyname CDATA #REQUIREDvalue CDATA #REQUIRED>]> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!-- processEnqine Activiti 的流程引擎 --><bean id="processEngineConfiguration"class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration"><property name="jdbcDriver" value="com.mysql.cj.jdbc.Driver"/><property name="jdbcUrl"value="jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false&nullCatalogMeansCurrent=true"/><property name="jdbcUsername" value="root"/><property name="jdbcPassword" value="root"/><!-- activiti 数据库表处理策略 --><!-- databaseSchemaUpdate 属性的值可以设置为以下几种 --><!-- none:这是默认值,表示不对数据库模式做任何变更,应用程序启动时不会检查数据库表结构是否与实体类匹配--><!-- true:设置为 true 时,Spring会在应用程序启动时检查数据库表结构,并在发现不匹配时自动创建或修改表结构以匹配实体类定义。这相当于执行了数据库迁移--><!-- create:与 true 类似,但 create 会在每次启动时删除并重新创建表,这可能会导致数据丢失,因此使用时需要谨慎--><!-- create-drop:在每次启动应用程序时创建表,在关闭应用程序时删除表。这通常用于测试环境--><!-- validate:在启动时验证数据库表结构是否与实体类定义匹配,如果不匹配则抛出异常,但不会自动进行任何更改--><property name="databaseSchemaUpdate" value="true"/></bean> </beans>
- 启动SpringBoot项目成功
- 开始添加一个查询数据测试接口(Controller,Service,Mapper,Entity)
Controller类
Service 类package com.activitidemo.act.controller;import com.activitidemo.act.entity.UserEntity; import com.activitidemo.act.service.impl.UserServiceImp; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;/*** <p>* 前端控制器* </p>** @author ningbeibei* @since 2024-09-26*/ @RestController @RequestMapping("/user-entity") public class UserController {@Resourceprivate UserServiceImp userService;@PostMapping("/user")public Object getUser(@RequestBody UserEntity user){QueryWrapper<UserEntity> queryWrapper = new QueryWrapper<>();queryWrapper.eq("id",user.getId());return userService.getBaseMapper().selectList(queryWrapper);}}
package com.activitidemo.act.service;import com.activitidemo.act.entity.UserEntity; import com.baomidou.mybatisplus.extension.service.IService;/*** @author ningbeibei* @since 2024-09-26*/ public interface UserService extends IService<UserEntity> {}
Mapper 类package com.activitidemo.act.service.impl;import com.activitidemo.act.mapper.UserMapper; import com.activitidemo.act.service.UserService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import com.activitidemo.act.entity.UserEntity;/*** <p>* 服务实现类* </p>** @author ningbeibei* @since 2024-09-26*/ @Service public class UserServiceImp extends ServiceImpl<UserMapper, UserEntity> implements UserService { }
package com.activitidemo.act.mapper;import com.activitidemo.act.entity.UserEntity; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper;/*** <p>* Mapper 接口* </p>** @author ningbeibei* @since 2024-09-26*/ @Mapper public interface UserMapper extends BaseMapper<UserEntity> {}
Entity 类<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.activitidemo.act.entity.UserEntity"></mapper>
目录结构package com.activitidemo.act.entity;import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Getter; import lombok.Setter;import java.io.Serializable; import java.time.LocalDateTime;/*** @author ningbeibei* @since 2024-09-26*/ @Getter @Setter @TableName("user") public class UserEntity implements Serializable {private static final long serialVersionUID = 1L;@TableField("ID")private Integer id;@TableField("NAME")private String name;@TableField("AGE")private int age;@TableField("CREATED_BY")private String createdBy;@TableField("CREATED_TIME")private LocalDateTime createdTime;@TableField("CREATED_ID")private String createdId;@TableField("UPDATED_BY")private String updatedBy;@TableField("UPDATED_TIME")private LocalDateTime updatedTime;@TableField("UPDATED_ID")private String updatedId;}
- 使用Postman接口测试工具,测试接口是否正常
四. Activiti 使用步骤
Activiti 主要流程操作步骤:
- 定义流程:按照BPMN的规范,使用流程定义工具,用流程符号把整个流程描述出来。
- 部署流程:把画好的流程定义文件,加载到数据库中,生成表的数据。
- 启动流程:使用 java 代码来操作数据库表中的内容。
- 处理任务:操作流程当中的各个任务。
1. 定义流程
2. 初始库表、定义、部署、操作任务代码
创建测试类
测试代码:
package com.activitidemo;import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.FileCopyUtils;import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;@SpringBootTest
class ActivitiDemoApplicationTests {// 创建 ProcessEngine 对象
// private ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// /**
// * 初始化数据库表:第一种方式
// */
// @Test
// public void testInitOne() {
// ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// System.err.println("processEngine = " + processEngine);
// }/*** 初始化数据库表* 通过读取 activiti.cfg.xml 配置文件*/@Testpublic void testInitTwo() {ProcessEngineConfiguration configuration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");ProcessEngine processEngine = configuration.buildProcessEngine();System.err.println("processEngine = " + processEngine);}/*** 流程部署*/@Testpublic void testDeploy() {// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取 repositoryService 对象RepositoryService repositoryService = processEngine.getRepositoryService();// 进行部署Deployment deployment = repositoryService.createDeployment().addClasspathResource("process/leave.bpmn20.xml").addClasspathResource("process/leave.bpmn20.png").name("请假流程").deploy();// 输出部署的一些信息System.out.println("流程部署ID:" + deployment.getId());System.out.println("流程部署名称:" + deployment.getName());System.out.println("流程部署成功");}/*** 启动流程实例*/@Testpublic void testStartProcess() {// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取 runtimeService 对象RuntimeService runtimeService = processEngine.getRuntimeService();// 1.根据流程定义的key启动流程实例,这个key是在定义bpmn的时候设置的ProcessInstance instance = runtimeService.startProcessInstanceByKey("leave");// 2.根据流程定义id启动流程实例
// ProcessInstance instance = runtimeService.startProcessInstanceById("leave:1:4");// 获取流程实例的相关信息System.out.println("流程定义的id = " + instance.getProcessDefinitionId());System.out.println("流程实例的id = " + instance.getId());System.out.println("启动流程成功 ");}/*** 查询待办任务*/@Testpublic void testSelectTodoTaskList() {String assignee = "李四";// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取指定用户审核任务TaskService taskService = processEngine.getTaskService();// 使用面对对象方式查询数据库List<Task> tasks = taskService.createTaskQuery().processDefinitionKey("leave").taskAssignee(assignee)// 返回多个结果.list();// 只返回一个结果// .singleResult();// 自定义 sql 查询// taskService.createNativeTaskQuery();// 获取流程实例的相关信息for (Task task : tasks) {System.out.println("流程定义的id = " + task.getProcessDefinitionId());System.out.println("流程实例的id = " + task.getProcessInstanceId());System.out.println("任务id = " + task.getId());System.out.println("任务名称 = " + task.getName());}}/*** 指定用户去完成任务待办:多人审批在这操作,改变审核人名称就行了*/@Testpublic void testCompleteTask() {String assignee = "李四";// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取指定用户审核任务TaskService taskService = processEngine.getTaskService();List<Task> tasks = taskService.createTaskQuery().processDefinitionKey("leave").taskAssignee(assignee).list();if (tasks != null && !tasks.isEmpty()){// 当前流程图所限制,只能做审核同意的动作for (Task task : tasks) {taskService.complete(task.getId());}}}/*** 审批添加备注*/@Testpublic void testAddComment() {String assignee = "张三";// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取指定用户审核任务TaskService taskService = processEngine.getTaskService();List<Task> tasks = taskService.createTaskQuery().processDefinitionKey("leave").taskAssignee(assignee).list();if (tasks != null && !tasks.isEmpty()) {// 当前流程图所限制,只能做审核同意的动作for (Task task : tasks) {// 添加备注taskService.addComment(task.getId(), task.getProcessInstanceId(), assignee + "表示同意");taskService.complete(task.getId());}}}/*** 查询审批历史*/@Testpublic void testSelectHistoryTask() {String processInstanceId = "2501";String assignee = "张三";// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 查看历史信息HistoryService historyService = processEngine.getHistoryService();// 获取指定用户审核任务TaskService taskService = processEngine.getTaskService();// 获取历史审核信息List<HistoricActivityInstance> userTask = historyService.createHistoricActivityInstanceQuery().activityType("userTask")// 指定实例的id.processInstanceId(processInstanceId).taskAssignee(assignee).finished().list();for (HistoricActivityInstance instance : userTask) {System.out.println("任务名称 = " + instance.getActivityName());System.out.println("任务开始时间 = " + instance.getStartTime());System.out.println("任务结束时间 = " + instance.getEndTime());System.out.println("任务耗时 = " + instance.getDurationInMillis());// 获取审批批注信息List<Comment> taskComments = taskService.getTaskComments(instance.getTaskId());if (!taskComments.isEmpty()){System.out.println("审批批注 = " + taskComments.get(0).getFullMessage());}}}/*** 查询流程相关信息*/@Testpublic void testDefinitionQuery() {// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取仓库服务RepositoryService repositoryService = processEngine.getRepositoryService();// 获取流程定义集合List<ProcessDefinition> processDefinitionList = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave")// 最新的一个版本.latestVersion().list();// 遍历集合for (ProcessDefinition definition : processDefinitionList) {System.out.println("流程定义id = " + definition.getId());System.out.println("流程定义名称 = " + definition.getName());System.out.println("流程定义key = " + definition.getKey());System.out.println("流程定义版本 = " + definition.getVersion());System.out.println("流程部署id = " + definition.getDeploymentId());System.out.println("===============");}}/*** 资源文件下载*/@Testpublic void testDownloadResource() throws IOException {// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取仓库服务RepositoryService repositoryService = processEngine.getRepositoryService();// 获取流程定义集合List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave")// 按照版本降序.orderByProcessDefinitionVersion()// 降序.desc().list();// 获取最新那个ProcessDefinition definition = list.get(0);// 获取部署idString deploymentId = definition.getDeploymentId();// 获取bpmn的输入流InputStream bpmnInputStream = repositoryService.getResourceAsStream(deploymentId, definition.getResourceName());// 获取png的输入流
// InputStream pngInputStream = repositoryService.getResourceAsStream(deploymentId, definition.getDiagramResourceName());String resourcePath = "C:/Users/ASUS/Desktop/" + File.separator + definition.getResourceName();File file = new File(resourcePath);if (!file.exists()) {file.getParentFile().mkdirs();}String diagramResourcePath = "C:/Users/ASUS/Desktop/" + File.separator + definition.getDiagramResourceName();file = new File(diagramResourcePath);if (!file.exists()) {file.getParentFile().mkdirs();}//复制文件FileCopyUtils.copy(bpmnInputStream, Files.newOutputStream(Paths.get(resourcePath)));
// FileCopyUtils.copy(pngInputStream, Files.newOutputStream(Paths.get(diagramResourcePath)));}/*** 删除已经部署的流程定义*/@Testpublic void testDeleteDeploy() {// 删除已经部署的流程定义String deploymentId = "45001";// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取仓库服务RepositoryService repositoryService = processEngine.getRepositoryService();// 删除流程定义,如果改流程定义已有流程实例启动,则会报错
// repositoryService.deleteDeployment(deploymentId);// 设置 true,级联删除流程定义,即使有启动的实例也可以删除repositoryService.deleteDeployment(deploymentId, true);}/*** 启动流程,需要进行 BusinessKey 绑定流程实例*/@Testpublic void testStartBindBusinessKey() {String businessKey = "1";// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();RuntimeService runtimeService = processEngine.getRuntimeService();// 根据流程定义的key启动流程实例,这个key是在定义bpmn的时候设置的// 在启动流程的时候将业务key加进去ProcessInstance instance = runtimeService.startProcessInstanceByKey("leave", businessKey);// 获取流程实例的相关信息System.out.println("流程定义id = " + instance.getProcessDefinitionId());System.out.println("流程实例id = " + instance.getId());System.out.println("业务标识 = " + instance.getBusinessKey());}/*** 跑到下一个节点,需要进行审批了,此时需要获取 BusinessKey 进而获取请假单信息*/@Testpublic void testGetBusinessKey() {// 1、获取李四的待办信息ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();TaskService taskService = processEngine.getTaskService();List<Task> task = taskService.createTaskQuery().taskAssignee("李四").processDefinitionKey("leave").list();// 2、获取 businessKey// 获取流程实例idString processInstanceId = task.get(1).getProcessInstanceId();RuntimeService runtimeService = processEngine.getRuntimeService();ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();String businessKey = processInstance.getBusinessKey();System.out.println("业务标识 = " + businessKey);}/*** 流程定义挂起与激活*/@Testpublic void testSuspendAllProcessInstance() {// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();// 获取仓库服务RepositoryService repositoryService = processEngine.getRepositoryService();// 获取流程定义对象ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave").singleResult();boolean suspended = processDefinition.isSuspended();// 输出流程定义状态System.out.println("流程定义状态:" + (suspended ? "已挂起" : "已激活"));String processDefinitionId = processDefinition.getId();if (suspended) {repositoryService.activateProcessDefinitionById(processDefinitionId, true, null);System.out.println("流程id:" + processDefinitionId + "已激活");} else {repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null);System.out.println("流程id:" + processDefinitionId + "已挂起");}}/*** 流程实例挂起与激活*/@Testpublic void testProcessInstance() {// 创建 ProcessEngine 对象ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();RuntimeService runtimeService = processEngine.getRuntimeService();// 获取流程定义对象List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery().processDefinitionKey("leave").list();// 遍历集合for (ProcessInstance processInstance : processInstanceList) {boolean suspended = processInstance.isSuspended();// 输出流程定义状态System.out.println("流程实例状态:" + processInstance + "," + (suspended ? "已挂起" : "已激活"));String processDefinitionId = processInstance.getId();if (suspended) {runtimeService.activateProcessInstanceById(processDefinitionId);System.out.println("流程实例id:" + processDefinitionId + "已激活");} else {runtimeService.suspendProcessInstanceById(processDefinitionId);System.out.println("流程实例id:" + processDefinitionId + "已挂起");}}}
}
相关文章:

SpringBoot 集成 Activiti 7 工作流引擎
一. 版本信息 IntelliJ IDEA 2023.3.6JDK 17Activiti 7 二. IDEA依赖插件安装 安装BPM流程图插件,如果IDEA的版本超过2020,则不支持actiBPM插件。我的IDEA是2023版本我装的是 Activiti BPMN visualizer 插件。 在Plugins 搜索 Activiti BPMN visualizer 安装创建…...

UVM初学篇 -(22)UVM field_automation 域的自动化机制
field_automation机制是域的自动化的机制,这个机制的最大的优点是可以对一些变量进行批量的处理,比如对象拷贝、克隆、打印之类的变量。 一、 成员变量的注册 使用field_automation机制首先要用uvm_field 系列宏完成变量的注册,类中的成员变…...

STL二分查找
本课主要介绍容器部分里面的二分查找函数。涉及的函数有 3 个,这 3 个函数的强两个输入参数都和迭代器有关,或者说参数是可以迭代的,而第三个参数则是你要查找的值。 1. binary_search binary_search 的返回结果是 bool 值,如果找…...

啤酒游戏—企业经营决策沙盘
感谢黄浦区文华学院的邀请,今年是为南房集团开展系统思考培训的第二年。我们现在为客户设计的一整年系统思考训练中,会将系统环路结构图与真实议题研讨作为前置内容,让大家在理解整体框架后,再体验麻省理工学院系统动力学著名的“…...

尚硅谷-react教程-求和案例-@redux-devtools/extension 开发者工具使用-笔记
## 7.求和案例_react-redux开发者工具的使用(1).npm install redux-devtools/extension(2).store中进行配置import { composeWithDevTools } from redux-devtools/extension;export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk))) src/redux/s…...

【动手学强化学习】part2-动态规划算法
阐述、总结【动手学强化学习】章节内容的学习情况,复现并理解代码。 文章目录 一、什么是动态规划?1.1概念1.2适用条件 二、算法示例2.1问题建模2.2策略迭代(policyiteration)算法2.2.1伪代码2.2.2完整代码2.2.3运行结果2.2.4代码…...

【python爬虫实战】爬取全年天气数据并做数据可视化分析!附源码
由于篇幅限制,无法展示完整代码,需要的朋友可在下方获取!100%免费。 一、主题式网络爬虫设计方案 1. 主题式网络爬虫名称:天气预报爬取数据与可视化数据 2. 主题式网络爬虫爬取的内容与数据特征分析: - 爬取内容&am…...

初识Linux · 动静态库(incomplete)
目录 前言: 静态库 动态库 前言: 继上文,我们从磁盘的理解,到了文件系统框架的基本搭建,再到软硬链接部分,我们开始逐渐理解了为什么运行程序需要./a.out了,这个前面的.是什么我们也知道了。…...

华为OD机试 - 匿名信(Java 2024 E卷 100分)
华为OD机试 2024E卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(E卷D卷A卷B卷C卷)》。 刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加…...

通过rancher2.7管理k8s1.24及1.24以上版本的k8s集群
目录 初始化实验环境 安装Rancher 登录Rancher平台 通过Rancher2.7管理已存在的k8s最新版集群 文档中的YAML文件配置直接复制粘贴可能存在格式错误,故实验中所需要的YAML文件以及本地包均打包至网盘. 链接:https://pan.baidu.com/s/1oYX4eGoBtW_R-7i…...

text-align的属性justify
text-align常用的属性是left、center、right,具体的可参考css解释,今天重点记录的对象是justify justify 可以使文本的两端都对齐在两端对齐文本中,文本行的左右两端都放在父元素的内边界上。然后,调整单词和字母间的间隔&#x…...

使用python自制桌面宠物,好玩!——枫原万叶桌宠,可以直接打包成exe去跟朋友炫耀。。。
大家好,我是小黄。 今天我们使用python实现一个桌面宠物。只需要gif动态图片就行。超级简单容易上手。 #完整源代码可在下方图片免费获取 一:下载相关的库文件。 我们本次使用到的库文件为:tkinter和pyautogui 下载命令: pip…...

使用 ASP.NET Core 8.0 创建最小 API
构建最小 API,以创建具有最小依赖项的 HTTP API。 它们非常适合需要在 ASP.NET Core 中仅包括最少文件、功能和依赖项的微服务和应用。 本教程介绍使用 ASP.NET Core 生成最小 API 的基础知识。 在 ASP.NET Core 中创建 API 的另一种方法是使用控制器。 有关在最小 …...

气候服务平台ClimateSERV2.0简介(python)
1 简介 ClimateSERV 2.0允许开发从业者、科学家/研究人员和政府决策者可视化和下载历史降雨数据、植被状况数据以及 180 天的降雨和温度预报,以增进对农业和水资源供应相关问题的理解并做出改进的决策。 这些数据可以通过 Web 应用程序直接访问,也可以…...

Docker | centos7上对docker进行安装和配置
安装docker docker配置条件安装地址安装步骤2. 卸载旧版本3. yum 安装gcc相关4. 安装需要的软件包5. 设置stable镜像仓库6. 更新yum软件包索引7. 安装docker引擎8. 启动测试9. 测试补充:设置国内docker仓库镜像 10. 卸载 centos7安装docker https://docs.docker.com…...

React--》掌握Valtio让状态管理变得轻松优雅
Valtio采用了代理模式,使状态管理变得更加直观和易于使用,同时能够与React等框架无缝集成,本文将深入探讨Valtio的核心概念、使用场景以及其在提升应用性能中的重要作用,帮助你掌握这一强大工具,从而提升开发效率和用户…...

python爬虫百度图片
直接给代码,可直接用,个人需要修改的地方有两处: self.directory 这是本地存储地址,修改为自己电脑的地址,另外,**{}**不要删spider.json_count 10 这是下载的图像组数,一组有30张图像&#x…...

前端开发:Vue中数据绑定原理
Vue 中最大的一个特征就是数据的双向绑定,而这种双向绑定的形式,一方面表现在元数据与衍生数据之间的响应,另一方面表现在元数据与视图之间的响应,而这些响应的实现方式,依赖的是数据链,因此,要…...

CTF-RE 从0到N: TEA
TEA TEA(Tiny Encryption Algorithm,轻量加密算法) 是一种简单、快速的对称加密算法。它是一个分组加密算法,通常用于加密 64 位的数据块,并使用 128 位的密钥。TEA 是一种“费斯妥结构”(Feistel structu…...

python 使用PIL获取图片长宽
在Python中,你可以使用Pillow库(PIL的一个分支和替代品)来获取图片的长和宽。Pillow提供了丰富的图像处理功能,包括获取图像的基本属性,如尺寸。 以下是一个简单的示例,展示了如何使用Pillow库来获取图片的…...

【Nas】X-DOC:搞机之PVE部署All In One(黑群晖NAS 软路由OpenWrt Docker Win10远程桌面)
【Nas】X-DOC:搞机之PVE部署All In One(黑群晖NAS & 软路由OpenWrt & Docker & Win10远程桌面) 1、原硬件配置清单:2、改AIO后增加配置清单:3、虚拟化平台PVE:4、搭建的关键服务: 1…...

linux 驱动源码分析的理解。
首先 , 是linux 驱动,我看网上的老师,在分析源码时 , 不会 所有的函数都分析,而是分析一些比较重要的函数,一些厉害的人,在分析源码时…...

鸿蒙-任务栏右击退出 或 UIAbility窗口关闭,怎么弹框拦截
onPrepareToTerminate 需要配置权限 ohos.permission.PREPARE_APP_TERMINATE 参考链接:文档中心import { emitter } from kit.BasicServicesKit; import { common } from kit.AbilityKit; import { TipsDialog } from kit.ArkUI;// entryAbility.ets 在你的uiabilit…...

【C++进阶篇】——STL的简介
【C进阶篇】——STL的简介 1.什么是STL STL(standard template libaray-标准模板库):是C标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。 2.STL的版本 原始版本 Alexander Stepanov、Meng Lee 在…...

信息安全工程师(70)网络攻击陷阱技术与应用
前言 网络攻击陷阱技术是一种主动的防御方法,作为网络安全的重要策略和技术手段,有利于网络安全管理者获得信息优势。 一、网络攻击陷阱技术原理 网络攻击陷阱技术可以消耗攻击者所拥有的资源,加重攻击者的工作量,迷惑攻击者&…...

Web保存状态的手段(Session的使用)
一,JSP中的page指令 1. <% page language“java” session“true”%> session:此页面是否使用session,默认值为true 二,使用Session完善之前的登录程序 1. 如何禁止直接输入URL地址进入登录功能的欢迎界面? …...

第五十四章 安全元素的详细信息 - DerivedKeyToken 详情
文章目录 第五十四章 安全元素的详细信息 - <DerivedKeyToken> 详情详情消息中的位置 第五十四章 安全元素的详细信息 - 详情 <DerivedKeyToken> 的目的是携带发送者和接收者可以独立使用的信息来生成相同的对称密钥。这些方可以使用该对称密钥对 SOAP 消息的相关…...

kafka 的高可用机制是什么?
大家好,我是锋哥。今天分享关于【kafka 的高可用机制是什么?】面试题?希望对大家有帮助; kafka 的高可用机制是什么? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Apache Kafka 是一个分布式消息系统&am…...

4.1.3 网站通信技术
文章目录 1. 网站通信方式2. URL - 统一资源定位符定义格式演示 3. 发送请求的4种形式在地址栏中输入URL访问超链接href属性指定URLform表单在action中指定URL通过AJAX请求后端数据 4. 两种不同返回的请求发送URL,后端处理完响应页面发送AJAX请求,后端处…...

Java-图书管理系统
我的个人主页 欢迎来到我的Java图书管理系统,接下来让我们一同探索如何书写图书管理系统吧! 1管理端和用户端 2建立相关的三个包(book、operation、user) 3建立程序入口Main类 4程序运行 1.首先图书馆管理系统分为管理员端和…...