目录
- 核心概念
- 快速入门:一个简单的 "Hello World" 示例
- 进阶:使用
JobDataMap传递数据 - 高级:使用 Cron 表达式
- Spring/Spring Boot 集成 (推荐方式)
- Quartz 配置详解
- 最佳实践
核心概念
在使用 Quartz 之前,必须理解以下几个核心组件:

-
Job (作业): 这是一个接口,你只需要实现
execute(JobExecutionContext context)方法,这个接口代表一个“任务”或“工作单元”,它包含了要执行的业务逻辑,Quartz 不会直接调用你的业务方法,而是调用Job接口的实现类。 -
JobDetail (作业详情): 这是一个
Job的详细信息描述,它包含了Job的实例以及Job的各种属性,name,group,jobClass等,一个JobDetail可以被多个Trigger关联,但每次触发时,Quartz 都会创建一个新的Job实例来执行,以保证作业的状态隔离。 -
Trigger (触发器): 它定义了
Job的执行调度规则,可以设置它在某个具体时间点执行,或者每隔多久执行一次。Trigger需要绑定到一个JobDetail上,Quartz 提供了多种Trigger,最常用的是SimpleTrigger和CronTrigger。 -
Scheduler (调度器): 这是 Quartz 的总指挥,它负责将
Job和Trigger绑定在一起,并根据Trigger的定义来执行Job,你可以通过Scheduler来启动、停止、暂停或恢复调度。
(图片来源网络,侵删)
它们之间的关系可以理解为:
Scheduler 持有 Trigger,Trigger 关联 JobDetail,JobDetail 描述 Job。Scheduler 根据 Trigger 的规则,找到对应的 JobDetail,然后创建并执行 Job。
快速入门:一个简单的 "Hello World" 示例
这个示例将演示如何创建一个简单的定时任务,每5秒打印一次 "Hello, Quartz!"。
步骤 1: 添加 Maven 依赖

在你的 pom.xml 文件中添加 Quartz 的核心依赖。
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version> <!-- 建议使用较新稳定版本 -->
</dependency>
步骤 2: 创建一个 Job 类
创建一个实现 org.quartz.Job 接口的类。
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 这里是你要执行的业务逻辑
System.out.println("Hello, Quartz! - " + new Date());
}
}
步骤 3: 编写主程序来调度任务
创建一个主类,用于初始化 Scheduler,并创建和调度我们的 Job。
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.Date;
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 jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("myJob", "group1") // 给 Job 一个名称和分组
.build();
// 4. 创建 Trigger 实例,定义每5秒执行一次
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1") // 给 Trigger 一个名称和分组
.startNow() // 立即开始
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(5) // 每5秒执行一次
.repeatForever()) // 永远重复
.build();
// 5. 将 Job 和 Trigger 绑定到 Scheduler,并调度执行
scheduler.scheduleJob(jobDetail, trigger);
// 6. 启动调度器
scheduler.start();
System.out.println("Scheduler started. Jobs are scheduled.");
// 为了让主线程不退出,可以睡眠一段时间或者使用 CountDownLatch
try {
Thread.sleep(60000); // 睡眠60秒,让程序运行一会儿
} catch (InterruptedException e) {
e.printStackTrace();
}
// 7. 关闭调度器
scheduler.shutdown();
System.out.println("Scheduler shutdown.");
}
}
运行这个 main 方法,你会看到控制台每隔5秒打印一次 "Hello, Quartz!"。
进阶:使用 JobDataMap 传递数据
我们需要在执行任务时向 Job 传递一些参数,这时可以使用 JobDataMap。
修改 Job 类
public class DataPassingJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 从 JobExecutionContext 中获取 JobDataMap
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
// 从 map 中获取数据
String jobSays = dataMap.getString("jobSays");
float myFloatValue = dataMap.getFloat("myFloatValue");
System.out.println("Job says: " + jobSays);
System.out.println("My Float Value: " + myFloatValue);
}
}
修改主程序
// ... (前面的 SchedulerFactory 和 Scheduler 获取代码)
// 创建 JobDetail
JobDetail jobDetail = JobBuilder.newJob(DataPassingJob.class)
.withIdentity("dataPassingJob", "group1")
.usingJobData("jobSays", "Hello from the JobDataMap!") // 存入字符串
.usingJobData("myFloatValue", 3.14f) // 存入浮点数
.build();
// ... (Trigger 的创建代码)
// ... (调度和启动代码)
高级:使用 Cron 表达式
SimpleTrigger 适合简单的、固定间隔的调度,但更常用、更强大的是 CronTrigger,它使用 Cron 表达式来定义复杂的调度规则。
Cron 表达式格式:
[秒] [分] [小时] [日] [月] [周] [年]
常用符号:
- 任意值
- 不指定值 (用于“日”和“周”冲突时)
- 范围 (
1-5) - 列表 (
1,3,5) - 步长 (
0/5表示从0开始,每5秒一次) L: (L表示月的最后一天,5L表示月的最后一个星期五)
示例:
0/5 * * * * ?: 每5秒执行一次。0 0 12 * * ?: 每天12点(中午)执行。0 15 10 ? * *: 每天10:15执行。0 0/30 9-17 * * ?: 每天9点到17点之间,每30分钟执行一次。0 0 0 L * ?: 每月最后一天的24点执行。
使用 CronTrigger
只需修改 Trigger 的创建部分:
// 创建 CronTrigger 实例
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("cronTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")) // 每5秒执行
.build();
// ... (其余代码不变)
Spring/Spring Boot 集成 (推荐方式)
在 Spring 或 Spring Boot 项目中,手动管理 Scheduler 实例是繁琐且不推荐的,Spring 为我们提供了更优雅的集成方式。
Spring Boot 集成 (最简单)
-
添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency> -
创建 Job 类
你不需要实现
Job接口,只需创建一个普通的 Spring Bean,并添加@Component注解,Quartz 会自动代理它。import org.quartz.DisallowConcurrentExecution; import org.quartz.PersistJobDataAfterExecution; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.Date; @Component // @PersistJobDataAfterExecution // 执行后更新 JobDataMap // @DisallowConcurrentExecution // 不允许并发执行 public class MySpringJob { // Spring Boot 会自动注入 JobDetail 和 Trigger 的配置 // 你也可以在这里注入其他 Service // @Autowired // private SomeService someService; public void execute() { // 这里是业务逻辑 System.out.println("Spring Boot Quartz Job is running at " + new Date()); } } -
配置 Quartz
在
application.properties或application.yml中进行配置。# quartz.properties # 设置 Job 的存储方式为内存 (重启任务会丢失) org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore # 设置线程池 org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.threadCount=5 # 设置调度器实例名称 org.quartz.scheduler.instanceName=MyScheduler # 配置你的任务 (Spring Boot 2.x 之后推荐这种方式) # 任务名称, 任务组, cron 表达式 spring.quartz.job-name=mySpringJob spring.quartz.job-group=myGroup spring.quartz.cron-expression=0/5 * * * * ?
或者,更推荐使用 Java Config 的方式,因为它更灵活:
import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class QuartzConfig { @Bean public JobDetail mySpringJobDetail() { // 关联我们定义的 Spring Bean return JobBuilder.newJob(MySpringJob.class) .withIdentity("mySpringJob", "myGroup") .storeDurably() // 持久化 .build(); } @Bean public Trigger mySpringJobTrigger() { // CronTrigger CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ?"); return TriggerBuilder.newTrigger() .forJob(mySpringJobDetail()) // 关联 JobDetail .withIdentity("mySpringJobTrigger", "myGroup") .withSchedule(cronScheduleBuilder) .build(); } }
Spring Boot 会自动检测到这些配置,并为你启动 Scheduler。
Quartz 配置详解
Quartz 的行为由 quartz.properties 文件或通过代码配置,一些重要的配置项:
- *`org.quartz.scheduler.`**: 调度器本身的配置,如实例名、线程池等。
- *`org.quartz.threadPool.
**: 线程池配置,Quartz 使用线程池来执行JobthreadCount是最重要的参数,它决定了可以并发执行多少个Job`。 - *`org.quartz.jobStore.Job` 的存储配置。
RAMJobStore: 默认配置,将Job和Trigger信息存储在内存中。优点是速度快,缺点是应用重启后所有调度信息都会丢失。JobStoreTX(或JDBCJobStore): 将Job和Trigger信息存储在数据库中。优点是持久化,应用重启后任务不会丢失,你需要提供数据库连接信息,并 Quartz 提供的 SQL 脚本创建相应的表(tables_mysql.sql等)。
使用 JDBCJobStore 示例 (简化版 quartz.properties)
# 使用 JDBCJobStore 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=true # 是否启用集群模式 org.quartz.jobStore.clusterCheckinInterval=20000 # 集群节点间检查间隔 # 数据源配置 (通常由 Spring 管理,这里仅为示例) # org.quartz.dataSource.myDS.connectionProviderURL=jdbc:mysql://localhost:3306/quartz_db # org.quartz.dataSource.myDS.driver=com.mysql.cj.jdbc.Driver # org.quartz.dataSource.myDS.user=root # org.quartz.dataSource.myDS.password=password
最佳实践
- 不要在
Job中使用new关键字:Job实例是由Scheduler创建的,不要在Job内部手动创建新的对象或进行复杂的初始化。Job应该是轻量级的,专注于执行逻辑。 - 处理
Job的异常: 在execute方法中捕获所有异常,并通过JobExecutionException抛出,可以设置refireImmediately来决定是否立即重新执行该任务。 - 状态管理:
Job本身是无状态的(每次执行都是新实例),如果需要保存状态,请使用JobDataMap或将状态保存在外部系统(如数据库、Redis)中。 - 集群部署: 在生产环境中,为了高可用和负载均衡,通常会将应用部署为多节点集群,这时必须使用
JDBCJobStore并且必须在quartz.properties中设置isClustered=true,集群中的所有节点必须共享同一个数据库。 - 考虑持久化: 除非是临时性、不重要的任务,否则强烈建议使用
JDBCJobStore,防止应用崩溃或重启导致任务丢失。 - 避免长任务:
Job的执行时间不应过长,因为它会占用线程池中的一个线程,如果任务耗时很长,应考虑将其设计为异步任务,Job只负责触发这个异步任务。
| 特性 | 描述 |
|---|---|
| 核心组件 | Job (任务逻辑), JobDetail (任务元数据), Trigger (触发规则), Scheduler (调度器) |
| 触发器类型 | SimpleTrigger (简单间隔), CronTrigger (复杂 Cron 表达式) |
| 数据传递 | 通过 JobDataMap 在 Scheduler 和 Job 之间传递参数 |
| 部署方式 | 原生 API: 适合学习和小型项目,代码耦合度高。Spring/Spring Boot 集成: 推荐方式,配置简单,与 Spring 生态无缝集成。 |
| 存储方式 | RAMJobStore (内存,速度快,不持久化), JDBCJobStore (数据库,持久化,支持集群) |
| 集群 | 必须使用 JDBCJobStore,并开启 isClustered 配置,确保所有节点共享数据库。 |
Quartz 是一个非常成熟和强大的调度框架,掌握它对于处理后台定时任务非常有帮助,对于现代 Java 应用,尤其是基于 Spring Boot 的项目,使用其 Starter 进行集成是最高效和最稳妥的选择。
