You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中Quartz重复调度问题:避免应用重启重复创建定时任务

Spring Quartz 避免重复创建Cron任务问题

我有一个Spring Quartz应用,每次启动应用时都会调度Cron任务。我希望当任务已存在时,不再重复创建该Cron任务。

调度代码

@PostConstruct
public void assignJobs() throws IOException, SchedulerException {
    JobDetail jobDetail = JobBuilder.newJob(CollectTransactionDataJob.class)
            .withIdentity(UUID.randomUUID().toString(), CollectTransactionDataJob.GROUP)
            .withDescription(CollectTransactionDataJob.DESCRIPTION)
            .storeDurably()
            .build();

    Trigger trigger = TriggerBuilder.newTrigger()
            .forJob(jobDetail)
            .withIdentity(jobDetail.getKey().getName(), CollectTransactionDataJob.TRIGGER_GROUP)
            .withDescription(CollectTransactionDataJob.TRIGGER_DESCRIPTION)
            .withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(10, 30))
            .build();

    Scheduler scheduler = quartzConfig.schedulerFactoryBean().getScheduler();
    scheduler.scheduleJob(jobDetail, trigger);
}

期望行为

若CRON触发时间为10:30
当前时间为10:00
在10:00至10:30期间应用启动2次
则10:30时,CRON应仅执行1次。

实际行为

10:30时,CRON执行了2次。

Quartz配置

org.quartz.jobStore.class = org.springframework.scheduling.quartz.LocalDataSourceJobStore
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate

问题原因

当前代码每次启动时都会用UUID.randomUUID().toString()生成唯一的Job和Trigger名称,结合持久化的LocalDataSourceJobStore,多次启动后会在数据库中创建多个逻辑完全相同的Job和Trigger,到触发时间时所有Trigger都会触发对应的Job,导致重复执行。

解决方案

固定Job和Trigger的唯一标识(name和group),并在调度前检查是否已存在,存在则跳过创建逻辑:

@PostConstruct
public void assignJobs() throws IOException, SchedulerException {
    // 固定Job和Trigger的标识,避免随机生成
    String jobName = "collect-transaction-data-job";
    String triggerName = "collect-transaction-data-trigger";
    JobKey jobKey = new JobKey(jobName, CollectTransactionDataJob.GROUP);
    TriggerKey triggerKey = new TriggerKey(triggerName, CollectTransactionDataJob.TRIGGER_GROUP);

    Scheduler scheduler = quartzConfig.schedulerFactoryBean().getScheduler();

    // 检查Job是否已存在,不存在则创建
    if (!scheduler.checkExists(jobKey)) {
        JobDetail jobDetail = JobBuilder.newJob(CollectTransactionDataJob.class)
                .withIdentity(jobKey)
                .withDescription(CollectTransactionDataJob.DESCRIPTION)
                .storeDurably()
                .build();
        scheduler.addJob(jobDetail, true);
    }

    // 检查Trigger是否已存在,不存在则创建并关联Job
    if (!scheduler.checkExists(triggerKey)) {
        Trigger trigger = TriggerBuilder.newTrigger()
                .forJob(jobKey)
                .withIdentity(triggerKey)
                .withDescription(CollectTransactionDataJob.TRIGGER_DESCRIPTION)
                .withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(10, 30))
                .build();
        scheduler.scheduleJob(trigger);
    }
}

关键改动说明

  1. 固定标识:用固定字符串代替随机UUID作为Job和Trigger的name,确保每次启动时检查的是同一个任务实体。
  2. 存在性检查:通过scheduler.checkExists()方法判断Job和Trigger是否已在Quartz的JobStore中存在,避免重复创建。
  3. 拆分调度逻辑:先创建Job(如果不存在),再创建Trigger并关联Job,逻辑更清晰,也避免了原代码中Job和Trigger绑定过紧的问题。

内容的提问来源于stack exchange,提问作者Aditya Rewari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 18:09:26