1. 项目初始化
1.1 引入依赖
首先,我们需要在 Spring Boot 项目中引入 Quartz 相关的依赖。
对于 Maven 项目,编辑 pom.xml
文件,添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
对于 Gradle 项目,则添加如下依赖:
implementation 'org.springframework.boot:spring-boot-starter-quartz'
1.2 添加数据库支持(可选)
如果我们打算让 Quartz 任务持久化(任务信息存储到数据库中),则需要添加数据库驱动。例如,使用 MySQL 数据库时,添加以下依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
这样我们就可以通过 Quartz 将任务存储在数据库中,保证任务在调度器重启后仍然存在。
2. 创建 Quartz 任务
2.1 创建一个简单的 Job
在 Quartz 中,每个任务都由一个 Job
类来表示。Job
是 Quartz 的核心接口,它只有一个 execute()
方法,当触发器触发任务时,该方法将会被调用。
我们首先创建一个简单的任务类 MyQuartzJob
,该任务每次被触发时会输出 "Hello, Quartz!" 以及当前的时间。
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;
@Component
public class MyQuartzJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("执行任务: Hello, Quartz! 当前时间: " + System.currentTimeMillis());
}
}
2.2 解释
Job
接口要求实现execute(JobExecutionContext context)
方法,这个方法将在任务触发时被 Quartz 调度器调用。JobExecutionContext
提供了调度任务的上下文信息,如任务数据、触发器信息等。- 我们使用了
@Component
注解将该任务类注册为 Spring 的 Bean,便于在配置中注入和管理。
3. 配置 Quartz 调度任务
为了使任务能够被 Quartz 调度器调度,我们需要配置 JobDetail
和 Trigger
。JobDetail
定义了任务的具体信息,而 Trigger
定义了任务的触发规则(如时间间隔、执行次数等)。
3.1 基于时间间隔的 SimpleTrigger
以下是一个配置类 QuartzConfig
,它定义了一个每 5 秒触发一次的任务。
import org.quartz.JobDetail;
import org.quartz.Trigger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
@Configuration
public class QuartzConfig {
// 配置 JobDetail
@Bean
public JobDetailFactoryBean jobDetail() {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(MyQuartzJob.class); // 绑定 Job 类
factoryBean.setDescription("这是一个示例 Quartz Job");
factoryBean.setDurability(true); // 任务持久化
return factoryBean;
}
// 配置 Trigger(每 5 秒执行一次)
@Bean
public SimpleTriggerFactoryBean trigger(JobDetail jobDetail) {
SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean();
factoryBean.setJobDetail(jobDetail); // 绑定任务
factoryBean.setRepeatInterval(5000); // 每 5 秒触发一次
factoryBean.setRepeatCount(SimpleTriggerFactoryBean.REPEAT_INDEFINITELY); // 无限重复执行
return factoryBean;
}
}
3.2 解释
- JobDetailFactoryBean:用于定义 Quartz 的
JobDetail
。它将指定的Job
与任务元数据(如任务描述、持久化策略等)绑定。 - SimpleTriggerFactoryBean:用于配置一个简单触发器(
SimpleTrigger
),我们可以设置触发间隔时间(单位:毫秒)以及重复执行次数。在这个示例中,任务会每隔 5 秒重复执行一次,且无限次重复。
3.3 CronTrigger 的使用
如果需要更复杂的触发条件,比如按照每天某个时间点或每周的某些特定时刻触发任务,Quartz 提供了 CronTrigger
。CronTrigger
允许我们通过 Cron 表达式精确地控制任务的调度时间。
import org.quartz.JobDetail;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
@Configuration
public class QuartzCronConfig {
// 配置 JobDetail
@Bean
public JobDetailFactoryBean cronJobDetail() {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(MyQuartzJob.class);
factoryBean.setDescription("这是一个示例 Cron Quartz Job");
factoryBean.setDurability(true); // 任务持久化
return factoryBean;
}
// 配置 CronTrigger(每 10 秒执行一次)
@Bean
public CronTriggerFactoryBean cronTrigger(JobDetail cronJobDetail) {
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setJobDetail(cronJobDetail); // 绑定任务
factoryBean.setCronExpression("0/10 * * * * ?"); // 每 10 秒触发一次
return factoryBean;
}
}
3.4 Cron 表达式解释
"0/10 * * * * ?"
:每隔 10 秒执行一次。Cron 表达式的格式是"秒 分 时 日 月 星期 年(可选)"
。这个表达式表示每分钟的第 0 秒开始,每 10 秒触发一次任务。
4. 使用 JobDataMap 传递参数
在实际应用中,任务往往需要使用外部数据。Quartz 提供了 JobDataMap
,用于在任务执行时传递参数。
4.1 配置任务并传递参数
在 JobDetailFactoryBean
中,我们可以通过 setJobDataAsMap()
方法传递参数:
@Bean
public JobDetailFactoryBean jobDetailWithParams() {
JobDetailFactoryBean factoryBean = new JobDetailFactoryBean();
factoryBean.setJobClass(MyQuartzJob.class);
factoryBean.setDescription("带参数的 Quartz Job");
factoryBean.setDurability(true);
// 传递参数
Map<String, Object> jobDataMap = new HashMap<>();
jobDataMap.put("param1", "参数值1");
factoryBean.setJobDataAsMap(jobDataMap);
return factoryBean;
}
4.2 在 Job 中获取参数
在 execute()
方法中,我们可以通过 JobExecutionContext
获取传递的参数:
public class MyQuartzJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
String param1 = context.getMergedJobDataMap().getString("param1");
System.out.println("执行任务,参数1: " + param1);
}
}
JobDataMap
允许我们在任务执行时携带自定义数据,并在任务中访问这些数据。对于复杂的业务逻辑,这一特性非常实用。
5. 任务持久化(使用 JDBC)
Quartz 支持将任务信息持久化到数据库中,这对于任务调度器重启时继续执行未完成的任务非常重要。为了使用持久化功能,我们需要配置数据库,并让 Quartz 将任务和调度信息保存到数据库中。
5.1 创建数据库表
首先,你需要为 Quartz 创建数据库表。Quartz 官方提供了 SQL 脚本用于创建这些表。你可以在 Quartz 文档 中找到
5.2 配置 Quartz 使用数据库
在 application.properties
或 application.yml
文件中,我们需要配置 Quartz 使用 JDBC 存储任务调度数据。
对于 application.properties
:
spring.quartz.job-store-type=jdbc # 使用数据库存储
spring.quartz.jdbc.initialize-schema=always # 自动创建表,生产环境中建议使用 validate
spring.quartz.properties.org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate # JDBC驱动代理
spring.datasource.url=jdbc:mysql://localhost:3306/quartz_db?useSSL=false&serverTimezone=UTC # 数据库连接URL
spring.datasource.username=root # 数据库用户名
spring.datasource.password=your_password # 数据库密码
对于 application.yml
:
spring:
quartz:
job-store-type: jdbc # 使用数据库存储
jdbc:
initialize-schema: always # 自动创建表
properties:
org:
quartz:
jobStore:
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate # JDBC代理
datasource:
url: jdbc:mysql://localhost:3306/quartz_db?useSSL=false&serverTimezone=UTC # 数据库URL
username: root # 数据库用户名
password: your_password # 数据库密码
5.3 初始化数据库
Quartz 提供了内置的表结构,默认会在数据库中创建任务、触发器、锁等所需的表。配置 spring.quartz.jdbc.initialize-schema=always
可以让 Spring Boot 在启动时自动初始化这些表。你也可以手动执行 Quartz 提供的 SQL 脚本,这些脚本位于 Quartz 官方发布包的 docs/dbTables
目录下。
5.4 解释
job-store-type
:指定 Quartz 如何存储任务信息。memory
表示将任务存储在内存中,jdbc
则表示将任务存储在数据库中。driverDelegateClass
:指定数据库驱动代理类。对于 MySQL,我们使用StdJDBCDelegate
,如果使用其他数据库(如 PostgreSQL),则需要根据数据库类型选择对应的代理类。initialize-schema
:控制是否在启动时自动创建 Quartz 需要的表。生产环境下应设置为validate
,以避免重复创建表。
5.5 测试任务持久化
当 Quartz 任务配置完成并启动时,所有的任务、触发器以及调度信息都会存储到配置的数据库中。你可以停止并重启应用程序,来验证任务的持久性。如果配置正确,任务调度器重新启动后,Quartz 将从数据库中恢复之前的任务调度信息并继续执行未完成的任务。
6. 任务并发控制
默认情况下,Quartz 会在多个线程中并发执行多个任务。如果你希望控制任务的并发执行,可以通过 @DisallowConcurrentExecution
注解来实现,确保同一个任务在同一时间只有一个实例在运行。
6.1 禁止并发执行
修改我们的 MyQuartzJob
类,添加 @DisallowConcurrentExecution
注解:
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.stereotype.Component;
@DisallowConcurrentExecution // 禁止并发执行
@Component
public class MyQuartzJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("执行任务: Hello, Quartz! 当前时间: " + System.currentTimeMillis());
}
}
6.2 解释
@DisallowConcurrentExecution
:Quartz 提供的注解,用于确保在同一时间内同一任务(JobDetail
)只能执行一次。如果该任务正在运行,新的任务不会并发执行。
7. 动态添加与删除任务
在实际应用中,我们可能需要根据某些业务规则动态地添加或删除定时任务。Quartz 支持动态管理任务,可以通过 Quartz 提供的 API 来实现任务的增删改查。
7.1 动态添加任务
以下是一个示例,用于动态添加任务:
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class QuartzJobService {
@Autowired
private Scheduler scheduler; // 注入 Quartz 的 Scheduler
// 动态添加任务
public void addJob(String jobName, String groupName, String cronExpression) throws SchedulerException {
JobDetail jobDetail = JobBuilder.newJob(MyQuartzJob.class)
.withIdentity(jobName, groupName)
.build();
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobName + "Trigger", groupName)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.build();
scheduler.scheduleJob(jobDetail, trigger); // 将任务和触发器注册到调度器
}
}
7.2 动态删除任务
以下是动态删除任务的示例:
public void deleteJob(String jobName, String groupName) throws SchedulerException {
JobKey jobKey = JobKey.jobKey(jobName, groupName);
scheduler.deleteJob(jobKey); // 删除指定的任务
}
7.3 调用示例
你可以通过 REST API 或者在服务启动时调用这些方法:
@RestController
@RequestMapping("/jobs")
public class JobController {
@Autowired
private QuartzJobService jobService;
@PostMapping("/add")
public String addJob(@RequestParam String jobName,
@RequestParam String groupName,
@RequestParam String cronExpression) {
try {
jobService.addJob(jobName, groupName, cronExpression);
return "任务添加成功";
} catch (SchedulerException e) {
e.printStackTrace();
return "任务添加失败";
}
}
@DeleteMapping("/delete")
public String deleteJob(@RequestParam String jobName,
@RequestParam String groupName) {
try {
jobService.deleteJob(jobName, groupName);
return "任务删除成功";
} catch (SchedulerException e) {
e.printStackTrace();
return "任务删除失败";
}
}
}
8. 总结
通过本教程,你已经学会了如何将 Quartz 整合到 Spring Boot 中,并实现以下功能:
- 创建简单的定时任务。
- 使用
SimpleTrigger
和CronTrigger
实现时间间隔和复杂调度的任务。 - 通过
JobDataMap
向任务传递参数。 - 实现任务的持久化,并将任务信息保存到数据库中。
- 控制任务的并发执行,避免重复调度。
- 动态添加和删除定时任务。
Quartz 强大的调度功能与 Spring Boot 的简洁配置相结合,使得我们可以轻松构建复杂的任务调度系统。在实际应用中,你可以根据业务需求进行进一步的自定义和优化,构建一个高度可扩展的调度系统。
评论 (0)