当前位置: 首页 > article >正文

实现动态增QuartzJob,通过自定义注解调用相应方法

:::tip
动态增加Quartz定时任务,通过自定义注解来实现具体的定时任务方法调用。
:::
相关依赖如下

<!-- 用来动态创建 Quartz 定时任务 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

1. 注解及相关实体类

1. TaskDesc注解

用于描述定时任务的方法名和描述信息, 方便

import java.lang.annotation.*;/*** @author eleven* @date 2025/2/25 9:45* @apiNote*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TaskDesc {String methodName();String desc();
}

2. 任务实体类

@Data
@TableName("sys_task_config")
@ApiModel(value="定时任务配置")
public class TaskConfig extends BaseEntity {@ApiModelProperty("定时任务表达式")private String cron;@ApiModelProperty("执行类的全限定名")private String execClass;@ApiModelProperty("方法名")private String execMethod;@ApiModelProperty("是否运行")private Boolean startFlag;@ApiModelProperty("任务名称")private String cronName;public String getExecMethod() {return StrUtil.isNotBlank(execMethod) ? execMethod.replace("()", "").trim() : execMethod;}public String getExecClass() {return StrUtil.isNotBlank(execClass) ? execClass.trim(): execClass;}public String getCron() {return StrUtil.isNotBlank(cron) ? cron.trim() : cron;}
} 

3. 可选任务配置 vo

用于前端展示,前端配置定时任务的时候只能从 @TaskDesc 注解中获取到的方法名中选择。
也是为了限制前端用户乱填方法名,避免定时任务执行失败

@Data
public class TaskDescVo {private Integer index;private String beanName;private String className;private String methodName;private String desc;
} 

4. 任务执行记录

@Data
@TableName("credit_task_run_log")
@ApiModel("定时任务日志")
public class TaskRunLog extends BaseEntity<TaskRunLog> {@NotBlank(message = "任务id不能为空")private String taskId;@ApiModelProperty("任务开始时间")private LocalDateTime runTime;@ApiModelProperty("任务完成时间")private LocalDateTime completedTime;@ApiModelProperty("任务间隔时间")private Long intervalSeconds;@ApiModelProperty("任务运行状态")private Boolean runFlag;@ApiModelProperty("任务运行消息")private String message;public LocalDateTime getRunTime() {return getTime(runTime);}public LocalDateTime getCompletedTime() {return getTime(completedTime);}public LocalDateTime getTime(LocalDateTime time) {return Optional.ofNullable(time).orElse(LocalDateTime.now());}public Long getIntervalSeconds() {return Math.abs(Duration.between(getRunTime(), getCompletedTime()).getSeconds());}
}

5. CronDto

前端传入选择的执行时间,通过CronUtil生成cron表达式

import lombok.Data;import javax.validation.constraints.NotNull;
import java.util.List;/*** @author eleven* @date 2023/12/6 8:19* @apiNote*/
@Data
public class CronDto {/*** 选择的小时*/@NotNull(message = "执行小时参数不允许为空")private List<String> chooseHours;/*** 选择的天数*/private List<String> chooseDays;/*** 选择周几执行*/private List<String> chooseDayOfWeeks;
}

2. 定时任务配置

1. PostRunner

用于在项目启动的时候,从数据库中获取到所有的定时任务配置,然后根据配

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import net.lesscoding.task.domain.TaskConfig;
import net.lesscoding.task.service.TaskConfigService;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.util.List;/*** @author eleven* @date 2024/11/11 15:01* @apiNote*/
@Component
@Slf4j
public class PostRunner {@Autowiredprivate TaskConfigService taskConfigService;@Autowiredprivate SchedulerFactoryBean schedulerFactoryBean;@Autowiredprivate Gson gson;@PostConstructpublic void run() throws Exception {List<TaskConfig> planTaskList = taskConfigService.selectAll();log.info("==============定时任务配置中心日志开始====================");log.info("计划任务列表:{}", gson.toJson(planTaskList));log.info("==============定时任务配置中心日志结束====================");Scheduler scheduler = schedulerFactoryBean.getScheduler();if (CollUtil.isNotEmpty(planTaskList)) {for (TaskConfig planTask : planTaskList) {JobDetail jobDetail = JobBuilder.newJob(RunnerJob.class).withIdentity(planTask.getId(), StrUtil.format("{}#{}", planTask.getExecClass(), planTask.getExecMethod())).build();Trigger trigger = TriggerBuilder.newTrigger().withIdentity(planTask.getId(), StrUtil.format("{}#{}", planTask.getExecClass(), planTask.getExecMethod())).startNow().withSchedule(CronScheduleBuilder.cronSchedule(planTask.getCron())).build();scheduler.scheduleJob(jobDetail, trigger);scheduler.start();}}}
}

2. Job类

具体 Quartz 任务执行的 Job, Quartz 最终会调用 RunnerJobexecute 方法来执行定时任务

import net.lesscoding.task.service.TaskConfigService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;/*** @author eleven* @date 2024/11/11 14:22* @apiNote*/
@Slf4j
@Component
public class RunnerJob implements Job {@Autowiredprivate TaskConfigService taskConfigService;@Overridepublic void execute(JobExecutionContext jobExecutionContext) {JobDetail jobDetail = jobExecutionContext.getJobDetail();JobKey key = jobDetail.getKey();String planId = key.getName();log.info("{} trigger {}", planId, ((CronTriggerImpl) jobExecutionContext.getTrigger()).getCronExpression());log.info("{} jobKey {} time {}", planId, key, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));log.info("定时任务开始执行");try {taskConfigService.runPlan(jobDetail);} catch (Exception e) {throw new RuntimeException(e);}}
}

3. 定时任务控制器

用于前端展示定时任务配置,以及新增、修改、删除定时任务配置

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.lesscoding.task.core.annotations.Log;
import net.lesscoding.task.core.common.AjaxResult;
import net.lesscoding.task.core.enums.BusinessType;
import net.lesscoding.task.domain.CronDto;
import net.lesscoding.task.domain.TaskConfig;
import net.lesscoding.task.service.TaskConfigService;
import net.lesscoding.task.utils.ResultUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import javax.validation.Valid;/*** @author eleven* @date 2024/11/11 15:56* @apiNote*/@Api(tags = "定时任务配置")
@RestController
@RequestMapping("/task/config")
public class TaskConfigController {@Autowiredprivate TaskConfigService taskConfigService;@ApiOperation("查询配置列表")@PostMapping("/page")public AjaxResult page(@RequestBody TaskConfig taskConfig) {Page<TaskConfig> list = taskConfigService.getConfigList(taskConfig);return ResultUtil.success(list);}@ApiOperation("编辑配置")@PostMapping("/edit")@Log(title = "编辑定时任务配置", businessType = BusinessType.UPDATE)public AjaxResult edit(@RequestBody TaskConfig taskConfig) throws SchedulerException {return ResultUtil.toAjax(taskConfigService.editTaskConfig(taskConfig));}@PostMapping("/getCron")@ApiOperation("获取表达式")public AjaxResult getCron(@Valid @RequestBody CronDto dto) {return ResultUtil.success(taskConfigService.getCron(dto));}@ApiOperation("删除配置")@DeleteMapping("/del/{id}")@Log(title = "删除定时任务配置", businessType = BusinessType.DELETE)public AjaxResult del(@PathVariable String id) throws SchedulerException {return ResultUtil.toAjax(taskConfigService.delTaskConfig(id));}@ApiOperation("获取所有任务列表")@GetMapping("/taskList")public AjaxResult taskList() {return ResultUtil.success(taskConfigService.getAllTaskDescList());}
}

4. ServiceImpl实现类

用于实现定时任务的具体逻辑,包括获取所有任务列表、获取表达式、编辑配置、删除配置、获取配置列表、运行计划等方法

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.lesscoding.task.core.annotations.TaskDesc;
import net.lesscoding.task.core.common.GlobalException;
import net.lesscoding.task.dao.TaskConfigMapper;
import net.lesscoding.task.dao.TaskRunLogMapper;
import net.lesscoding.task.domain.CronDto;
import net.lesscoding.task.domain.TaskConfig;
import net.lesscoding.task.domain.TaskRunLog;
import net.lesscoding.task.model.vo.TaskDescVo;
import net.lesscoding.task.runner.RunnerJob;
import net.lesscoding.task.service.TaskConfigService;
import net.lesscoding.task.utils.CronUtil;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;/*** @author eleven* @date 2024/11/11 14:21* @apiNote*/
@Service
@Slf4j
public class TaskConfigServiceImpl extends ServiceImpl<TaskConfigMapper, TaskConfig> implements TaskConfigService {@Autowiredprivate TaskConfigMapper configMapper;@Autowiredprivate TaskRunLogMapper runLogMapper;@Autowiredprivate ConfigurableListableBeanFactory beanFactory;@Autowiredprivate SchedulerFactoryBean schedulerFactoryBean;@Autowiredprivate ApplicationContext applicationContext;@Overridepublic List<TaskConfig> selectAll() {return configMapper.selectList(new QueryWrapper<>());}/*** 具体执行任务的方法* @param jobDetail Quartz的JobDetail对象,包含任务的详细信息* @return*/@Override@Asyncpublic void runPlan(JobDetail jobDetail) {JobKey key = jobDetail.getKey();String taskId = key.getName();TaskRunLog runLog = new TaskRunLog();runLog.setId(IdUtil.simpleUUID());runLog.setTaskId(taskId);runLog.setRunTime(LocalDateTime.now());TaskConfig taskConfig = configMapper.selectById(taskId);if (taskConfig == null || !taskConfig.getStartFlag()) {String logStr = StrUtil.format("任务ID {} 不存在或配置为关闭 {}", taskId, taskConfig);log.info(logStr);runLog.setRunFlag(false);runLog.setCompletedTime(LocalDateTime.now());runLog.setMessage(logStr);runLogMapper.insert(runLog);return;}String className = taskConfig.getExecClass();String methodName = taskConfig.getExecMethod();try {// 这里可以直接通过 applicationContext 获取到类的实例// Object bean = applicationContext.getBean(className);// 加载类并获取实例Class<?> execClass = getClass().getClassLoader().loadClass(className);// 从Spring容器中获取实例Object bean = beanFactory.getBean(execClass);// 获取方法Method execMethod = execClass.getDeclaredMethod(methodName);// 执行方法Object invoke = execMethod.invoke(bean);runLog.setRunFlag(true);runLog.setMessage(String.valueOf(invoke));} catch (Exception e) {runLog.setRunFlag(false);runLog.setMessage(e.getCause().getMessage());log.error("执行任务失败", e);}runLog.setCompletedTime(LocalDateTime.now());runLogMapper.insert(runLog);}@Overridepublic Page<TaskConfig> getConfigList(TaskConfig taskConfig) {PageDTO page = taskConfig.getPage();List<TaskConfig> list = configMapper.getPageByLike(page, taskConfig);page.setRecords(list);return page;}@Overridepublic int editTaskConfig(TaskConfig taskConfig) throws SchedulerException {checkEditTaskConfig(taskConfig);if (StrUtil.isBlank(taskConfig.getId())) {return saveTaskConfig(taskConfig);}return updateTaskConfig(taskConfig);}@Overridepublic int delTaskConfig(String id) throws SchedulerException {TaskConfig taskConfig = configMapper.selectById(id);deleteJob(taskConfig);return configMapper.deleteById(id);}private void checkEditTaskConfig(TaskConfig taskConfig) {boolean valid = CronUtil.isValid(taskConfig.getCron());if (!valid) {throw new GlobalException("cron表达式不合法");}try {Class<?> execClass = getClass().getClassLoader().loadClass(taskConfig.getExecClass());Object bean = beanFactory.getBean(execClass);if (bean == null) {throw new GlobalException("请检查当前类名是否存在");}Method declaredMethod = execClass.getDeclaredMethod(taskConfig.getExecMethod());if (declaredMethod == null) {throw new GlobalException(StrUtil.format("请检查当前方法{}#{}()是否存在", taskConfig.getExecClass(), taskConfig.getExecMethod()));}} catch (ClassNotFoundException e) {throw new GlobalException("请检查当前类名是否存在");} catch (NoSuchMethodException e) {throw new GlobalException(StrUtil.format("请检查当前方法{}#{}()是否存在", taskConfig.getExecClass(), taskConfig.getExecMethod()));}List<TaskConfig> allTasks = selectAll();List<TaskConfig> sameTaskList = allTasks.stream().filter(item -> StrUtil.equals(item.getExecClass(), taskConfig.getExecClass())&& StrUtil.equals(item.getExecMethod(), taskConfig.getExecMethod())).collect(Collectors.toList());if (CollUtil.isNotEmpty(sameTaskList)) {// 新增任务的时候存在相同的类名和方法名if (StrUtil.isBlank(taskConfig.getId())) {throw new GlobalException(StrUtil.format("任务{}.{}()已存在", taskConfig.getExecClass(), taskConfig.getExecMethod()));}// 修改任务的时候存在相同的类名和方法名if (sameTaskList.size() == 1 && !StrUtil.equals(sameTaskList.get(0).getId(), taskConfig.getId())) {throw new GlobalException(StrUtil.format("任务{}.{}()已存在", taskConfig.getExecClass(), taskConfig.getExecMethod()));}}}public int saveTaskConfig(TaskConfig taskConfig) throws SchedulerException {taskConfig.setId(IdUtil.simpleUUID());int effect = configMapper.insert(taskConfig);createNewScheduler(taskConfig);return effect;}public int updateTaskConfig(TaskConfig taskConfig) throws SchedulerException {deleteJob(configMapper.selectById(taskConfig.getId()));int effect = configMapper.updateById(taskConfig);createNewScheduler(taskConfig);return effect;}private void createNewScheduler(TaskConfig task) throws SchedulerException {log.info("开始执行创建新任务");Scheduler scheduler = schedulerFactoryBean.getScheduler();JobKey jobKey = jobKey(task);JobDetail jobDetail = JobBuilder.newJob(RunnerJob.class).withIdentity(jobKey).build();Trigger trigger = TriggerBuilder.newTrigger().withIdentity(task.getId(), StrUtil.format("{}#{}", task.getExecClass(), task.getExecMethod())).startNow().withSchedule(CronScheduleBuilder.cronSchedule(task.getCron())).build();scheduler.scheduleJob(jobDetail, trigger);scheduler.start();log.info("任务创建完成");}/*** 阐述job** @param task* @throws SchedulerException*/public boolean deleteJob(TaskConfig task) throws SchedulerException {Scheduler scheduler = schedulerFactoryBean.getScheduler();JobKey jobKey = jobKey(task);boolean deleteJob = scheduler.deleteJob(jobKey);log.info("当前 jobKey {} 删除结果{}", jobKey, deleteJob);return deleteJob;}private JobKey jobKey(TaskConfig task) {JobKey jobKey = new JobKey(task.getId(), StrUtil.format("{}#{}", task.getExecClass(), task.getExecMethod()));log.info("当前任务 {}, jobKey{}", task, jobKey);return jobKey;}@Overridepublic String getCron(CronDto dto) {boolean daysEmptyFlag = CollUtil.isEmpty(dto.getChooseDays());boolean dayOfWeeksEmptyFlag = CollUtil.isEmpty(dto.getChooseDayOfWeeks());if (daysEmptyFlag && dayOfWeeksEmptyFlag) {throw new RuntimeException("执行天数和星期必须选择一个");}if (!daysEmptyFlag && !dayOfWeeksEmptyFlag) {throw new RuntimeException("执行天数和星期只能选择一个");}String hours = String.join(",", dto.getChooseHours());String days = CollUtil.isEmpty(dto.getChooseDays()) ? "?" : String.join(",", dto.getChooseDays());String dayOfWeek = CollUtil.isEmpty(dto.getChooseDayOfWeeks()) ? "?" : String.join(",", dto.getChooseDayOfWeeks());String cronStr = String.format("0 0 %s %s * %s", hours, days, dayOfWeek);if (!CronUtil.isValid(cronStr)) {throw new RuntimeException("定时任务表达式不合法");}log.info("当前任务表达式 {}", cronStr);return cronStr;}@Overridepublic List<TaskDescVo> getAllTaskDescList() {List<TaskDescVo> result = new ArrayList<>();List<String> beanNames = new ArrayList<>(Arrays.asList(applicationContext.getBeanDefinitionNames()));beanNames.sort(String::compareTo);TaskDescVo vo = null;for (String beanName : beanNames) {Object bean = applicationContext.getBean(beanName);// 使用 AopUtils 来获取代理对象的原始类, 否则获得的是代理类,无法获取@Service等类上的注解Class<?> beanClass = AopUtils.getTargetClass(bean);if (beanClass.isAnnotationPresent(TaskDesc.class)) {TaskDesc annotation = beanClass.getAnnotation(TaskDesc.class);vo = new TaskDescVo();vo.setMethodName(annotation.methodName());vo.setDesc(annotation.desc());vo.setBeanName(beanName);vo.setClassName(beanClass.getName());vo.setIndex(beanNames.indexOf(beanName));result.add(vo);}}return result;}private CronDto parseCron(String cron) {String[] split = cron.split(" ");// 计算几个小时String cronHours = split[2];// 计算几天String cronDays = split[3];// 计算的周期String cronDayOfWeeks = split[5];CronDto cronDto = new CronDto();cronDto.setChooseHours(Arrays.asList(cronHours.split(",")));cronDto.setChooseDays(Arrays.asList(cronDays.split(",")));cronDto.setChooseDayOfWeeks(Arrays.asList(cronDayOfWeeks.split(",")));return cronDto;}
}

5. CronUtil

import org.quartz.CronExpression;import java.text.ParseException;
import java.util.Date;/*** cron表达式工具类** @author ruoyi*/
public class CronUtil {/*** 返回一个布尔值代表一个给定的Cron表达式的有效性** @param cronExpression Cron表达式* @return boolean 表达式是否有效*/public static boolean isValid(String cronExpression) {return CronExpression.isValidExpression(cronExpression);}public static void main(String[] args) {System.out.println(isValid("0/1 * * * * ?"));}/*** 返回一个字符串值,表示该消息无效Cron表达式给出有效性** @param cronExpression Cron表达式* @return String 无效时返回表达式错误描述,如果有效返回null*/public static String getInvalidMessage(String cronExpression) {try {new CronExpression(cronExpression);return null;} catch (ParseException pe) {return pe.getMessage();}}/*** 返回下一个执行时间根据给定的Cron表达式** @param cronExpression Cron表达式* @return Date 下次Cron表达式执行时间*/public static Date getNextExecution(String cronExpression) {try {CronExpression cron = new CronExpression(cronExpression);return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));} catch (ParseException e) {throw new IllegalArgumentException(e.getMessage());}}
}

6. @TaskDesc注解使用

@TaskDesc注解的类需要使用@Component注解标注,被SpringBoot容器管理到定时任务才能正常执行

import net.lesscoding.task.core.annotations.TaskDesc;
import net.lesscoding.task.dao.EvaluateDsCustomerMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.jexl3.JexlEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** @author eleven* @date 2025/3/12 15:11* @apiNote 客户评分计数器*/
@Component
@Slf4j
@TaskDesc(methodName = "scoreCounter", desc = "客户评分计数器")
public class CustomerScoreCounter extends AbstractScoreCounter {@Autowiredprivate EvaluateDsCustomerMapper dsCustomerMapper;@Autowiredprivate JexlEngine jexlEngine;// 定时任务实际执行的方法@Overridepublic void scoreCounter() {calcScoreAndSave(2, null, "customer_id",dsCustomerMapper.selectList(null));}
}

相关文章:

实现动态增QuartzJob,通过自定义注解调用相应方法

:::tip 动态增加Quartz定时任务&#xff0c;通过自定义注解来实现具体的定时任务方法调用。 ::: 相关依赖如下 <!-- 用来动态创建 Quartz 定时任务 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-start…...

PyTorch可视化工具——使用Visdom进行深度学习可视化

文章目录 前置环境Visdom安装并启动VisdomVisdom图形APIVisdom静态更新API详解通用参数说明使用示例Visdom动态更新API详解1. 使用updateappend参数2. ~~使用vis.updateTrace方法~~3. 完整训练监控示例 Visdom可视化操作散点图plot.scatter()散点图案例线性图vis.line()vis.lin…...

Qt无边框界面添加鼠标事件

在Qt中实现无边框窗口的鼠标事件处理&#xff0c;主要涉及窗口拖动和调整大小功能。以下是分步实现的代码示例&#xff1a; 1. 创建无边框窗口 首先&#xff0c;创建一个继承自QWidget的自定义窗口类&#xff0c;并设置无边框标志&#xff1a; #include <QWidget> #in…...

企业级爬虫进阶开发指南

企业级爬虫进阶开发指南 一、分布式任务调度系统的深度设计 1.1 架构设计原理 图表 1.2 核心代码实现与注释 分布式锁服务 # distributed_lock.py import redis import timeclass DistributedLock:def __init__(self, redis_conn):self.redis = redis_connself.lock_key = …...

Ubuntu ping网络没有问题,但是浏览器无法访问到网络

我这边是尝试清楚DNS缓存然后重新访问就可以了。 使用 resolvectl 刷新 DNS 缓存 在 Ubuntu 20.04 及更高版本中&#xff0c;可以使用以下命令来刷新 DNS 缓存&#xff1a; sudo resolvectl flush-caches 使用 systemd-resolve&#xff08;适用于旧版本&#xff09; 如果你…...

网络安全-等级保护(等保) 2-7 GB/T 25058—2019 《信息安全技术 网络安全等级保护实施指南》-2019-08-30发布【现行】

################################################################################ GB/T 22239-2019 《信息安全技术 网络安全等级保护基础要求》包含安全物理环境、安全通信网络、安全区域边界、安全计算环境、安全管理中心、安全管理制度、安全管理机构、安全管理人员、安…...

数据结构实验10.1:内部排序的基本运算

文章目录 一&#xff0c;实验目的二&#xff0c;实验内容1. 数据生成与初始化2. 排序算法实现&#xff08;1&#xff09;直接插入排序&#xff08;2&#xff09;二分插入排序&#xff08;3&#xff09;希尔排序&#xff08;4&#xff09;冒泡排序&#xff08;5&#xff09;快速…...

C#:多线程

一.线程常用概念 线程&#xff08;Thread&#xff09;&#xff1a;操作系统执行程序的最小单位 进程&#xff08;Process&#xff09;&#xff1a;程序在内存中的运行实例 并发&#xff08;Concurrency&#xff09;&#xff1a;多个任务交替执行&#xff08;单核CPU&#xff0…...

基于Zynq SDK的LWIP UDP组播开发实战指南

一、为什么选择LWIP组播? 在工业控制、智能安防、物联网等领域,一对多的高效数据传输需求日益增长。Zynq-7000系列SoC凭借其ARM+FPGA的独特架构,结合LWIP轻量级网络协议栈,成为嵌入式网络开发的理想选择。本文将带您实现: LWIP组播配置全流程动态组播组切换技术零拷贝数据…...

c#将json字符串转换为对象数组

在C#中&#xff0c;将JSON字符串转换为对象数组是一个常见的需求&#xff0c;特别是在处理来自Web API的响应或需要反序列化本地文件内容时。这可以通过使用Newtonsoft.Json&#xff08;也称为Json.NET&#xff09;库或.NET Core内置的System.Text.Json来完成。以下是如何使用这…...

机器学习在智能水泥基复合材料中的应用与实践

“机器学习在智能水泥基复合材料中的应用与实践” 课程 内容 机器学习基础模型与复合材料研究融合 机器学习在复合材料中的应用概述机器学习用于复合材料研究的流程复合材料数据收集与数据预处理 实例&#xff1a;数据的收集和预处理 复合材料机器学习特征工程与选择 实例&a…...

wps编辑技巧

1、编辑模式 2、图片提取方法&#xff1a;右键保存图片 可以直接右键保存下来看看是否是原始图&#xff0c;如果歪着的图&#xff0c;可能保存下来是正的&#xff0c;直接保存试下 3、加批注...

开放世界RPG:无缝地图与动态任务的拓扑学架构

目录 开放世界RPG:无缝地图与动态任务的拓扑学架构引言第一章 地图分块系统1.1 动态加载算法1.2 内存管理模型第二章 任务拓扑网络2.1 任务依赖图2.2 动态可达性分析第三章 NPC行为系统3.1 行为森林架构3.2 日程规划算法第四章 动态事件系统4.1 事件传播模型4.2 玩家影响指标第…...

【图像处理入门】1. 数字图像的本质:从像素到色彩模型

作为图像处理的开篇&#xff0c;本文将带你拆解数字图像的底层逻辑&#xff1a;从模拟图像到数字信号的神奇转换&#xff0c;到像素世界的微观构成&#xff0c;再到彩色图像的编码奥秘。通过 Python 代码实战&#xff0c;你将亲手触摸图像的 “基因”—— 像素值&#xff0c;并…...

(已解决:基于WSL2技术)Windows11家庭中文版(win11家庭版)如何配置和使用Docker Desktop

目录 问题现象&#xff1a; 问题分析&#xff1a; 拓展&#xff1a; 解决方法&#xff1a; 1、使用WSL2技术&#xff08;亲测有效&#xff09; 注意&#xff1a; 2、开启Hyper-V功能&#xff08;未经亲测&#xff0c;待研究&#xff09; 问题现象&#xff1a; 今天想在本…...

Ubuntu20.04部署KVM

文章目录 一. 环境准备关闭防火墙&#xff08;UFW&#xff09;禁用 SELinux更换镜像源检查 CPU 虚拟化支持 二. 安装KVM安装 KVM 及相关组件启动 libvirtd 服务验证安装创建虚拟机 一. 环境准备 4C8G&#xff0c;50G硬盘——VMware Workstation需要给虚拟机开启虚拟化引擎 roo…...

OpenCV CUDA 模块图像过滤------创建一个高斯滤波器函数createGaussianFilter()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::createGaussianFilter 是 OpenCV CUDA 模块中的一个工厂函数&#xff0c;用于创建一个高斯滤波器。这个滤波器可以用来平滑图像&#…...

计算机视觉与深度学习 | matlab实现ARIMA-WOA-CNN-LSTM时间序列预测(完整源码和数据)

以下是一个基于MATLAB的ARIMA-WOA-CNN-LSTM时间序列预测框架。由于完整代码较长,此处提供核心模块和实现思路,完整源码和数据可通过文末方式获取。 1. 数据准备(示例数据) 使用MATLAB内置的航空乘客数据集: % 加载数据 data = readtable(airline-passengers.csv); data …...

可视化图解算法43:数组中的逆序对

1. 题目 ​牛客网 面试笔试TOP101 描述 在数组中的两个数字&#xff0c;如果前面一个数字大于后面的数字&#xff0c;则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P mod 1000000007 数据范围&…...

【Python】使用Python实现调用API获取图片存储到本地

使用Python实现调用API获取图片存储到本地 目录 使用Python实现调用API获取图片存储到本地1、项目概述2、核心功能3、环境准备4、代码实现5、结果查看 1、项目概述 开发一个自动化工具&#xff0c;用于从JSON数据源中提取图像ID&#xff0c;通过调用指定API获取未经压缩的原始…...

腾讯2025年校招笔试真题手撕(一)

一、题目 有n 把钥匙&#xff0c;m 个锁&#xff0c;每把锁只能由一把特定的钥匙打开&#xff0c;其他钥匙都无法打开。一把钥匙可能可以打开多把锁&#xff0c;钥匙也可以重复使用。 对于任意一把锁来说&#xff0c;打开它的钥匙是哪一把是等概率的。但你无法事先知道是哪一把…...

Vue3 与 Vue2 区别

一、Vue3 与 Vue2 区别 对于生命周期来说&#xff0c;整体上变化不大&#xff0c;只是大部分生命周期钩子名称上 “on”&#xff0c;功能上是类似的。不过有一点需要注意&#xff0c;组合式API的Vue3 中使用生命周期钩子时需要先引入&#xff0c;而 Vue2 在选项API中可以直接…...

java集合详细讲解

Java 8 集合框架详解 Java集合框架是Java中最重要、最常用的API之一&#xff0c;Java 8对其进行了多项增强。下面我将全面讲解Java 8中的集合框架。 一、集合框架概述 Java集合框架主要分为两大类&#xff1a; Collection - 单列集合 List&#xff1a;有序可重复Set&#xf…...

嵌入式学习笔记 - STM32 U(S)ART 模块HAL 库函数总结

一 串口发送方式&#xff1a; ①轮训方式发送&#xff0c;也就是主动发送&#xff0c;这个容易理解&#xff0c;使用如下函数&#xff1a; HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout); ②中断方式发送&#xff…...

【VLNs篇】04:SayNav-为新环境中的动态规划到导航进行大型语言模型的基础构建

栏目内容论文标题SayNav: 为新环境中的动态规划到导航进行大型语言模型的基础构建 (SayNav: Grounding Large Language Models for Dynamic Planning to Navigation in New Environments)研究问题自主代理在未知环境中执行复杂导航任务&#xff08;如MultiON&#xff09;时&…...

MySQL中添加一个具有创建数据库权限的用户

要在MySQL中添加一个具有创建数据库权限的用户&#xff0c;可按以下步骤操作&#xff1a; 1. 登录MySQL 使用拥有足够权限&#xff08;一般是root用户 &#xff09;的账号登录到MySQL数据库。在命令行输入&#xff1a; mysql -u root -p然后输入对应的密码&#xff0c;即可进…...

oracle使用SPM控制执行计划

一 SPM介绍 Oracle在11G中推出了SPM&#xff08;SQL Plan management&#xff09;,SPM是一种主动的稳定执行计划的手段&#xff0c;能够保证只有被验证过的执行计划才会被启用&#xff0c;当由于种种原因&#xff08;比如统计信息的变更&#xff09;而导致目标SQL产生了新的执…...

[Java实战]Spring Boot整合Seata:分布式事务一致性解决方案(三十一)

[Java实战]Spring Boot整合Seata&#xff1a;分布式事务一致性解决方案&#xff08;三十一&#xff09; 引言 在微服务架构中&#xff0c;业务逻辑被拆分为多个独立的服务&#xff0c;每个服务可能拥有独立的数据库。当需要跨服务操作多个数据库时&#xff0c;如何保证数据的…...

Openwrt下使用ffmpeg配合自建RTSP服务器实现推流

目前在Openwrt下时mjpg_streamer实现UVC摄像头转网络摄像头的方案很多&#xff0c;这种方案视频服在路由模组中&#xff0c;在局域网中使用很方便。但是对于需要远程监控管理的情况&#xff0c;mjpg_streamer不适应&#xff0c;因为不在局域网中的播放器无法访问到路由模组中的…...

MySQL 索引的增删改查

MySQL 索引的增删改查 1 建表时创建索引 [UNIQUE|FULLTEXT|SPATIAL] INDEX|KEY [别名] (字段名 [(长度)] [ASC|DESC] )主键直接写&#xff1a; PRIMARY KEY (Id)例如&#xff1a; CREATE TABLE people (id int NOT NULL PRIMARY KEY AUTO_INCREMENT,last_name varchar(10)…...