Spring Boot 自定义定时任务组件深度解析:Quartz 集成与设计模式实战

news2025/4/22 2:02:33

一、组件设计目标

解决痛点:

  • 简化 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=false
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  autoconfigure:
    exclude:
    	#排除Quartz自动配置即关闭定时任务,注释掉该配置即打开定时任务
      - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # 不开启 Quartz 的自动配置
  quartz:
    auto-startup: true # 本地开发环境,尽量不要开启 Job
    scheduler-name: schedulerName # Scheduler 名字。默认为 schedulerName
    job-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。
    wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 true
    properties: # 添加 Quartz Scheduler 附加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档
      org:
        quartz:
          # Scheduler 相关配置
          scheduler:
            instanceName: schedulerName
            instanceId: AUTO # 自动生成 instance ID
          # JobStore 相关配置
          jobStore:
            # JobStore 实现类。可见博客:https://blog.csdn.net/weixin_42458219/article/details/122247162
            class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
            isClustered: 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.importsMETA-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports声明自动配置类(Spring Boot 2.7+ 新增)
spring.factoriesMETA-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 Bean
    public BeanPostProcessor threadPoolTaskExecutorBeanPostProcessor() {
        return new BeanPostProcessor() { // 4️⃣ 创建BeanPostProcessor实现(Bean生命周期处理器)
            @Override
            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                if (!(bean instanceof ThreadPoolTaskExecutor)) {  // 5️⃣ 仅处理ThreadPoolTaskExecutor类型的Bean
                    return bean;
                }
                // 修改提交的任务,接入 TransmittableThreadLocal
                ThreadPoolTaskExecutor 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 Bean
    public 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 和 retryInterval
        data.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 {

    @Resource
    private ApplicationContext applicationContext;

    @Resource
    private JobLogFrameworkService jobLogFrameworkService;

    @Override
    protected 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 {
    @Override
    public 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. 流程关键步骤说明

  1. 自动配置阶段
    • Spring Boot 通过 AutoConfiguration.imports 加载 DyhQuartzAutoConfiguration
    • 初始化 SchedulerManager 并注入 Quartz Scheduler
  2. 任务注册阶段
    • 业务代码调用 SchedulerManager.addJob() 方法
    • 创建 JobDetail(绑定 JobHandlerInvoker)和 Trigger
    • 任务配置持久化到数据库(JDBC JobStore)
  3. 调度触发阶段
    • Quartz Scheduler 根据 Cron 表达式触发任务
    • 集群模式下通过数据库锁实现任务分片
  4. 任务执行阶段
    • JobHandlerInvoker 执行模板方法:
      • a. 获取任务参数(JobDataMap)
      • b. 记录初始日志(JobLogFrameworkService.createJobLog())
      • c. 策略模式 调用具体 JobHandler.execute()
      • d. 异步更新日志结果(updateJobLogResultAsync())
      • e. 异常重试机制(handleException)
  5. 异步日志处理
    • 使用 ThreadPoolTaskExecutor 异步记录日志
    • 通过 TtlRunnable 实现线程池上下文传递(TransmittableThreadLocal)

3. 设计模式标注

  • 🎯 策略模式:JobHandler 接口统一任务执行入口
  • 📜 模板方法:QuartzJobBean 定义任务执行骨架
  • 🏭 工厂模式:Spring 容器管理 JobHandler 实例
  • 🎨 装饰器模式:TtlRunnable 增强线程池任务

五、完整使用示例

1. 定义具体策略的任务处理器

@Component
@Slf4j
public class OrderTimeoutJob implements JobHandler {

    @Override
    public 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 * * ?",     // CRON
        3,                 // 最大重试次数
        5000               // 重试间隔(毫秒)
    );
}

六、组件优势总结

  1. 开箱即用:通过 Starter 自动配置,快速集成
  2. 灵活扩展:基于策略模式轻松添加新任务类型
  3. 企业级特性:
    • 分布式调度(基于 Quartz JDBC JobStore)
    • 任务日志追踪
    • 失败重试机制
  4. 生产就绪:完善的异常处理与防御性编程

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

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

相关文章

嵌入式exfat-nofuse文件系统移植和使用

exfat-nofuse 是一款专为linux ARM平台设计的开源项目,它提供了一个非FUSE机制的内核级驱动,用于在Linux系统上无缝地读写exFAT和VFAT文件系统。此项目由Dorimanx维护,采用C语言编写&#xff0c;兼容GPL-2.0许可证。它避开了FUSE&#xff08;用户空间文件系统&#xff09;的使用…

再来一篇,Linux中的软件管理

Linux中软件包的类型 在Linux系统中&#xff0c;软件包有多种不同的格式和类型&#xff0c;主要包括以下几种&#xff1a; DEB (Debian软件包)&#xff08;此软件包不适用于RHEL8 系统&#xff09;&#xff1a; 适用于 Debian 及其衍生版本&#xff08;如Ubuntu等&…

SimBody安装

SimBody安装 Simbody 是一个用于创建生物力学和机械系统仿真的多体动力学库。 SimBody安装 Windows安装&#xff1a; 下载地址&#xff1a;GitHub - simbody/simbody: High-performance C multibody dynamics/physics library for simulating articulated biomechanical and…

thinkphp:部署完整项目到本地phpstudy

一、准备工作 首先准备一个thinkphp的项目文件&#xff1b;准备mysql数据库 二、小皮初步搭建 1、建立网站 在小皮界面&#xff0c;网站->创建网站->输入域名&#xff0c;选择PHP版本等 注&#xff1a;确保端口未被占用 2、将项目文件放入根目录 网站->管理->…

大模型相关面试问题原理及举例

大模型相关面试问题原理及举例 目录 大模型相关面试问题原理及举例Transformer相关面试问题原理及举例大模型模型结构相关面试问题原理及举例注意力机制相关面试问题原理及举例大模型与传统模型区别 原理:大模型靠海量参数和复杂结构,能学习更复杂模式。传统模型参数少、结构…

Redis List 的详细介绍

Redis List 的详细介绍 以下是 Redis List 的详细介绍&#xff0c;从基础命令、内部编码和使用场景三个维度展开&#xff1a; 一、基础命令 Redis List 支持双向操作&#xff08;头尾插入/删除&#xff09;&#xff0c;适用于队列、栈等场景&#xff0c;以下是核心命令分类&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 瑞吉外卖系统开发&#xff08;五&#xff09; 删除分类 分类列表中每条分类信息右侧提供了一个“删除”按钮&#xff0c;当需要将已经存在的分类信息删除时&#xff0c;可以通过单击“删除”按钮实现。 请求路径为/category&#xff0c;携带参数id&#xf…

【Web部署问题】在Tomcat中部署web项目出现http状态-404 -未找到详细解决方案

部署完tomcat记得在选中要运行的工件。 如果没有工件&#xff0c;或者工件有缺失东西&#xff0c;去这里配置工件&#xff0c;

Linux——Shell编程之正则表达式与文本处理器(笔记)

目录 基础正则表达式 1:基础正则表达式示例 &#xff08;4&#xff09;查找任意一个字符“.”与重新字符“*” &#xff08;5&#xff09;查找连续字符范围“{ }” 文本处理器 一、sed工具 二、awk工具 &#xff08;1&#xff09;按行输出文本 &#xff08;2&#xff0…

05-DevOps-Jenkins自动拉取构建代码

新建Gitlab仓库 先在Gitab上创建一个代码仓库&#xff0c;选择创建空白项目 安装说明进行填写&#xff0c;然后点击创建项目 创建好的仓库是空的&#xff0c;什么都没有 新建一个springboot项目&#xff0c;用于代码上传使用。 只是为了测试代码上传功能&#xff0c;所以代码…

SRS transcode支持 h264_nvenc 硬件解码方案

文章目录 SRS transcode支持 h264_nvenc 硬件解码方案1、修改文件2、重新编译3、使用 SRS transcode支持 h264_nvenc 硬件解码方案 SRS 是开源的流媒体服务&#xff0c;但在使用 GPU 服务器时&#xff0c;想要通过硬件加速&#xff0c;目前官方是不支持的&#xff0c;所以简单…

阿里云服务器搭建开源版禅道

一&#xff0c;下载地址&#xff1a;禅道11.5版本发布&#xff0c;主要完善细节&#xff0c;修复bug&#xff0c;新增动态过滤机制 - 禅道下载 - 禅道项目管理软件 下载地址二&#xff1a; 禅道21.6.stable 实现旧编辑器撰写的文档无感升级至新版编辑器 - 禅道下载 - 禅道项目…

怎么用面向对象和状态机架构,设计一个通用的按键检测功能?

说起按键检测&#xff0c;在座的各位&#xff0c;哪个没被它折磨过&#xff1f; 我刚入门时&#xff0c;为了实现一个简单的按键功能&#xff0c;硬生生写了几十行代码&#xff0c;各种 if...else 嵌套&#xff0c;逻辑绕得我自己都头晕。 更可气的是&#xff0c;辛辛苦苦写完…

Java基础系列-LinkedList源码解析

文章目录 简介LinkedList 插入和删除元素的时间复杂度&#xff1f;LinkedList 为什么不能实现 RandomAccess 接口&#xff1f; LinkedList 源码分析Node 定义初始化获取元素插入元素删除元素遍历链表 简介 LinkedList 是一个基于双向链表实现的集合类&#xff0c;经常被拿来和…

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信号时&#xff0c;十分重要的一环就是LVDS输入信号线在经过PCB输入到FPGA中后&#xff0c;本来该严格对齐的信号线会出现时延&#xff0c;所以需要在FPGA内部对其进行延时对齐后再进行解析。 Xilinx 7系器件中用于输入信号延时的组件为IDELAYE2可编程原语&#xff0…

【Oracle专栏】函数中SQL拼接参数 报错处理

Oracle相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 1.背景 最近同事反馈了一个很奇怪的问题,即有一个函数,入参是当前年月,主要作用是通过SQL语句将不合规的数据插入到指定表中,插入数据时带上入参的年月参数。当前问题:单独测试SQL没有问题可以执行成功,…

自然语言处理(NLP)领域大图

以下是一份自然语言处理&#xff08;NLP&#xff09;与大模型领域的领域大图&#xff0c;涵盖技术框架、发展脉络、交叉融合点和应用场景的完整解析&#xff1a; 1. 核心技术体系 基础分析层级 词法分析&#xff1a;分词、词性标注、命名实体识别句法分析&#xff1a;依存句法…