目录
- Quartz 核心概念
- 快速入门:一个简单的 "Hello World" 示例
- 进阶:使用
JobDataMap传递数据 - 高级:使用 Cron 表达式
- Spring/Spring Boot 集成(推荐方式)
- 总结与最佳实践
Quartz 核心概念
在开始编码前,理解 Quartz 的几个核心组件至关重要:

-
Job(作业)- 这是一个接口,你只需要实现它的
execute()方法,这个接口代表一个“可执行的任务”,即你想要定时执行的逻辑。 - 发送邮件、生成报表、清理数据等。
- 这是一个接口,你只需要实现它的
-
JobDetail(作业详情)- 它不是
Job本身,而是Job的实例化配置信息,它包含了Job的全限定类名、关联的JobDataMap(用于给 Job 传递数据)以及一些其他元数据。 JobDetail是一个“定义”,而Job是一个“执行体”,你可以用同一个JobDetail多次调度同一个Job。
- 它不是
-
Trigger(触发器)- 它定义了
Job的执行时间规则。Trigger决定了Job何时、以何种频率执行。 - Quartz 提供了两种主要的
Trigger:SimpleTrigger:用于简单的调度,比如在指定时间执行一次,或每隔 N 秒重复执行 N 次。CronTrigger:功能更强大,基于 Cron 表达式,可以定义复杂的调度规则(如:每个工作日的上午 9 点执行)。
- 它定义了
-
Scheduler(调度器)
(图片来源网络,侵删)- 这是 Quartz 的核心控制器,它负责将
Job和Trigger绑定在一起,并按照Trigger的定义来执行Job。 Scheduler的生命周期是:创建 -> 添加 Job 和 Trigger -> 启动 -> 关闭。
- 这是 Quartz 的核心控制器,它负责将
关系图解:
+----------------+ +---------------------+
| Scheduler |----->| Trigger |
| (调度器) | | (触发器: 何时执行) |
+----------------+ +----------+----------+
^ |
| |
| v
| +----------------+
| | JobDetail |
| | (作业详情: 执行什么)|
| +--------+--------+
| |
| |
| v
| +----------------+
+----------------->| Job |
| (作业: 具体逻辑) |
+----------------+
快速入门:一个简单的 "Hello World" 示例
这个示例将展示如何手动创建一个最简单的定时任务。
步骤 1: 添加依赖
使用 Maven,在你的 pom.xml 中添加 Quartz 依赖:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version> <!-- 建议使用较新稳定版本 -->
</dependency>
<!-- 不需要单独的 slf4j-api,quartz 会传递依赖 -->
步骤 2: 创建一个 Job
创建一个实现 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: 编码调度任务
创建一个主类来启动调度器。
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
scheduler.start();
// 为了让程序不退出,保持 Scheduler 运行
try {
Thread.sleep(60000); // 运行 60 秒后关闭
} catch (InterruptedException e) {
e.printStackTrace();
}
// 7. 关闭 Scheduler
scheduler.shutdown();
}
}
运行 QuartzSchedulerDemo,你将看到控制台每 5 秒打印一次 "Hello, Quartz!"。
进阶:使用 JobDataMap 传递数据
有时我们需要在执行任务时传递一些参数,比如用户 ID、订单号等,这时就可以使用 JobDataMap。
JobDataMap 是 java.util.Map 的实现,可以在 JobDetail 和 Trigger 中设置。
修改 Job 类
public class DataPassingJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 从 JobExecutionContext 中获取 JobDataMap
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
// 从 map 中获取数据
String jobName = dataMap.getString("jobName");
int count = dataMap.getInt("count");
System.out.println("Job Name: " + jobName + ", Count: " + count + ", Time: " + new Date());
}
}
修改调度代码
// ... 在 main 方法中 ...
// 3. 创建 JobDetail 并设置数据
JobDetail jobDetail = JobBuilder.newJob(DataPassingJob.class)
.withIdentity("dataJob", "group1")
.usingJobData("jobName", "Data Passing Test") // 传递字符串
.usingJobData("count", 0) // 传递整数
.build();
// 4. 创建 Trigger,也可以在 Trigger 中设置数据
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("dataTrigger", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(3)
.repeatForever())
.usingJobData("triggerParam", "Hello from Trigger!") // 在 Trigger 中设置的数据
.build();
// ... 其余代码不变 ...
在 Job 中,你可以通过 context.getTrigger().getJobDataMap() 来获取 Trigger 中设置的数据。
高级:使用 Cron 表达式
CronTrigger 是最常用的触发器,它使用 Cron 表达式来定义复杂的调度时间。
Cron 表达式格式
[秒] [分] [小时] [日] [月] [周] [年]
- 代表所有值
- 不指定值,用于日和周字段,避免冲突
- 范围,如
1-5 - 列表,如
1,3,5 - 步长,如
0/5表示从 0 开始,每 5 秒一次
常用示例:
0 0 12 * * ?:每天中午 12 点触发。0 15 10 ? * *:每天上午 10:15 触发。0 0/5 14 * * ?:每天下午 2 点到 2:55 点之间,每 5 分钟触发一次。0 0/30 9-17 * * ?:每天上午 9 点到下午 5 点之间,每 30 分钟触发一次。0 0 0 L * ?:每月最后一天午夜触发。0 0 0 ? * 1-5:每周一至周五的午夜触发。
使用 CronTrigger
// ... 在 main 方法中 ...
// 3. 创建 JobDetail
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("cronJob", "group1")
.build();
// 4. 创建 CronTrigger,设置表达式(每天上午 9 点执行)
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("cronTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 9 * * ?")) // 每天 9:00
.build();
// ... 其余代码不变 ...
Spring/Spring Boot 集成(推荐方式)
在实际企业级应用中,不推荐像上面那样手动创建和调度任务,最佳实践是将其与 Spring/Spring Boot 框架集成,这样可以利用 Spring 的 IoC 容器管理 Bean,实现更优雅的代码。
Spring Boot 集成(非常简单)
步骤 1: 添加依赖
Spring Boot 提供了 spring-boot-starter-quartz,它会自动配置好所有事情。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
步骤 2: 创建 Job
Quartz 的 Job 不能是 Spring 的 @Service,但我们可以让它注入 Spring 管理的 Bean,Quartz 会通过 AutowireCapableBeanJobFactory 自动为你注入。
import org.quartz.*;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class SpringBootJob implements Job {
// 你可以直接注入任何 Spring 管理的 Bean
// @Autowired
// private SomeService someService;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("Spring Boot 集成的 Quartz Job 正在执行! " + new Date());
// someService.doSomething();
}
}
步骤 3: 配置 Job 和 Trigger
创建一个配置类来定义 Job 和 Trigger。
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuartzConfig {
// 1. 定义 JobDetail
@Bean
public JobDetail springBootJobDetail() {
// 不需要指定 class,因为 Spring 会根据名字自动查找
return JobBuilder.newJob(SpringBootJob.class)
.withIdentity("springBootJob")
.storeDurably() // 没有立即关联的 Trigger 时需要设置为持久化
.build();
}
// 2. 定义 Trigger
@Bean
public Trigger springBootJobTrigger() {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/10 * * * * ?");
return TriggerBuilder.newTrigger()
.forJob(springBootJobDetail()) // 关联 JobDetail
.withIdentity("springBootJobTrigger")
.withSchedule(cronScheduleBuilder)
.build();
}
}
就这么简单! 启动你的 Spring Boot 应用,Quartz 会自动被初始化,并按照你定义的 Cron 表达式执行任务。
总结与最佳实践
- 使用
JobDataMap传递数据:避免在Job类中硬编码参数,使任务更具通用性。 - 优先使用
CronTrigger:对于绝大多数业务场景,Cron 表达式比SimpleTrigger更灵活、更易读。 - 使用 Spring/Spring Boot 集成:在项目中,强烈推荐使用框架集成,它简化了配置,让你能专注于业务逻辑,并能方便地与其他 Spring 组件(如事务、数据源)集成。
- 处理 Job 异常:在
Job的execute方法中妥善处理异常,避免任务因未捕获的异常而失败,Quartz 会将异常记录下来。 - 持久化配置:对于生产环境,务必配置 Quartz 使用数据库(JDBC JobStore)来存储 Job 和 Trigger 的信息,这样即使应用重启,调度任务也不会丢失,并且支持集群部署,Spring Boot Starter 默认会使用内存存储,你需要通过
application.properties修改为 JDBC 存储。# 示例:配置为 JDBC 存储 (需要提前创建 Quartz 相关表) spring.quartz.job-store-type=jdbc spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
希望这份详细的指南能帮助你掌握 Quartz 定时任务!
