杰瑞科技汇

Java Quartz定时任务如何配置与运行?

什么是 Quartz?

Quartz 的核心是三个基本概念:

Java Quartz定时任务如何配置与运行?-图1
(图片来源网络,侵删)
  1. 调度器:这是 Quartz 的中枢,负责管理所有调度相关的组件(Job, Trigger, Scheduler本身),你可以把它想象成一个“引擎”,驱动着所有定时任务的执行。
  2. 任务:这是你想要执行的具体工作,在 Quartz 中,Job 是一个接口,你只需要实现这个接口的 execute() 方法,将你的业务逻辑写在里面即可。
  3. 触发器:它决定了 Job 何时以及如何执行,你可以设置一个触发器让它每天凌晨 2 点执行,或者每隔 10 分钟执行一次,一个 Job 可以绑定多个 Trigger,但一个 Trigger 在同一时间只能绑定一个 Job

核心组件详解

1 Job (任务)

Job 是一个接口,你只需要实现它。

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class MyJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 这里写你的业务逻辑
        System.out.println("Hello, Quartz! MyJob is running at " + new Date());
        // 你可以从 JobExecutionContext 中获取更多信息
        // 获取 Trigger 的名称
        String triggerName = context.getTrigger().getKey().getName();
        System.out.println("Trigger Name: " + triggerName);
    }
}

2 Trigger (触发器)

Trigger 用来定义调度规则,最常用的两种触发器是:

  • SimpleTrigger:用于简单的调度场景,在指定时间执行一次,或者在指定时间开始,然后每隔一段时间重复执行 N 次。
  • CronTrigger:功能更加强大,基于 Cron 表达式,适用于复杂的调度规则,每周一、周三、周五的上午 9:30 执行”。

Cron 表达式示例:

表达式 含义
0 0 12 * * ? 每天中午12点触发
0 15 10 ? * * 每天上午10:15触发
0 15 10 * * ? 2025 在2025年的每天上午10:15触发
0 * 14 * * ? 在每天下午2点到下午2点59分之间的每分钟触发
0 0/5 14 * * ? 在每天下午2点到下午2点55分之间的每5分钟触发
0 0/5 14,18 * * ? 在每天下午2点到2点55分和下午6点到6点55分之间的每5分钟触发
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时触发
0 0 0 1 * ? 每月1号凌晨12点触发
0 15 10 ? * MON-FRI 周一至周五的上午10:15触发
0 15 10 L * ? 每月最后一天的上午10:15触发
0 15 10 ? * 6L 每月最后一个星期五的上午10:15触发
0 15 10 ? * 6#3 每月的第三个星期五的上午10:15触发

3 Scheduler (调度器)

Scheduler 是总指挥,它将 JobTrigger 结合起来,并按照 Trigger 的定义来执行 Job


快速上手示例 (原生 API)

下面是一个完整的、简单的 Quartz 示例,展示了如何创建、调度并运行一个任务。

步骤 1: 添加依赖

在你的 pom.xml 文件中添加 Quartz 的核心依赖。

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version> <!-- 建议使用较新稳定版本 -->
</dependency>
<!-- 为了方便日志输出,可以添加 slf4j 实现 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
    <version>1.7.36</version>
</dependency>

步骤 2: 编写 Job 类

创建一个实现 Job 接口的类。

package com.example.demo;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 获取当前时间
        Date date = new Date();
        // 打印任务执行信息
        System.out.println("HelloJob 正在执行,时间: " + date);
    }
}

步骤 3: 编写调度代码

这是将所有组件串联起来的主程序。

package com.example.demo;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
public class QuartzSchedulerDemo {
    public static void main(String[] args) throws SchedulerException {
        // 1. 创建一个 SchedulerFactory 工厂实例
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        // 2. 从工厂中获取一个 Scheduler 调度器实例
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 3. 创建 JobDetail 实例,并与 HelloJob 类绑定
        // JobDetail 是任务的详细配置,包括任务的名称、分组以及关联的 Job 类
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("myJob", "group1") // 定义 Job 的名称和分组
                .build();
        // 4. 创建 Trigger 实例,定义触发规则
        // 使用 CronTrigger,设置每 5 秒执行一次
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("myTrigger", "group1") // 定义 Trigger 的名称和分组
                .startNow() // 立即生效
                .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")) // Cron 表达式
                .build();
        // 5. 将 Job 和 Trigger 绑定到 Scheduler 中,并调度执行
        scheduler.scheduleJob(jobDetail, trigger);
        // 6. 启动调度器
        scheduler.start();
        // 为了让程序不退出,可以主线程睡眠
        try {
            Thread.sleep(60000); // 睡眠60秒,观察任务执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 7. 关闭调度器
        // scheduler.shutdown();
    }
}

运行结果: 你会看到控制台每隔 5 秒打印一次 "HelloJob 正在执行,时间: ...",持续 60 秒。


高级特性与最佳实践

在实际项目中,我们通常不会直接使用原生 API,而是结合 Spring/Spring Boot 来使用,这样能更好地管理生命周期和依赖注入。

1 Spring Boot 集成 (推荐)

Spring Boot 提供了非常方便的 spring-boot-starter-quartz,可以零配置快速集成。

步骤 1: 添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

步骤 2: 编写 Job 类

Spring Boot 会自动扫描 @Component@Service 注解的 Job 实现类。关键点Job 类需要有一个无参构造函数,因为 Spring 会通过反射创建实例。

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class MySpringJob implements Job {
    // Spring 会自动注入依赖,Service
    // @Autowired
    // private MyBusinessService myBusinessService;
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("MySpringJob 正在执行,时间: " + new Date());
        // 这里可以调用你的业务逻辑
        // myBusinessService.doSomething();
    }
}

步骤 3: 配置 Job 和 Trigger

在 Spring Boot 的配置类中,你可以使用 @Bean 来定义 JobDetailTrigger

import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuartzConfig {
    // 1. 定义 JobDetail
    @Bean
    public JobDetail myJobDetail() {
        // 使用 JobBuilder 构建 JobDetail
        // ofType 指定 Job 的实现类
        // storeDurably 表示即使没有 Trigger 关联,也持久化保存 JobDetail
        return JobBuilder.newJob(MySpringJob.class)
                .withIdentity("mySpringJob", "springGroup")
                .storeDurably()
                .build();
    }
    // 2. 定义 Trigger
    @Bean
    public Trigger myJobTrigger() {
        // 使用 TriggerBuilder 构建 Trigger
        // forJob 关联上面定义的 JobDetail
        // withSchedule 设置 Cron 表达式
        return TriggerBuilder.newTrigger()
                .forJob(myJobDetail()) // 关联 JobDetail
                .withIdentity("mySpringJobTrigger", "springGroup")
                .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")) // 每10秒执行一次
                .build();
    }
}

启动你的 Spring Boot 应用,Quartz 就会自动加载并执行这个定时任务。

2 持久化

默认情况下,Quartz 将 JobTrigger 的信息保存在内存中,这意味着如果应用重启,所有调度信息都会丢失。

持久化 是将调度信息(Job, Trigger, Calendar 等)保存到数据库中,这样做的好处是:

  • 应用重启后,之前的调度任务不会丢失,会按照之前的配置继续执行。
  • 可以实现集群部署,多个节点共享同一个数据库,协同工作。

如何实现持久化?

  1. 添加数据库驱动依赖:如 mysql-connector-java
  2. 创建 Quartz 表:Quartz 官方提供了多种数据库的 SQL 脚本(在 quartz 包的 docs/dbTables 目录下),你需要在你选择的数据库中执行这些脚本。
  3. 配置数据源:在 application.propertiesapplication.yml 中配置 Quartz 使用的数据库连接信息。

application.properties 示例:

# Quartz 相关配置
spring.quartz.job-store-type=jdbc # 使用 JDBC 方式持久化
spring.quartz.jdbc.initialize-schema=never # 不让 Spring Boot 自动创建表,因为我们手动执行了
# 配置 Quartz 使用的数据源 (可以复用 Spring 的主数据源,也可以单独配置)
spring.quartz.properties.org.quartz.dataSource.myDS.connectionProviderUrl=jdbc:mysql://localhost:3306/quartz_db?useSSL=false&serverTimezone=UTC
spring.quartz.properties.org.quartz.dataSource.myDS.driverClass=com.mysql.cj.jdbc.Driver
spring.quartz.properties.org.quartz.dataSource.myDS.user=root
spring.quartz.properties.org.quartz.dataSource.myDS.password=yourpassword

3 动态修改 Cron 表达式

在实际业务中,我们经常需要动态修改任务的执行周期,Spring Boot 集成下,可以通过 Scheduler API 来实现。

import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Service;
@Service
public class DynamicQuartzService {
    @Autowired
    private SchedulerFactoryBean schedulerFactoryBean;
    /**
     * 更新任务的 Cron 表达式
     * @param jobName 任务名
     * @param jobGroup 任务组
     * @param newCronExpression 新的 Cron 表达式
     */
    public void updateCronExpression(String jobName, String jobGroup, String newCronExpression) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        // 获取 Trigger 的 Key
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
        // 获取旧的 Trigger
        CronTrigger oldTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        // 如果旧的 Trigger 不存在,则创建一个新的
        if (oldTrigger == null) {
            // 获取 JobDetail
            JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroup));
            if (jobDetail == null) {
                throw new RuntimeException("Job " + jobName + " not found.");
            }
            // 创建新的 Trigger
            CronTrigger newTrigger = TriggerBuilder.newTrigger()
                    .withIdentity(triggerKey)
                    .forJob(jobDetail)
                    .withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression))
                    .build();
            // 调度新的 Job
            scheduler.scheduleJob(jobDetail, newTrigger);
        } else {
            // 修改旧的 Trigger
            CronTrigger newTrigger = (CronTrigger) oldTrigger.getTriggerBuilder()
                    .withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression))
                    .build();
            // 移除旧的 Trigger,并调度新的 Trigger
            scheduler.rescheduleJob(triggerKey, newTrigger);
        }
    }
}

特性 原生 API Spring Boot 集成
易用性 较低,需要手动管理 Scheduler 生命周期 ,自动配置,开箱即用
依赖注入 不支持,Job 内部无法直接注入 Spring Bean 支持Job 类可以像普通 Spring Bean 一样注入依赖
配置方式 Java 代码硬编码 application.properties / @Bean 注解,更灵活
推荐场景 学习 Quartz 原理、非 Spring 项目 绝大多数 Java 项目,尤其是 Spring/Spring Boot 项目

Quartz 是一个非常成熟和强大的调度框架,掌握它对于处理后台定时任务、数据处理、报表生成等场景至关重要,从原生 API 入手有助于理解其核心概念,但在实际开发中,强烈推荐使用 Spring Boot 集成方式,以获得更高的开发效率和更好的可维护性。

分享:
扫描分享到社交APP
上一篇
下一篇