Spring Boot 自定义定时任务组件深度解析:Quartz 集成与设计模式实战
一、组件设计目标
解决痛点:
- 简化 Quartz 原生 API 的复杂性
- 统一任务调度管理(增删改查、日志、重试)
- 与 Spring Boot 生态无缝整合
二、实现步骤详解
1. 组件初始化配置
1.1 初始化 Quartz 表结构
下载 SQL 脚本
🔗 官方表结构下载地址:Quartz
根据 Quartz 版本选择对应 SQL 文件
1.2 引入pom依赖
<!-- pom.xml -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
1.3 引入yaml配置
# application.yml
# Quartz 配置项,对应 QuartzProperties 配置类
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/your_db?useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverautoconfigure:exclude:#排除Quartz自动配置即关闭定时任务,注释掉该配置即打开定时任务- org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 不开启 Quartz 的自动配置quartz:auto-startup: true # 本地开发环境,尽量不要开启 Jobscheduler-name: schedulerName # Scheduler 名字。默认为 schedulerNamejob-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 trueproperties: # 添加 Quartz Scheduler 附加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档org:quartz:# Scheduler 相关配置scheduler:instanceName: schedulerNameinstanceId: AUTO # 自动生成 instance ID# JobStore 相关配置jobStore:# JobStore 实现类。可见博客:https://blog.csdn.net/weixin_42458219/article/details/122247162class: org.springframework.scheduling.quartz.LocalDataSourceJobStoreisClustered: true # 是集群模式clusterCheckinInterval: 15000 # 集群检查频率,单位:毫秒。默认为 15000,即 15 秒misfireThreshold: 60000 # misfire 阀值,单位:毫秒。# 线程池相关配置threadPool:threadCount: 25 # 线程池大小。默认为 10 。threadPriority: 5 # 线程优先级class: org.quartz.simpl.SimpleThreadPool # 线程池类型jdbc: # 使用 JDBC 的 JobStore 的时候,JDBC 的配置initialize-schema: NEVER # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。
1.4 Spring Boot 2.7 与 3.0 自动配置声明对比
1. 文件路径与作用
| 文件类型 | 路径 | 用途说明 |
|---|---|---|
| AutoConfiguration.imports | META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports | 声明自动配置类(Spring Boot 2.7+ 新增) |
| spring.factories | META-INF/spring.factories | 旧版声明方式(Spring Boot 2.7+ 兼容,直到3.0 废弃spring.factories 中自动配置类声明。) |
2. 版本差异详解
Spring Boot 2.7
- 兼容模式: 同时支持两种声明方式,优先级:
AutoConfiguration.imports > spring.factories - 推荐做法: 使用新的 AutoConfiguration.imports 文件
Spring Boot 3.0
- 强制要求: 自动配置类 必须 通过 AutoConfiguration.imports 声明
- 废弃内容: spring.factories 中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 条目失效
- 保留内容: 其他非自动配置条目(如监听器、模板引擎)仍可保留在 spring.factories
3. 文件内容对比
Spring Boot 2.7+ 配置
在 resources/META-INF 目录下创建文件:
文件路径:
src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件内容:
# 声明自定义的自动配置类
cn.iocoder.dyh.framework.quartz.config.DyhQuartzAutoConfiguration
cn.iocoder.dyh.framework.quartz.config.DyhAsyncAutoConfiguration
Spring Boot 2.6 及以下配置
在 resources/META-INF 目录下创建文件:
文件路径:
src/main/resources/META-INF/spring.factories
文件内容:
# 声明自定义的自动配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\cn.iocoder.dyh.framework.quartz.config.DyhQuartzAutoConfiguration,\cn.iocoder.dyh.framework.quartz.config.DyhAsyncAutoConfiguration
1.5 自定义自动配置类
DyhAsyncAutoConfiguration
/*** 异步任务 Configuration*/
@AutoConfiguration// 1️⃣ Spring Boot自动配置注解,标识这是一个自动配置类
@EnableAsync// 2️⃣ 启用Spring的异步方法执行功能
public class DyhAsyncAutoConfiguration {@Bean// 3️⃣ 声明该方法返回的对象会被注册为Spring Beanpublic BeanPostProcessor threadPoolTaskExecutorBeanPostProcessor() {return new BeanPostProcessor() { // 4️⃣ 创建BeanPostProcessor实现(Bean生命周期处理器)@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {if (!(bean instanceof ThreadPoolTaskExecutor)) { // 5️⃣ 仅处理ThreadPoolTaskExecutor类型的Beanreturn bean;}// 修改提交的任务,接入 TransmittableThreadLocalThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) bean; // 6️⃣ 类型转换executor.setTaskDecorator(TtlRunnable::get); // 7️⃣ 设置任务装饰器(用于TTL线程池上下文传递)return executor;}};}
}
DyhQuartzAutoConfiguration
@AutoConfiguration // 1️⃣ Spring Boot自动配置注解,标识这是一个自动配置类
@EnableScheduling // 2️⃣ 启用Spring自带的定时任务功能(@Scheduled注解生效)
@Slf4j // 3️⃣ Lombok注解,自动生成日志对象 log
public class DyhQuartzAutoConfiguration {@Bean // 4️⃣ 声明该方法返回的对象会被注册为Spring Beanpublic SchedulerManager schedulerManager(Optional<Scheduler> scheduler) { // 5️⃣ 使用Optional包装的Quartz调度器if (!scheduler.isPresent()) { // 6️⃣ 检测是否没有Scheduler实例log.info("[定时任务 - 已禁用]");return new SchedulerManager(null); // 7️⃣ 创建禁用状态的调度管理器}return new SchedulerManager(scheduler.get()); // 8️⃣ 创建正常工作的调度管理器}
}
1.6 相关类
JobDataKeyEnum
/*** Quartz Job Data 的 key 枚举*/
public enum JobDataKeyEnum {JOB_ID,JOB_HANDLER_NAME,JOB_HANDLER_PARAM,JOB_RETRY_COUNT, // 最大重试次数JOB_RETRY_INTERVAL, // 每次重试间隔
}
JobLogFrameworkService
/*** Job 日志 Framework Service 接口** @author dyh*/
public interface JobLogFrameworkService {/*** 创建 Job 日志** @param jobId 任务编号* @param beginTime 开始时间* @param jobHandlerName Job 处理器的名字* @param jobHandlerParam Job 处理器的参数* @param executeIndex 第几次执行* @return Job 日志的编号*/Long createJobLog(@NotNull(message = "任务编号不能为空") Long jobId,@NotNull(message = "开始时间") LocalDateTime beginTime,@NotEmpty(message = "Job 处理器的名字不能为空") String jobHandlerName,String jobHandlerParam,@NotNull(message = "第几次执行不能为空") Integer executeIndex);/*** 更新 Job 日志的执行结果** @param logId 日志编号* @param endTime 结束时间。因为是异步,避免记录时间不准去* @param duration 运行时长,单位:毫秒* @param success 是否成功* @param result 成功数据*/void updateJobLogResultAsync(@NotNull(message = "日志编号不能为空") Long logId,@NotNull(message = "结束时间不能为空") LocalDateTime endTime,@NotNull(message = "运行时长不能为空") Integer duration,boolean success, String result);
}
CronUtils
/*** Quartz Cron 表达式的工具类** @author dyh*/
public class CronUtils {/*** 校验 CRON 表达式是否有效** @param cronExpression CRON 表达式* @return 是否有效*/public static boolean isValid(String cronExpression) {return CronExpression.isValidExpression(cronExpression);}/*** 基于 CRON 表达式,获得下 n 个满足执行的时间** @param cronExpression CRON 表达式* @param n 数量* @return 满足条件的执行时间*/public static List<LocalDateTime> getNextTimes(String cronExpression, int n) {// 1. 获得 CronExpression 对象CronExpression cron;try {cron = new CronExpression(cronExpression);} catch (ParseException e) {throw new IllegalArgumentException(e.getMessage());}// 2. 从当前开始计算,n 个满足条件的Date now = new Date();List<LocalDateTime> nextTimes = new ArrayList<>(n);for (int i = 0; i < n; i++) {Date nextTime = cron.getNextValidTimeAfter(now);// 2.1 如果 nextTime 为 null,说明没有更多的有效时间,退出循环if (nextTime == null) {break;}nextTimes.add(LocalDateTimeUtil.of(nextTime));// 2.2 切换现在,为下一个触发时间;now = nextTime;}return nextTimes;}}
2. 核心模块实现
2.1 任务调度管理(SchedulerManager)
/*** {@link Scheduler} 的管理器,负责创建任务** 考虑到实现的简洁性,我们使用 jobHandlerName 作为唯一标识,即:* 1. Job 的 {@link JobDetail#getKey()}* 2. Trigger 的 {@link Trigger#getKey()}** 另外,jobHandlerName 对应到 Spring Bean 的名字,直接调用** @author dyh*/
public class SchedulerManager {// Quartz调度器核心对象private final Scheduler scheduler;// 通过依赖注入获取Quartz调度器实例public SchedulerManager(Scheduler scheduler) {this.scheduler = scheduler;}/*** 添加 Job 到 Quartz 中** @param jobId 任务编号* @param jobHandlerName 任务处理器的名字* @param jobHandlerParam 任务处理器的参数* @param cronExpression CRON 表达式* @param retryCount 重试次数* @param retryInterval 重试间隔* @throws SchedulerException 添加异常*/public void addJob(Long jobId, String jobHandlerName, String jobHandlerParam, String cronExpression,Integer retryCount, Integer retryInterval)throws SchedulerException {validateScheduler();// 创建 JobDetail 对象JobDetail jobDetail = JobBuilder.newJob(JobHandlerInvoker.class).usingJobData(JobDataKeyEnum.JOB_ID.name(), jobId).usingJobData(JobDataKeyEnum.JOB_HANDLER_NAME.name(), jobHandlerName).withIdentity(jobHandlerName).build();// 创建 Trigger 对象Trigger trigger = this.buildTrigger(jobHandlerName, jobHandlerParam, cronExpression, retryCount, retryInterval);// 新增 Job 调度scheduler.scheduleJob(jobDetail, trigger);}/*** 更新 Job 到 Quartz** @param jobHandlerName 任务处理器的名字* @param jobHandlerParam 任务处理器的参数* @param cronExpression CRON 表达式* @param retryCount 重试次数* @param retryInterval 重试间隔* @throws SchedulerException 更新异常*/public void updateJob(String jobHandlerName, String jobHandlerParam, String cronExpression,Integer retryCount, Integer retryInterval)throws SchedulerException {validateScheduler();// 创建新 Trigger 对象Trigger newTrigger = this.buildTrigger(jobHandlerName, jobHandlerParam, cronExpression, retryCount, retryInterval);// 修改调度scheduler.rescheduleJob(new TriggerKey(jobHandlerName), newTrigger);}/*** 删除 Quartz 中的 Job** @param jobHandlerName 任务处理器的名字* @throws SchedulerException 删除异常*/public void deleteJob(String jobHandlerName) throws SchedulerException {validateScheduler();// 暂停 Trigger 对象scheduler.pauseTrigger(new TriggerKey(jobHandlerName));// 取消并删除 Job 调度scheduler.unscheduleJob(new TriggerKey(jobHandlerName));scheduler.deleteJob(new JobKey(jobHandlerName));}/*** 暂停 Quartz 中的 Job** @param jobHandlerName 任务处理器的名字* @throws SchedulerException 暂停异常*/public void pauseJob(String jobHandlerName) throws SchedulerException {validateScheduler();scheduler.pauseJob(new JobKey(jobHandlerName));}/*** 启动 Quartz 中的 Job** @param jobHandlerName 任务处理器的名字* @throws SchedulerException 启动异常*/public void resumeJob(String jobHandlerName) throws SchedulerException {validateScheduler();scheduler.resumeJob(new JobKey(jobHandlerName));scheduler.resumeTrigger(new TriggerKey(jobHandlerName));}/*** 立即触发一次 Quartz 中的 Job** @param jobId 任务编号* @param jobHandlerName 任务处理器的名字* @param jobHandlerParam 任务处理器的参数* @throws SchedulerException 触发异常*/public void triggerJob(Long jobId, String jobHandlerName, String jobHandlerParam)throws SchedulerException {validateScheduler();// 触发任务JobDataMap data = new JobDataMap(); // 无需重试,所以不设置 retryCount 和 retryIntervaldata.put(JobDataKeyEnum.JOB_ID.name(), jobId);data.put(JobDataKeyEnum.JOB_HANDLER_NAME.name(), jobHandlerName);data.put(JobDataKeyEnum.JOB_HANDLER_PARAM.name(), jobHandlerParam);scheduler.triggerJob(new JobKey(jobHandlerName), data);}private Trigger buildTrigger(String jobHandlerName, String jobHandlerParam, String cronExpression,Integer retryCount, Integer retryInterval) {return TriggerBuilder.newTrigger().withIdentity(jobHandlerName).withSchedule(CronScheduleBuilder.cronSchedule(cronExpression)).usingJobData(JobDataKeyEnum.JOB_HANDLER_PARAM.name(), jobHandlerParam).usingJobData(JobDataKeyEnum.JOB_RETRY_COUNT.name(), retryCount).usingJobData(JobDataKeyEnum.JOB_RETRY_INTERVAL.name(), retryInterval).build();}private void validateScheduler() {if (scheduler == null) {throw exception0(NOT_IMPLEMENTED.getCode(),"[定时任务 - 已禁用]");}}}
2.2 任务执行器(JobHandlerInvoker)
/*** 基础 Job 调用者,负责调用 {@link JobHandler#execute(String)} 执行任务** @author dyh*/
@DisallowConcurrentExecution // 禁止同一JobDetail并发执行
@PersistJobDataAfterExecution // 执行后持久化JobDataMap数据
@Slf4j
public class JobHandlerInvoker extends QuartzJobBean {@Resourceprivate ApplicationContext applicationContext;@Resourceprivate JobLogFrameworkService jobLogFrameworkService;@Overrideprotected void executeInternal(JobExecutionContext executionContext) throws JobExecutionException {// 第一步,获得 Job 数据Long jobId = executionContext.getMergedJobDataMap().getLong(JobDataKeyEnum.JOB_ID.name());String jobHandlerName = executionContext.getMergedJobDataMap().getString(JobDataKeyEnum.JOB_HANDLER_NAME.name());String jobHandlerParam = executionContext.getMergedJobDataMap().getString(JobDataKeyEnum.JOB_HANDLER_PARAM.name());int refireCount = executionContext.getRefireCount();int retryCount = (Integer) executionContext.getMergedJobDataMap().getOrDefault(JobDataKeyEnum.JOB_RETRY_COUNT.name(), 0);int retryInterval = (Integer) executionContext.getMergedJobDataMap().getOrDefault(JobDataKeyEnum.JOB_RETRY_INTERVAL.name(), 0);// 第二步,执行任务Long jobLogId = null;LocalDateTime startTime = LocalDateTime.now();String data = null;Throwable exception = null;try {// 记录 Job 日志(初始)jobLogId = jobLogFrameworkService.createJobLog(jobId, startTime, jobHandlerName, jobHandlerParam, refireCount + 1);// 执行任务data = this.executeInternal(jobHandlerName, jobHandlerParam);} catch (Throwable ex) {exception = ex;}// 第三步,记录执行日志this.updateJobLogResultAsync(jobLogId, startTime, data, exception, executionContext);// 第四步,处理有异常的情况handleException(exception, refireCount, retryCount, retryInterval);}private String executeInternal(String jobHandlerName, String jobHandlerParam) throws Exception {// 获得 JobHandler 对象JobHandler jobHandler = applicationContext.getBean(jobHandlerName, JobHandler.class);Assert.notNull(jobHandler, "JobHandler 不会为空");// 执行任务return jobHandler.execute(jobHandlerParam);}private void updateJobLogResultAsync(Long jobLogId, LocalDateTime startTime, String data, Throwable exception,JobExecutionContext executionContext) {LocalDateTime endTime = LocalDateTime.now();// 处理是否成功boolean success = exception == null;if (!success) {data = getRootCauseMessage(exception);}// 更新日志try {jobLogFrameworkService.updateJobLogResultAsync(jobLogId, endTime, (int) LocalDateTimeUtil.between(startTime, endTime).toMillis(), success, data);} catch (Exception ex) {log.error("[executeInternal][Job({}) logId({}) 记录执行日志失败({}/{})]",executionContext.getJobDetail().getKey(), jobLogId, success, data);}}private void handleException(Throwable exception,int refireCount, int retryCount, int retryInterval) throws JobExecutionException {// 如果有异常,则进行重试if (exception == null) {return;}// 情况一:如果到达重试上限,则直接抛出异常即可if (refireCount >= retryCount) {throw new JobExecutionException(exception);}// 情况二:如果未到达重试上限,则 sleep 一定间隔时间,然后重试// 这里使用 sleep 来实现,主要还是希望实现比较简单。因为,同一时间,不会存在大量失败的 Job。if (retryInterval > 0) {ThreadUtil.sleep(retryInterval);}// 第二个参数,refireImmediately = true,表示立即重试throw new JobExecutionException(exception, true);}}
2.3 任务处理器(JobHandler)
/*** 任务处理器** @author dyh*/
public interface JobHandler {/*** 执行任务** @param param 参数* @return 结果* @throws Exception 异常*/String execute(String param) throws Exception;}
三、核心设计模式解析
1. 策略模式(Strategy Pattern)
应用场景:不同业务任务的具体实现
public interface JobHandler {String execute(String param) throws Exception;
}@Component
public class DemoJob implements JobHandler {@Overridepublic String execute(String param) {// 具体业务逻辑}
}
2. 模板方法模式(Template Method)
应用场景:任务执行流程标准化
此代码为org.springframework.scheduling.quartz包下源码
public abstract class QuartzJobBean implements Job {public QuartzJobBean() {}//不可变的流程用final修饰生成流程骨架public final void execute(JobExecutionContext context) throws JobExecutionException {try {BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);MutablePropertyValues pvs = new MutablePropertyValues();pvs.addPropertyValues(context.getScheduler().getContext());pvs.addPropertyValues(context.getMergedJobDataMap());bw.setPropertyValues(pvs, true);} catch (SchedulerException var4) {SchedulerException ex = var4;throw new JobExecutionException(ex);}this.executeInternal(context);}//可变部分抽象出来由具体子类实现功能protected abstract void executeInternal(JobExecutionContext context) throws JobExecutionException;
}
3. 工厂方法模式(Factory Method)
间接应用:通过 Spring 容器获取 JobHandler 实例
// 获得 JobHandler 对象
JobHandler jobHandler = applicationContext.getBean(jobHandlerName, JobHandler.class);
4. 装饰器模式(Decorator)
应用场景:TTL 异步任务上下文传递
executor.setTaskDecorator(TtlRunnable::get); // 增强 Runnable
四、组件运行流程
1. 组件运行流程图
┌───────────────────────┐ ┌───────────────────────┐
│ Spring Boot │ │ Quartz Scheduler │
│ Application │ │ (Cluster Mode) │
└───────────┬───────────┘ └───────────┬───────────┘│ ││ 1. 自动配置触发 │├─────────────────────────────────▶│ - @AutoConfiguration ││ - AutoConfiguration.imports ││ ││ 2. 初始化 SchedulerManager ││ (依赖注入 Scheduler) ││ ││ 3. 业务系统调用 ││ SchedulerManager.addJob() ││ ││ 4. 创建 JobDetail & Trigger ││ (存储到 JDBC JobStore) ││ │
┌───────────▼───────────┐ ┌───────────▼───────────┐
│ Quartz JobStore │ │ ThreadPoolTaskExecutor
│ (Database) │ │ (异步日志记录) │
└───────────┬───────────┘ └───────────▲───────────┘│ ││ 5. 调度触发 ││ (Cron 表达式) ││ ││ 6. 执行 JobHandlerInvoker ││ (extends QuartzJobBean) ││ │
┌───────────▼───────────┐ │
│ JobHandler 策略模式 │ │
│ (具体业务实现类) │ │
│ - DemoJob.execute() │ │
│ - OrderTimeoutJob │ │
└───────────┬───────────┘ ││ ││ 7. 执行结果/异常 │├─────────────────────────────────▶│ JobLogFrameworkService ││ .updateJobLogResultAsync() ││ ││ 8. 异常重试机制 ││ (handleException 方法) ││ ││ │
└───────────────────────┘ └───────────────────────┘
2. 流程关键步骤说明
- 自动配置阶段
- Spring Boot 通过 AutoConfiguration.imports 加载 DyhQuartzAutoConfiguration
- 初始化 SchedulerManager 并注入 Quartz Scheduler
- 任务注册阶段
- 业务代码调用 SchedulerManager.addJob() 方法
- 创建 JobDetail(绑定 JobHandlerInvoker)和 Trigger
- 任务配置持久化到数据库(JDBC JobStore)
- 调度触发阶段
- Quartz Scheduler 根据 Cron 表达式触发任务
- 集群模式下通过数据库锁实现任务分片
- 任务执行阶段
- JobHandlerInvoker 执行模板方法:
- a. 获取任务参数(JobDataMap)
- b. 记录初始日志(JobLogFrameworkService.createJobLog())
- c. 策略模式 调用具体 JobHandler.execute()
- d. 异步更新日志结果(updateJobLogResultAsync())
- e. 异常重试机制(handleException)
- JobHandlerInvoker 执行模板方法:
- 异步日志处理
- 使用 ThreadPoolTaskExecutor 异步记录日志
- 通过 TtlRunnable 实现线程池上下文传递(TransmittableThreadLocal)
3. 设计模式标注
- 🎯 策略模式:JobHandler 接口统一任务执行入口
- 📜 模板方法:QuartzJobBean 定义任务执行骨架
- 🏭 工厂模式:Spring 容器管理 JobHandler 实例
- 🎨 装饰器模式:TtlRunnable 增强线程池任务
五、完整使用示例
1. 定义具体策略的任务处理器
@Component
@Slf4j
public class OrderTimeoutJob implements JobHandler {@Overridepublic String execute(String orderId) {log.info("[订单超时检查] 订单ID: {}", orderId);// 业务逻辑:关闭超时订单return "已关闭订单: " + orderId;}
}
2. 注册定时任务
说明:注册定时任务可以自建业务类对接该自定义定时任务组件持久化到数据库表,这样就可以像xxl-job一样界面上操作定时任务了,在这里写就篇幅太长了,有空我写了把链接贴过来。
@Autowired
private SchedulerManager schedulerManager;// 添加任务
public void addOrderTimeoutJob(Long jobId, String orderId) throws SchedulerException {schedulerManager.addJob(jobId, "orderTimeoutJob", // Bean 名称orderId, // 参数"0 0 0 * * ?", // CRON3, // 最大重试次数5000 // 重试间隔(毫秒));
}
六、组件优势总结
- 开箱即用:通过 Starter 自动配置,快速集成
- 灵活扩展:基于策略模式轻松添加新任务类型
- 企业级特性:
- 分布式调度(基于 Quartz JDBC JobStore)
- 任务日志追踪
- 失败重试机制
- 生产就绪:完善的异常处理与防御性编程
相关文章:
Spring Boot 自定义定时任务组件深度解析:Quartz 集成与设计模式实战
一、组件设计目标 解决痛点: 简化 Quartz 原生 API 的复杂性统一任务调度管理(增删改查、日志、重试)与 Spring Boot 生态无缝整合 二、实现步骤详解 1. 组件初始化配置 1.1 初始化 Quartz 表结构 下载 SQL 脚本 🔗 官方表…...
嵌入式exfat-nofuse文件系统移植和使用
exfat-nofuse 是一款专为linux ARM平台设计的开源项目,它提供了一个非FUSE机制的内核级驱动,用于在Linux系统上无缝地读写exFAT和VFAT文件系统。此项目由Dorimanx维护,采用C语言编写,兼容GPL-2.0许可证。它避开了FUSE(用户空间文件系统)的使用…...
再来一篇,Linux中的软件管理
Linux中软件包的类型 在Linux系统中,软件包有多种不同的格式和类型,主要包括以下几种: DEB (Debian软件包)(此软件包不适用于RHEL8 系统): 适用于 Debian 及其衍生版本(如Ubuntu等&…...
【重学Android】02.Java环境配置的一些分享
背景说明 其实只是学习Android的话,只要下载好Android Studio开发工具,是自带JDK环境的,所以不需要再额外去进行配置,我之所以还要进行单独配置,是因为我其他的工具需要Java的环境,而且我目前用的是JDK 12…...
SimBody安装
SimBody安装 Simbody 是一个用于创建生物力学和机械系统仿真的多体动力学库。 SimBody安装 Windows安装: 下载地址:GitHub - simbody/simbody: High-performance C multibody dynamics/physics library for simulating articulated biomechanical and…...
CUDA编程中影响性能的小细节总结
一、内存访问优化 合并内存访问:确保相邻线程访问连续内存地址(全局内存对齐访问)。优先使用共享内存(Shared Memory)减少全局内存访问。避免共享内存的Bank Conflict(例如,使用padding或调整访…...
thinkphp:部署完整项目到本地phpstudy
一、准备工作 首先准备一个thinkphp的项目文件;准备mysql数据库 二、小皮初步搭建 1、建立网站 在小皮界面,网站->创建网站->输入域名,选择PHP版本等 注:确保端口未被占用 2、将项目文件放入根目录 网站->管理->…...
Linux网络编程——基于ET模式下的Reactor
一、前言 上篇文章中我们已经讲解了多路转接剩下的两个接口:poll和epoll,并且知道了epoll的两种工作模式分别是 LT模式和ET模式,下来我们就实现的是一个简洁版的 Reactor,即半同步半异步I/O,在linux网络中,…...
大模型相关面试问题原理及举例
大模型相关面试问题原理及举例 目录 大模型相关面试问题原理及举例Transformer相关面试问题原理及举例大模型模型结构相关面试问题原理及举例注意力机制相关面试问题原理及举例大模型与传统模型区别 原理:大模型靠海量参数和复杂结构,能学习更复杂模式。传统模型参数少、结构…...
Redis List 的详细介绍
Redis List 的详细介绍 以下是 Redis List 的详细介绍,从基础命令、内部编码和使用场景三个维度展开: 一、基础命令 Redis List 支持双向操作(头尾插入/删除),适用于队列、栈等场景,以下是核心命令分类&a…...
使用virtualbox的HostOnly建立共享网络-实现虚拟机上网
目录 环境描述解决方案具体步骤1.新建一个virtual host-only ethernet adapter2.设置windows的wifi信号网络共享3.确认winows宿主网络信息3.1.wifi适配器的信息3.2.虚拟网卡的信息3.3.确认virtualbox中虚拟网卡的ip地址 4.虚拟机网卡设置5.虚拟机网络设置5.1.本地连接设置5.2.u…...
springboot+vue3+mysql+websocket实现的即时通讯软件
项目演示 即时通讯软件项目演示 业务架构 技术栈 后端 选用编程语言 Javaweb框架SpringBootdb MySQL 持久存储nosql 缓存 Redis全双工通信框架 WebSocket 前端 前端框架Vue3TypescriptUI样式 Css、ElementPlus网页路由 vue-router全双工通信框架Websocket 功能完成情况 已实…...
基于 Spring Boot 瑞吉外卖系统开发(五)
基于 Spring Boot 瑞吉外卖系统开发(五) 删除分类 分类列表中每条分类信息右侧提供了一个“删除”按钮,当需要将已经存在的分类信息删除时,可以通过单击“删除”按钮实现。 请求路径为/category,携带参数id…...
AES (高级加密标准)
原理详解 AES是一种对称加密算法,使用相同的密钥进行加密和解密。它采用替代-置换网络(SPN)结构,主要步骤包括: 密钥扩展:从初始密钥派生多轮密钥 初始轮:AddRoundKey(轮密钥加) 主轮ÿ…...
详解Windows(一)——系统盘下目录及文件详解
引言 你是否曾经好奇过电脑里那些神秘的文件夹都是干什么用的?为什么有些文件是.exe而有些是.dll?不同的图片格式.jpg和.png到底有什么区别?如果你对这些问题感到困惑,这篇文章就是为你准备的。今天,我们将以通俗易懂…...
基于Spring MVC的客户端真实IP获取方案解析
文章目录 基于Spring MVC的客户端真实IP获取方案解析概述核心方法解析代码实现工作流程 IP获取优先级策略IP有效性验证异常处理与日志使用场景注意事项扩展建议 基于Spring MVC的客户端真实IP获取方案解析 概述 在Web应用开发中,准确获取客户端真实IP地址是常见的…...
【Web部署问题】在Tomcat中部署web项目出现http状态-404 -未找到详细解决方案
部署完tomcat记得在选中要运行的工件。 如果没有工件,或者工件有缺失东西,去这里配置工件,...
Linux——Shell编程之正则表达式与文本处理器(笔记)
目录 基础正则表达式 1:基础正则表达式示例 (4)查找任意一个字符“.”与重新字符“*” (5)查找连续字符范围“{ }” 文本处理器 一、sed工具 二、awk工具 (1)按行输出文本 (2࿰…...
Linux 进程控制(自用)
非阻塞调用waitpid 这样父进程就不会阻塞,此时循环使用我们可以让父进程执行其他任务而不是阻塞等待 进程程序替换 进程PCB加载到内存中的代码和数据 替换就是完全替换当前进程的代码段、数据段、堆和栈,保存当前的PCB 代码指的是二进制代码不是源码&a…...
05-DevOps-Jenkins自动拉取构建代码
新建Gitlab仓库 先在Gitab上创建一个代码仓库,选择创建空白项目 安装说明进行填写,然后点击创建项目 创建好的仓库是空的,什么都没有 新建一个springboot项目,用于代码上传使用。 只是为了测试代码上传功能,所以代码…...
【Spring学习】
Spring学习 简介 Spring 是一个开源的 Java 企业级开发框架,最核心的特点是: IOC(控制反转)AOP(面向切面编程) 它有完整的生态: 🚀 Spring Boot:用于快速构建服务&a…...
网络基础与 HTTP 协议
一、网络基础 (一)TCP/IP 协议族 TCP/IP 协议族是互联网通信的核心协议,它包含了多个层次的协议,共同协作实现网络通信。 1. IP 协议 IP(Internet Protocol)协议位于网络层,主要负责将数据包…...
SRS transcode支持 h264_nvenc 硬件解码方案
文章目录 SRS transcode支持 h264_nvenc 硬件解码方案1、修改文件2、重新编译3、使用 SRS transcode支持 h264_nvenc 硬件解码方案 SRS 是开源的流媒体服务,但在使用 GPU 服务器时,想要通过硬件加速,目前官方是不支持的,所以简单…...
阿里云服务器搭建开源版禅道
一,下载地址:禅道11.5版本发布,主要完善细节,修复bug,新增动态过滤机制 - 禅道下载 - 禅道项目管理软件 下载地址二: 禅道21.6.stable 实现旧编辑器撰写的文档无感升级至新版编辑器 - 禅道下载 - 禅道项目…...
【刷题Day21】TCP(浅)
说说 TCP 的四次挥手? TCP的四次挥手事用于安全关闭一个已建立的连接的过程,它确保双方都能完成数据传输并安全地释放连接资源。 简述步骤: 第一次挥手(FIN --> ACK):客户端主动关闭连接,…...
怎么用面向对象和状态机架构,设计一个通用的按键检测功能?
说起按键检测,在座的各位,哪个没被它折磨过? 我刚入门时,为了实现一个简单的按键功能,硬生生写了几十行代码,各种 if...else 嵌套,逻辑绕得我自己都头晕。 更可气的是,辛辛苦苦写完…...
Java基础系列-LinkedList源码解析
文章目录 简介LinkedList 插入和删除元素的时间复杂度?LinkedList 为什么不能实现 RandomAccess 接口? LinkedList 源码分析Node 定义初始化获取元素插入元素删除元素遍历链表 简介 LinkedList 是一个基于双向链表实现的集合类,经常被拿来和…...
day47—双指针-平方数之和(LeetCode-633)
题目描述 给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a^2 b^2 c 。 示例 1: 输入:c 5 输出:true 解释:1 * 1 2 * 2 5示例 2: 输入:c 3 输出:f…...
qwen 14B模型配置文件,层名称weight_map. 28GB
qwen 14B模型配置文件,层名称weight_map. 28GB 目录 qwen 14B模型配置文件,层名称weight_map. 28GBmetadata(元数据)weight_map(权重映射)lm_head.weightmodel.layersmlp.{proj_type}.weightpost_attention_layernormself_attn.{proj_type}.{bias_or_weight}model.norm.w…...
LVDS系列8:Xilinx 7系可编程输入延迟(一)
在解析LVDS信号时,十分重要的一环就是LVDS输入信号线在经过PCB输入到FPGA中后,本来该严格对齐的信号线会出现时延,所以需要在FPGA内部对其进行延时对齐后再进行解析。 Xilinx 7系器件中用于输入信号延时的组件为IDELAYE2可编程原语࿰…...
