目录
- Quartz 是什么?
- 核心概念
- 快速入门:第一个 Quartz 程序
- 进阶:使用 Cron 表达式
- 高级:Job 与 JobDataMap
- 任务管理与持久化
- Spring/Spring Boot 集成 (推荐方式)
- Quartz 的优缺点与适用场景
Quartz 是什么?
Quartz 是一个功能强大、开源的作业调度库,完全由 Java 编写,它可以集成到任何 Java 应用中,也可以独立运行。

它的核心功能是:在指定的时间点执行指定的任务(Job)。
想象一下你需要实现以下功能:
- 每天凌晨 2 点生成一份销售报表。
- 每隔 30 分钟检查一次订单状态。
- 每个月的 1 号自动清理过期数据。
手动用 Thread.sleep() 或 Timer 来实现这些功能会非常复杂且不可靠,Quartz 就是为了解决这类问题而生的,它提供了稳定、灵活、可管理的任务调度解决方案。
核心概念
要使用 Quartz,必须先理解它的三个核心组件:

Job (任务)
Job 是一个接口,你只需要实现这个接口并重写 execute() 方法。execute() 方法中的就是你希望定时执行的具体业务逻辑。
public interface Job {
void execute(JobExecutionContext context) throws JobExecutionException;
}
Trigger (触发器)
Trigger 用于定义 Job 的执行时间规则,它决定了一个 Job 何时以及以怎样的频率被执行。
Quartz 提供了多种类型的 Trigger,最常用的是:
SimpleTrigger: 用于简单的调度,在指定的时间点执行一次,或者重复执行 N 次,间隔固定时间。CronTrigger: 功能最强大的触发器,它基于 Cron 表达式,可以定义非常复杂的调度规则,每周一、周三、周五的上午 9:30”。
Scheduler (调度器)
Scheduler 是 Quartz 的总指挥,它负责将 Job 和 Trigger 绑定在一起,并按照 Trigger 设定的规则来执行 Job。
你可以把它想象成一个“调度中心”,所有任务的注册、启动、暂停、恢复、删除等操作都通过它来完成。
它们三者的关系:
Scheduler 将 Job 和 Trigger 关联起来,Trigger 驱动 Scheduler 在特定时间去执行 Job。

[Scheduler] -- (调度) --> [Job]
^
|
| (关联)
|
[Trigger] -- (定义何时执行) --
快速入门:第一个 Quartz 程序
下面我们通过一个最简单的例子,来感受一下 Quartz 的基本用法,这个任务的功能是:每隔 5 秒打印一次 "Hello, Quartz!"。
步骤 1: 添加 Maven 依赖
在你的 pom.xml 文件中添加 Quartz 的核心依赖。
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version> <!-- 建议使用较新稳定版本 -->
</dependency>
<!-- 如果不需要连接数据库,可以排除这个依赖 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
<exclusions>
<exclusion>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
</exclusion>
</exclusions>
</dependency>
步骤 2: 创建一个 Job 类
创建一个实现 Job 接口的类。
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.util.Date;
public class MyFirstJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 这里是你要执行的业务逻辑
System.out.println("Hello, Quartz! " + new Date());
}
}
步骤 3: 编写主程序,启动调度
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
public class QuartzDemo {
public static void main(String[] args) throws SchedulerException {
// 1. 创建一个 SchedulerFactory(调度器工厂)
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
// 2. 从工厂中获取一个 Scheduler 实例(调度器)
Scheduler scheduler = schedulerFactory.getScheduler();
// 3. 创建 JobDetail 实例,并与 MyFirstJob 类绑定
// JobDetail 是 Job 的详细配置信息
JobDetail jobDetail = JobBuilder.newJob(MyFirstJob.class)
.withIdentity("job1", "group1") // 给 Job 一个名称和分组
.build();
// 4. 创建 Trigger 实例(触发器)
// 设置立即开始,每隔 5 秒重复执行一次
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1") // 给 Trigger 一个名称和分组
.startNow() // 立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(5) // 间隔 5 秒
.repeatForever()) // 永久重复
.build();
// 5. 将 Job 和 Trigger 绑定到 Scheduler 中,并启动调度器
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
System.out.println("Scheduler started. Jobs are scheduled.");
// 由于是后台线程,程序不会退出,你需要手动关闭或使用其他方式保持主线程存活
// scheduler.shutdown(); // 关闭调度器
}
}
运行结果: 你会看到控制台每隔 5 秒打印一次 "Hello, Quartz!"。
进阶:使用 Cron 表达式
SimpleTrigger 虽然简单,但不够灵活,在实际开发中,我们更常用 CronTrigger。
Cron 表达式是什么?
Cron 表达式是一个字符串,由 6-7 个子表达式组成,描述了详细的日期和时间信息。
格式:[秒] [分] [小时] [日] [月] [周] [年] (年可选)
常用符号:
- 代表任意值。 在“小时”字段表示每小时。
- 代表“不指定”,通常用于“日”和“周”字段,因为这两个字段会相互冲突。
- 列出多个值。
MON,WED,FRI表示周一、周三、周五。 - 表示一个范围。
1-5表示 1 到 5。 - 表示起始时间和间隔时间。
0/15表示从 0 秒开始,每隔 15 秒;10/5表示从 10 秒开始,每隔 5 秒。 L:表示“。L在“日”字段表示当月的最后一天;L在“周”字段表示周六(周日是 1,周六是 7)。W:表示“工作日”(周一到周五)。- 表示“第几个”。
6#3表示“每个月的第 3 个周五”(周五是 6)。
示例:
0 0 12 * * ?:每天中午 12 点触发。0 15 10 ? * *:每天上午 10:15 触发。0 0/5 14 * * ?:每天下午 2 点到 2 点 55 分之间,每隔 5 分钟触发一次。0 0 18 ? * FRI:每周五下午 6 点触发。0 0 0 1 * ?:每月 1 号午夜 0 点触发。
修改上面的例子,使用 Cron 表达式
我们只需要修改创建 Trigger 的部分。
// ... (前面的代码不变)
// 4. 创建 CronTrigger 实例
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("cronTrigger1", "group1")
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")) // 每隔 5 秒执行一次
.build();
// ... (后面的代码不变)
运行效果和之前一样,但配置方式更加灵活和强大。
高级:Job 与 JobDataMap
我们需要在执行 Job 时传递一些参数,Quartz 提供了 JobDataMap 来实现这个功能。
JobDataMap 是一个 Map 接口的实现,可以存储任何可序列化的对象。
如何传递和使用数据?
通过 JobDetail 传递
这种方式传递的数据,在 Job 的整个生命周期内(对于 StatefulJob)是共享的。
// 在主程序中
JobDetail jobDetail = JobBuilder.newJob(MyDataJob.class)
.withIdentity("dataJob", "group1")
.usingJobData("message", "Hello from JobDetail!") // 存入数据
.usingJobData("count", new Integer(10)) // 存入另一个数据
.build();
// 在 Job 类中
public class MyDataJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 从 JobExecutionContext 中获取 JobDataMap
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String message = dataMap.getString("message");
int count = dataMap.getInt("count");
System.out.println("Message: " + message);
System.out.println("Count: " + count);
}
}
通过 Trigger 传递
JobDataMap 中同时存在 Job 和 Trigger 的同名键,Trigger 中的值会覆盖 Job 中的值。
// 在主程序中
// ... 创建 jobDetail ...
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("dataTrigger", "group1")
.usingJobData("message", "Hello from Trigger!") // 存入同名数据
.build();
在 MyDataJob 中,execute 方法打印出的 message 将会是 "Hello from Trigger!"。
任务管理与持久化
默认情况下,Quartz 会将任务信息保存在内存中,这种方式虽然简单,但如果应用重启,所有已配置的任务都会丢失。
为了解决这个问题,Quartz 支持将任务信息持久化到数据库中,这样即使应用重启,调度器也能从数据库中恢复任务,继续执行。
如何实现持久化?
-
创建 Quartz 数据库表 Quartz 提供了 SQL 脚本来创建所需的表,你可以在 Quartz 的官方文档或其 JAR 包的
org/quartz/impl/jdbcjobstore目录下找到这些脚本(如tables_mysql.sql,tables_oracle.sql等)。 你需要根据你使用的数据库类型,执行相应的 SQL 脚本。 -
配置
SchedulerFactory在创建Scheduler之前,你需要配置quartz.properties文件,或者直接在代码中配置,告诉 Quartz 使用数据库作为 JobStore。quartz.properties配置示例 (MySQL):# 实例名 org.quartz.scheduler.instanceName = MyScheduler # 实例ID,AUTO表示自动生成 org.quartz.scheduler.instanceId = AUTO # JobStore配置 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.jobStore.isClustered = false # 是否集群部署 org.quartz.jobStore.dataSource = myDB # DataSource配置 org.quartz.dataSource.myDB.driver = com.mysql.cj.jdbc.Driver org.quartz.dataSource.myDB.URL = jdbc:mysql://localhost:3306/quartz_db?useSSL=false&serverTimezone=UTC org.quartz.dataSource.myDB.user = root org.quartz.dataSource.myDB.password = your_password
将此文件放在
src/main/resources目录下,Quartz 会自动加载。
其他管理操作
- 暂停任务:
scheduler.pauseJob(jobKey); - 恢复任务:
scheduler.resumeJob(jobKey); - 删除任务:
scheduler.deleteJob(jobKey); - 立即触发任务:
scheduler.triggerJob(jobKey);
Spring/Spring Boot 集成 (推荐方式)
虽然原生 Quartz API 很强大,但在企业级应用中,我们通常不会直接使用它。Spring/Spring Boot 提供了更简洁、更集成的 Quartz 使用方式,它会帮你管理 Scheduler 的生命周期,并且可以方便地将 Job 声明为 Spring Bean,从而注入其他 Service。
Spring Boot 集成示例
-
添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> -
配置
application.ymlSpring Boot 会自动配置 Quartz,如果你想连接数据库,可以添加如下配置:spring: quartz: job-store-type: jdbc # 使用 JDBC 持久化 jdbc: initialize-schema: always # 启动时自动创建 Quartz 表 (首次建议使用,之后可改为 embedded 或 never) properties: org: quartz: scheduler: instanceName: clusteredScheduler instanceId: AUTO jobStore: class: org.quartz.impl.jdbcjobstore.JobStoreTX driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate tablePrefix: QRTZ_ isClustered: true clusterCheckinInterval: 10000 threadPool: class: org.quartz.simpl.SimpleThreadPool threadCount: 10 threadPriority: 5 -
创建一个 Job (作为 Spring Bean) 直接创建一个类,继承
QuartzJobBean并重写executeInternal方法,Spring 会自动管理它。import org.quartz.*; import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.stereotype.Component; @Component public class MySpringJob extends QuartzJobBean { // Spring 会自动注入这个 Service @Autowired private SomeBusinessService someBusinessService; @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { System.out.println("Executing Spring-managed Job at " + new Date()); // 可以在这里调用业务逻辑 someBusinessService.doSomething(); } } -
配置 Job 和 Trigger 创建一个配置类,使用
@Bean来定义JobDetail和Trigger。import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class QuartzConfig { // 定义 JobDetail @Bean public JobDetail myJobDetail() { // 使用 JobBuilder 创建 JobDetail // 注意:这里传入的是 MySpringJob.class return JobBuilder.newJob(MySpringJob.class) .withIdentity("mySpringJob", "myJobGroup") .storeDurably() // 没有触发器关联时也持久化 .build(); } // 定义 Trigger @Bean public Trigger myJobTrigger() { // 使用 CronTriggerBuilder 创建 Trigger CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/10 * * * * ?"); return TriggerBuilder.newTrigger() .forJob(myJobDetail()) // 关联 JobDetail .withIdentity("mySpringJobTrigger", "myTriggerGroup") .withSchedule(cronScheduleBuilder) .build(); } }
启动 Spring Boot 应用,Quartz 任务就会自动运行,这种方式与 Spring 生态无缝集成,是当前最主流的用法。
Quartz 的优缺点与适用场景
优点
- 功能强大:支持复杂的 Cron 表达式,能满足绝大多数定时任务需求。
- 集群支持:通过数据库锁机制,可以实现 Quartz 集群,确保高可用和任务不重复执行。
- 持久化:支持将任务信息存入数据库,应用重启后任务不丢失。
- 灵活管理:提供了丰富的 API 来管理任务的生命周期(暂停、恢复、删除等)。
- 集成性好:可以方便地与 Spring、Spring Boot 等框架集成。
缺点
- 学习成本:概念相对较多(Job, Trigger, Scheduler, JobDataMap 等),初学者需要一定时间理解。
- 重量级:相比一些简单的轻量级定时方案(如 Spring 自带的
@Scheduled),Quartz 的配置和启动过程更重。 - 依赖数据库:如果使用持久化和集群,必须依赖一个数据库实例。
适用场景
- 需要持久化的任务:任务需要在应用重启后恢复。
- 复杂的调度规则:业务逻辑需要基于 Cron 表达式等复杂时间规则来执行。
- 需要集群部署的应用:确保任务在分布式环境下能可靠执行,且不重复执行。
- 需要动态管理任务:在运行时动态地添加、删除、修改任务。
| 特性 | 说明 |
|---|---|
| 核心组件 | Job (任务), Trigger (触发器), Scheduler (调度器) |
| 触发器类型 | SimpleTrigger (简单), CronTrigger (强大,基于表达式) |
| 数据传递 | 通过 JobDataMap,可在 JobDetail 或 Trigger 中设置 |
| 持久化 | 通过配置 JobStore 为 JDBCJobStore,将任务信息存入数据库 |
| 集群 | 基于数据库锁机制,实现高可用和任务负载均衡 |
| 最佳实践 | 在 Spring/Spring Boot 项目中,优先使用其提供的集成方式,将 Job 声明为 Bean,管理更方便 |
选择 Quartz,意味着你选择了一个成熟、稳定、功能全面的调度解决方案,虽然它比 @Scheduled 复杂,但对于有复杂业务需求的系统来说,它的价值是巨大的。
