利用Spring中的SchedulingConfigurer实现数据库配置化定时任务

目录

1.利用@Scheduled来实现传统的定时任务

2.两者的区别

3.Spring中的SchedulingConfigurer来拓展定时任务的灵活性

1)UrTaskConfig

2)TaskMain

3)BaseTask

4)效果

(1)插入配置定时任务的sql语句

(2)写一个实现类去继承BaseTask

代码示例:

pom.xml

enums枚举类

EnumTask

EnumTaskRule

数据库Dao操作

UrConfigAllDao

UrTaskTestDao

BaseTask

TaskMain

UrTaskConfig

TaskTextJob

数据库

ur_config_all

ur_task_test


1.利用@Scheduled来实现传统的定时任务

实现定时任务的方式有多种,其中最广为人知的一种是利用@Scheduled,来在代码里面实现代码的定时任务。但是这样简单粗暴的方式可以实现定时任务的功能,但是在我们之后的开发过程中会频繁的使用到定时任务,如果拓展定时任务实现方式的灵活性与拓展性就成了问题。

直接使用@Scheduled的方式来实现定时任务有是有效,但是每增加一个定时任务,或者要修改定时任务的规则,比如修改cron表达式为固定频率或者固定次数,每次修改都要重启一下服务,为我们的开发带来了许多不方便的地方。因为想了一下,可以利用SPring中的SchedulingConfigurer来实现定时任务的拓展性

@Component
@Slf4j
public class MonthEndDataSync {@Autowiredprivate CarBrandService carBrandService;@Autowiredprivate CarModelsService carModelsService;@Autowiredprivate CarSeriesService carSeriesService;@Scheduled(cron = "0 0 3 L *  *")public void MonthEndDataSync() {carBrandService.syncCarBrand4Network();carModelsService.syncCarModels4Network();carSeriesService.syncCarSeries4Network();log.debug("[MonthEndDataSync start] 月底同步数据完成");}

2.两者的区别

1.@Scheduled不支持动态修改定时周期,只能停止服务器,修改cron表达式,再启动服务器;SchedulingConfigurer可以动态修改

2.@Scheduled只能是单线程,而SchedulingConfigurer默认是单线程,可以通过添加线程池,实现多线程下定时任务的运行

3.Spring中的SchedulingConfigurer来拓展定时任务的灵活性

总体思路:

1)UrTaskConfig

UrTaskConfig这一类用来获取定时任务的一些配置,定时任务的规则,定时任务的状态,以及定时任务的类型(cron表达式、固定频率、固定间隔)

2)TaskMain

实现SchedulingConfigurer配置Spring的定时任务调度器,在TaskMain类中实现configureTasks方法,根据不同的定时任务规则来实现不同的定时任务。

3)BaseTask

是一个抽象类实现Runnable接口,实现了Runnable接口。这意味着BaseTask的实例可以被当作线程来运行,即可以被提交给线程池或者由Thread对象直接启动。

在BaseTask中定义了三个抽象方法before(),excute(),after(),方法需要由具体的任务子类来实现,可以创建多个不同的任务类,它们都继承自BaseTask,并实现自己的before(),excute(),after()方法。

4)效果

设置一个每5秒执行的定时任务

最终的效果,在我们实际开发的过程中想要实现一个定时任务就只需要做两步

(1)插入配置定时任务的sql语句

例如

INSERT INTO `task_db`.`ur_config_all` (`ID`, `TABLE_NAME`, `COLUMN_NAME`, `S_ID`, `VALUEE`) VALUES (1, 'TASK_WORK', 'TaskTextJob', 'CORN_RULE', '*/5 * * * * *');

INSERT INTO `task_db`.`ur_config_all` (`ID`, `TABLE_NAME`, `COLUMN_NAME`, `S_ID`, `VALUEE`) VALUES (2, 'TASK_WORK', 'TaskTextJob', 'CORN_STATUS', '1');

INSERT INTO `task_db`.`ur_config_all` (`ID`, `TABLE_NAME`, `COLUMN_NAME`, `S_ID`, `VALUEE`) VALUES (3, 'TASK_WORK', 'TaskTextJob', 'CORN_TYPE', '1');

(2)写一个实现类去继承BaseTask

这样就会开启一个定时任务例如:TaskTextJob

代码示例:

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion><groupId>org.example</groupId>
<artifactId>untitled</artifactId><version>1.0-SNAPSHOT</version><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>16</source><target>16</target></configuration></plugin></plugins></build><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring-boot-starter.version>3.1.1</spring-boot-starter.version><mybatis-plus-boot-starter.version>3.5.3.1</mybatis-plus-boot-starter.version><fastjson.version>2.0.32</fastjson.version><mysql-connector-j.version>8.0.33</mysql-connector-j.version><disruptor.version>3.4.2</disruptor.version><spring-boot-starter-web.version>3.1.1</spring-boot-starter-web.version></properties><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.1</version><relativePath/> <!-- lookup parent from repository --></parent><dependencies><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus-boot-starter.version}</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>${mysql-connector-j.version}</version></dependency><dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId><version>${disruptor.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>3.1.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>${spring-boot-starter-web.version}</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.18</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId><version>3.1.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId><version>3.1.1</version></dependency></dependencies></project>
enums枚举类
EnumTask
public enum EnumTask {corn_task("TASK_WORK", "类名", "CORN_RULE", "定时任务"),corn_task_status("TASK_WORK", "类名", "CORN_STATUS", "定时任务开关"),corn_task_type("TASK_WORK", "类名", "CORN_TYPE", "定时任务类型");private final String tableName;private final String columnName;private final String sId;private final String valuee;EnumTask(String tableName, String columnName, String sId, String valuee) {this.columnName = columnName;this.sId = sId;this.valuee = valuee;this.tableName = tableName;}public String getTableName() {return tableName;}public String getColumnName() {return columnName;}public String getsId() {return sId;}public String getValuee() {return valuee;}
}
EnumTaskRule
public enum EnumTaskRule {ONE("1","corn表达式"),TWO("2","固定频率"),THREE("3","固定间隔"),STATUS1("1","开启"),STATUS0("0","禁用");private final String code;private final String msg;EnumTaskRule(String code, String msg) {this.code = code;this.msg = msg;}public String getCode() {return code;}public String getMsg() {return msg;}
}
数据库Dao操作
UrConfigAllDao
public interface UrConfigAllDao extends BaseMapper<UrConfigAll> {@Select("""select TABLE_NAME, COLUMN_NAME, S_ID, VALUEE,IDfrom ur_config_allwhere TABLE_NAME = #{tableName}and COLUMN_NAME = #{columnName}and S_ID = #{sId}""")UrConfigAll queryUrConfigAll(String tableName, String columnName, String sId);
}
UrTaskTestDao
public interface UrTaskTestDao extends BaseMapper<UrTaskTest> {@Select("""select ID, TASK_NAME, PROCESS_ID, CREATE_TIME, UPDATE_TIME,VERSION,STATUS,CLASS_NAME,METHON,CRON,CRON_TYPE,NEXT_EXEC_TIMEfrom ur_task_testwhere TASK_NAME = #{taskName}""")UrTaskTest selectByTaskName(String taskName);@Update("""update ur_task_test setPROCESS_ID = #{processId},UPDATE_TIME = #{updateTime},VERSION = VERSION+1where  TASK_NAME = #{taskName} and  VERSION=#{version}""")int updateProcessorIDandVersionByTaskName(UrTaskTest urTaskTest);@Delete("""DELETE FROM ur_task_test;""")int deleteAll();@Insert("""insert into ur_task_test (TASK_NAME, PROCESS_ID,CREATE_TIME, UPDATE_TIME,VERSION,STATUS,CLASS_NAME,METHON,CRON,CRON_TYPE,NEXT_EXEC_TIME)values (#{taskName}, #{processId},#{createTime},  #{updateTime},#{version},  #{status},  #{className},  #{methon} ,  #{cron}, #{cronType},#{nextExecTime})""")int insert2(UrTaskTest urTaskTest);
}
BaseTask
@Component
public abstract class BaseTask implements Runnable{private final static Logger log = LoggerFactory.getLogger(BaseTask.class);@Autowiredprivate UrConfigAllService urConfigAllService;@Autowiredprivate UrTaskTestService taskTestService ;private String taskName;public String getTaskName() {return taskName;}public void setTaskName(String taskName) {this.taskName = taskName;}//获取数据库中关于定时任务的配置UrTaskConfig getUrTaskConfig(){UrTaskConfig urTaskConfig=new UrTaskConfig();return urTaskConfig.getUrTaskConfig(taskName,urConfigAllService);}//执行前public abstract void before();//执行public abstract void execute();//执行后public abstract void after();@Overridepublic  void run(){//生成任务进程uuidString uuid = UUID.randomUUID().toString();//根据taskName在ur_task_test表中查询执行进程UrTaskTest urTaskTest = taskTestService.selectByTaskName(taskName);//获取任务进程的statusif (urTaskTest==null){System.out.println("任务 :" + taskName + " 进程不存在");return;}//判断status,如果是禁用就直接returnif (StringUtils.equals(String.valueOf(urTaskTest.getStatus()),EnumTaskRule.STATUS0.getCode()))return;//uuid设置为进程idurTaskTest.setProcessId(uuid);//版本也是urTaskTest.setVersion(urTaskTest.getVersion());//更新ur_task_test中的字段System.out.println("更新ur_task_test中的字段"+urTaskTest);int i=taskTestService.updateProcessIDandVersionByTaskName(urTaskTest);//更新返回为0,则该任务已经在其他节点执行if (i==0){System.out.println("任务 :" + taskName + " 已经在其他节点执行");return;}//更新成功则任务进程开始System.out.println("任务 :" + uuid + " 执行开始");try {//前置处理before();execute();//后置处理after();}catch (Exception e){throw new RuntimeException(e);}finally {urTaskTest.setTaskName(taskName);urTaskTest.setProcessId(null);urTaskTest.setUpdateTime(new Date());System.out.println("任务进程查看urTaskTest的值 :" + urTaskTest + " 运行结束");taskTestService.updateProcessIDandVersionByTaskName(urTaskTest);System.out.println("任务进程 :" + uuid + " 运行结束");}}}
TaskMain
@Component
public class TaskMain implements SchedulingConfigurer {@Autowiredprivate ApplicationContext applicationContext;@Autowiredprivate UrTaskTestService urTaskTestService;private final static Logger log = LoggerFactory.getLogger(TaskMain.class);//注册定时任务@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {//获取所有的定时任务,它们都是BaseTask类的实例或其子类Map<String, BaseTask> map = applicationContext.getBeansOfType(BaseTask.class);System.out.println("获取所有的定时任务"+map);//开始线程数taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));//清除所有的进程urTaskTestService.deleteAll();System.out.println("获取所有的定时任务111"+map);//遍历map(存储所有BaseTask bean的Map),针对每个bean执行相应的操作for (String key:map.keySet()){//为每个定时任务创建一个UrTaskTest对象,并设置其属性,如创建时间、任务名称、版本等System.out.println("为每个定时任务创建一个UrTaskTest对象"+key);UrTaskTest urTaskTest=new UrTaskTest().setCreateTime(new Date()).setTaskName(key).setVersion(0L);//从Map中获取当前key对应的BaseTask对象,并设置其任务名称taskNmaeBaseTask baseTask = map.get(key);baseTask.setTaskName(key);//从BaseTask对象中获取TaskConfigAll对象,并获取其配置信息UrTaskConfig urTaskConfig = baseTask.getUrTaskConfig();String cornStatus = urTaskConfig.getCornStatus();String taskName = urTaskConfig.getTaskName();//如果定时任务的cronStatus为TaskEnum.status0.getCode(),表示任务已停止,则跳过当前循环。if (StringUtils.equals(cornStatus, EnumTaskRule.STATUS0.getCode())){System.out.println("{} 定时任务配置状态为禁用"+urTaskConfig);continue;}//设置urTaskControl的状态为TaskEnum.status1.getCode()urTaskTest.setStatus(EnumTaskRule.STATUS1.getCode());//如果cron(定时规则)为空,则记录日志并跳过当前循环String corn = urTaskConfig.getCorn();if (StringUtils.isEmpty(corn)){System.out.println("{} 定时任务没配置定时的规则规则"+taskName);continue;}//设置urTaskControlAll的其他属性,如cronType、cron、methon和classNameurTaskTest.setCronType(urTaskConfig.getCornModern());urTaskTest.setCron(corn);urTaskTest.setMethon("execute()");urTaskTest.setClassName(baseTask.getClass().getName());//根据urTaskConfig.getCornModern()的值判断注册何种类型的定时任务try {//如果urTaskConfig.getCornModern()的值为TaskEnum.ONE.getCode(),则注册一个基于CronTrigger的触发任务if (EnumTaskRule.ONE.getCode().equals(urTaskConfig.getCornModern())){taskRegistrar.addTriggerTask(baseTask,sheduledConfig->{Date date = new CronTrigger(corn).nextExecutionTime(sheduledConfig);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String formattedDate = sdf.format(date);// 打印格式化后的日期System.out.println(taskName + "执行定时任务的时间:" + formattedDate);return date.toInstant();});//如果urTaskConfig.getCornModern()的值为TaskEnum.TWO.getCode(),则注册一个固定频率的定时任务}else if (EnumTaskRule.TWO.getCode().equals(urTaskConfig.getCornModern())){int parseInt = Integer.parseInt(corn);taskRegistrar.addFixedRateTask(baseTask,parseInt);System.out.println("已添加固定频率任务,频率为:" + parseInt);//如果urTaskConfig.getCornModern()的值为TaskEnum.THREE.getCode(),则注册一个固定次数的定时任务}else if (EnumTaskRule.THREE.getCode().equals(urTaskConfig.getCornModern())){int fixedDelay = Integer.parseInt(corn);taskRegistrar.addFixedDelayTask(baseTask, fixedDelay);}else {//最后判断是否有定时任务的规则System.out.println("{} 定时任务未找到动态方法"+taskName);continue;}//插入urTaskTest一条数据到定时任务的进程表中urTaskTestService.insert2(urTaskTest);}catch (Exception e){throw new RuntimeException(e);}}}
}
UrTaskConfig
@Data
@Accessors(chain = true)
public class UrTaskConfig {private final static Logger log = LoggerFactory.getLogger(UrTaskConfig.class);//定时任务类名private String taskName;//定时任务规则private String corn;//定时任务状态private String cornStatus;//定时任务的类型private String cornModern;public UrTaskConfig getUrTaskConfig(String taskName, UrConfigAllService urConfigAllService){//获取定时任务规则UrConfigAll urConfigAll = urConfigAllService.getUrConfigAll(EnumTask.corn_task.getTableName(), taskName, EnumTask.corn_task.getsId());String rule = Optional.ofNullable(urConfigAll).map(UrConfigAll::getValuee).orElse(null);//获取定时任务状态UrConfigAll urConfigAll1 = urConfigAllService.getUrConfigAll(EnumTask.corn_task_status.getTableName(), taskName, EnumTask.corn_task_type.getsId());String ruleState = Optional.ofNullable(urConfigAll1).map(UrConfigAll::getValuee).orElse(null);//定时任务任务类型UrConfigAll urConfigAll2 = urConfigAllService.getUrConfigAll( EnumTask.corn_task_type.getTableName(), taskName,EnumTask.corn_task_type.getsId());String ruleType = Optional.ofNullable(urConfigAll2).map(UrConfigAll::getValuee).orElse(EnumTaskRule.ONE.getCode());UrTaskConfig urTaskConfig=new UrTaskConfig().setTaskName(taskName).setCorn(rule).setCornStatus(ruleState).setCornModern(ruleType);System.out.println("定时任务配置参数"+JSON.toJSONString(urTaskConfig) );return urTaskConfig;}
}
TaskTextJob
@Component
public class TaskTextJob extends BaseTask {@Overridepublic void before() {}@Overridepublic void execute() {// 获取当前时间LocalDateTime currentTime = LocalDateTime.now();// 定义格式化模板,精确到秒DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");// 格式化当前时间String formattedTime = currentTime.format(formatter);// 输出格式化后的时间System.out.println("执行定时任务测试: " + formattedTime);}@Overridepublic void after() {}
}

数据库
ur_config_all
CREATE TABLE `ur_config_all` (`ID` int NOT NULL AUTO_INCREMENT COMMENT '主键',`TABLE_NAME` varchar(255) NOT NULL,`COLUMN_NAME` varchar(255) NOT NULL,`S_ID` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,`VALUEE` varchar(255) NOT NULL,PRIMARY KEY (`ID`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
ur_task_test
CREATE TABLE `ur_task_test` (`ID` int NOT NULL AUTO_INCREMENT COMMENT '任务ID',`TASK_NAME` varchar(255) NOT NULL COMMENT '任务名',`PROCESS_ID` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '进程ID',`CREATE_TIME` datetime NOT NULL COMMENT '创建时间',`UPDATE_TIME` datetime DEFAULT NULL COMMENT '更新时间',`VERSION` bigint NOT NULL COMMENT '版本',`STATUS` varchar(50) NOT NULL COMMENT '状态',`CLASS_NAME` varchar(255) NOT NULL COMMENT '类路径',`METHON` varchar(255) NOT NULL COMMENT '方法',`CRON` varchar(255) DEFAULT NULL COMMENT '定时规则 有2种值,表达式和毫秒',`CRON_TYPE` varchar(50) DEFAULT NULL COMMENT '定时任务类型 1:cron表达式,2:固定频率,3:固定间隔',`NEXT_EXEC_TIME` datetime DEFAULT NULL COMMENT '任务名',PRIMARY KEY (`ID`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

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

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

相关文章

【webrtc】Chrome和Firefox在SDP协商过程中,针对localhost的不同处理

内网下chrome端webrtc协商失败 现象 我有一个webrtc服务器在局域网内&#xff0c;使用chrome浏览器访问时&#xff0c;发现webrtc在做媒体协商时失败。 具体表现是&#xff0c;在交换sdp后&#xff0c;ice的状态是oniceconnectionstatechange: failed 但是换成Firefox浏览器…

广东理工学院携手泰迪智能科技成功部署人工智能实验室

广东理工学院是经国家教育部批准设立的全日制普通本科院校&#xff0c;入选全国应用型人才培养工程培养基地、国家级众创空间试点单位、广东省高校电子商务人才孵化基地。开设34个本科专业&#xff0c;涵盖工学、经济学、管理学、文学、艺术学、教育学等6大学科门类&#xff0c…

docker容器技术篇:容器集群管理实战mesos+zookeeper+marathon(一)

容器集群管理实战mesoszookeepermarathon&#xff08;一&#xff09; mesos概述 1.1 Mesos是什么 Apache Mesos 是一个基于多资源调度的集群管理软件&#xff0c;提供了有效的、跨分布式应用或框架的资源隔离和共享&#xff0c;可以运行 Hadoop、Spark以及docker等。 1.2 为…

自然语言处理: 第二十八章大模型基底之llama3

项目地址: meta-llama/llama3: The official Meta Llama 3 GitHub site 前言 LLaMa系列一直是人们关注的焦点&#xff0c;Meta在4月18日发布了其最新大型语言模型 LLaMA 3。该模型将被集成到其虚拟助手Meta AI中。Meta自称8B和70B的LLaMA 3是当今 8B 和 70B 参数规模的最佳模…

npm install 卡在still idealTree buildDeps不动

前言 再使用npm install 安装包依赖时 发现一直卡住 停留在 观察node_cache下的_logs文件 发现一直在拉取包 37 silly idealTree buildDeps 38 silly fetch manifest riophae/vue-treeselect0.4.0尝试解决 尝试设置了taobao镜像源 依然如此 获取已经设置的镜像源 确实是ta…

图像哈希:全局+局部提取特征

文章信息 作者&#xff1a;梁小平&#xff0c;唐振军期刊&#xff1a;ACM Trans. Multimedia Comput. Commun. Appl&#xff08;三区&#xff09;题目&#xff1a;Robust Hashing via Global and Local Invariant Features for Image Copy Detection 目的、实验步骤及结论 目…

学习Rust的第10天:枚举和模式匹配

今天我们来看看一个类似的概念 enums 。 Enums: We saw that in Rust, enums are data types that list possible values, giving a simple and type-safe mechanism to describe alternatives. We looked at how to create enums and use them to represent similar possibili…

webpack中mode、NODE_ENV、DefinePlugin、cross-env的使用

本文讲的全部知识点&#xff0c;都是和webpack相关的。如果你之前有疑问&#xff0c;那本文一定能帮你搞清楚。 问题来源一般是类似下面代码&#xff08;webpack.json中&#xff09;&#xff1a; "scripts": {"dev": "cross-env NODE_ENVdevelopmen…

opencv android 使用笔记

目录 获取app路径&#xff1a; 下载&#xff1a;OpenCV-android-sdk cmakelist配置&#xff1a; 头文件路径&#xff1a; 编译报错&#xff1a;clang: error: linker command failed with exit code 1 (use -v to see invocation) 读取图片例子 保存mp4 获取app路径&am…

自定义一个RedisTemplate

1.引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis&…

springcloud Ribbon的详解

1、Ribbon是什么 Ribbon是Netflix发布的开源项目&#xff0c;Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的框架。 2、Ribbon能干什么 LB负载均衡(Load Balance)是什么&#xff1f;简单的说就是将用户的请求平摊的分配到多个服务上&#xff0c;从而达…

<前端>Electron-builder为公证后的app打更新信息latest.yml

MacOS下&#xff0c;Electron-builder可以很方便的为测试包app打更新信息&#xff08;latest-mac.yml&#xff09;。 但是&#xff0c;正式发布的时候&#xff0c;不可能用测试包app&#xff0c;因为还没有进行公证。如何为公证的app打latest-mac.yml呢。 其实观察latest-mac.y…

Keil和VSCode协同开发STM32程序

系列文章 STM32单片机系列专栏 C语言术语和结构总结专栏 文章目录 1. 配置环境 2. 测试打开工程 3. 测试编译工程 随着项目的复杂度上升&#xff0c;开发者不仅需要强大的硬件支持&#xff0c;还需要一个高效和灵活的开发环境。 vscode是一款集成大量可以便携开发插件的代码…

C++中的list类模拟实现

目录 list类模拟实现 list类节点结构设计 list类非const迭代器结构设计 迭代器基本结构设计 迭代器构造函数 operator()函数 operator*()函数 operator!()函数 operator(int)函数 operator--()函数 operator--(int)函数 operator()函数 operator->()函数 list…

TiDB 6.x 新特性解读 | Collation 规则

对数据库而言&#xff0c;合适的字符集和 collation 规则能够大大提升使用者运维和分析的效率。TiDB 从 v4.0 开始支持新 collation 规则&#xff0c;并于 TiDB 6.0 版本进行了更新。本文将深入解读 Collation 规则在 TiDB 6.0 中的变更和应用。 引 这里的“引”&#xff0c;…

Modbus转Profinet网关接称重设备与工控机通讯

Modbus转Profinet网关&#xff08;XD-MDPN100&#xff09;是一种能够实现Modbus协议和Profinet协议之间转换的设备。Modbus转Profinet网关可提供单个或多个RS485接口&#xff0c;使得不同设备之间可以顺利进行通信&#xff0c;进一步提升了工业自动化程度。 通过使用Modbus转Pr…

相亲平台app小程序

相亲平台app小程序是一种基于手机应用的微型程序&#xff0c;专为在线相亲交友活动设计。它提供了一系列的功能&#xff0c;旨在帮助用户更方便、更高效地找到心仪的伴侣。 首先&#xff0c;用户可以在个人资料部分上传照片、填写个人资料、设置兴趣爱好等信息&#xff0c;以便…

Electron+Vue3+ElectronForge整合 - 打包时整合 -分步打包

说明 本文介绍一下 Electron Vue3 的打包整合的基本操作。实现的效果是 &#xff1a; 1、一个正常的Vue3项目&#xff1b; 2、整合加入 Electron 框架 &#xff1a;开发时 Electron 加载的是开发的vue项目&#xff1b; 3、完成打包时整合&#xff1a;3.1 先完成vue3项目的正常…

Laravel 6 - 第十一章 中间件

​ 文章目录 Laravel 6 - 第一章 简介 Laravel 6 - 第二章 项目搭建 Laravel 6 - 第三章 文件夹结构 Laravel 6 - 第四章 生命周期 Laravel 6 - 第五章 控制反转和依赖注入 Laravel 6 - 第六章 服务容器 Laravel 6 - 第七章 服务提供者 Laravel 6 - 第八章 门面 Laravel 6 - …

深度解析:云计算的三宝——IaaS、PaaS和SaaS

4月22日&#xff0c;腾讯宣布旗下协作SaaS产品全面接入腾讯混元大模型&#xff0c;除去企业微信、腾讯会议、腾讯文档等“一门三杰”产品&#xff0c;腾讯乐享、腾讯电子签、腾讯问卷、腾讯云AI代码助手等协作SaaS产品也都已实现智能化升级。大模型应用落地再加速。 那么什么是…