本稿では、Spring BootアプリケーションにおいてQuartzを使用してスケジューリングタスクを実装する手法を詳述する。QuartzはJava環境で広く利用されているオープンソースの_jobスケジューリングライブラリであり、柔軟なcron式に基づく実行タイミングの制御が可能である。
依存関係の定義
Mavenプロジェクトにおいて、spring-boot-starter-quartz依存関係をpom.xmlに追加することでQuartz機能を利用できる。以下に設定例を示す。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/>
</parent>
<groupId>org.sample</groupId>
<artifactId>quartz-scheduler-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
タスク情報のデータ転送オブジェクト
スケジューリング対象タスクの各種属性を保持するDTOクラスを定義する。このクラスは_job_key、trigger_key、cron式などの情報を一元管理する役割を果たす。
package org.sample.dto;
import lombok.Data;
import org.quartz.Job;
@Data
public class ScheduledTaskInfo {
/**
* タスク識別名
*/
private String taskName;
/**
* タスクグループ識別子
*/
private String taskGroup;
/**
* トリガー識別名
*/
private String triggerName;
/**
* トリガーグループ識別子
*/
private String triggerGroup;
/**
* Cron実行式
*/
private String cronExpression;
/**
* 実装クラス完全修飾名
*/
private String className;
/**
* 現在のステータス
*/
private String status;
/**
* 次回実行予定時刻
*/
private String nextExecutionTime;
/**
* 前回実行完了時刻
*/
private String lastExecutionTime;
/**
* 拡張設定データ
*/
private String extendedData;
/**
* 実行ロジック実装クラス
*/
private Class<? extends Job> executionClass;
// ゲッター・セッターメソッドはLombokの@Dataアノテーションにより自動生成
}
スケジューラ設定クラス
Schedulerインスタンスの初期化および_job登録処理を実装するConfigクラスを定義する。@PostConstructアノテーションを付与することで、bean生成時に初期タスクが自動登録される。
package org.sample.config;
import lombok.RequiredArgsConstructor;
import org.quartz.*;
import org.sample.dto.ScheduledTaskInfo;
import org.sample.job.SampleJobTask;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@Configuration
@RequiredArgsConstructor
public class TaskSchedulerConfig {
private final Scheduler scheduler;
@Bean
public void initializeScheduledTasks() {
registerTaskDefinition(createTaskInfo());
}
private ScheduledTaskInfo createTaskInfo() {
ScheduledTaskInfo info = new ScheduledTaskInfo();
info.setTaskName("sampleTask");
info.setTaskGroup("sampleTaskGroup");
info.setTriggerGroup("sampleTriggerGroup");
info.setExecutionClass(SampleJobTask.class);
info.setTriggerName("sampleTaskTrigger");
info.setCronExpression("0/5 * * * * ?");
return info;
}
private void registerTaskDefinition(ScheduledTaskInfo taskInfo) {
JobKey jobKey = JobKey.jobKey(taskInfo.getTaskName(), taskInfo.getTaskGroup());
JobDetail jobDetail = JobBuilder.newJob(taskInfo.getExecutionClass())
.withIdentity(jobKey)
.storeDurably()
.build();
TriggerKey triggerKey = TriggerKey.triggerKey(taskInfo.getTriggerName(), taskInfo.getTriggerGroup());
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(triggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(taskInfo.getCronExpression())
.withMisfireHandlingInstructionDoNothing())
.build();
try {
if (scheduler.checkExists(jobKey)) {
scheduler.deleteJob(jobKey);
}
scheduler.scheduleJob(jobDetail, trigger);
} catch (SchedulerException e) {
throw new RuntimeException("タスク登録中にエラーが発生しました", e);
}
}
}
タスク実装クラス
QuartzJobBeanを拡張し、実際の実行ロジックを実装する。このクラスはSpring管理のbeanであるため、依存性注入を活用したビジネスロジックの呼び出しが可能である。
package org.sample.job;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.sample.service.TaskExecutionService;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Slf4j
@RequiredArgsConstructor
public class SampleJobTask extends QuartzJobBean {
private final TaskExecutionService executionService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
log.info("{} - Quartzタスクを実行中...", timestamp);
try {
executionService.performScheduledOperation();
} catch (Exception e) {
log.error("タスク実行エラー", e);
throw new JobExecutionException(e);
}
}
}
ビジネスサービスクラス
タスクから呼び出されるサービス層を実装する。Springの@Serviceアノテーションにより管理beanとして登録される。
package org.sample.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Slf4j
@Service
public class TaskExecutionService {
public void performScheduledOperation() {
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
log.info("{} - サービス層ビジネスロジックを実行中...", timestamp);
}
}
実行タイミングの動的更新
スケジューリングされたタスクの実行間隔は、アプリケーション稼働中に動的に変更可能である。以下にトリガーを再設定するメソッドの実装を示す。
package org.sample.config;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RequiredArgsConstructor
public class TaskScheduleUpdater {
private final Scheduler scheduler;
/**
* 指定されたタスクのcron式を更新する
* @param targetTaskName 更新対象タスク名
* @param targetGroup タスクグループ名
* @param newCronExpression 新的cron式
*/
public void updateTaskSchedule(String targetTaskName, String targetGroup, String newCronExpression) {
String triggerName = targetTaskName + "Trigger";
String triggerGroup = targetGroup + "TriggerGroup";
TriggerKey targetTriggerKey = TriggerKey.triggerKey(triggerName, triggerGroup);
try {
Trigger existingTrigger = scheduler.getTrigger(targetTriggerKey);
if (existingTrigger == null) {
log.warn("指定されたトリガーが見つかりません: {}", targetTriggerKey);
return;
}
Trigger newTrigger = TriggerBuilder.newTrigger()
.withIdentity(targetTriggerKey)
.withSchedule(CronScheduleBuilder.cronSchedule(newCronExpression)
.withMisfireHandlingInstructionDoNothing())
.build();
scheduler.rescheduleJob(targetTriggerKey, newTrigger);
log.info("タスク '{}' の実行スケジュールを更新しました。新的cron式: {}", targetTaskName, newCronExpression);
} catch (SchedulerException e) {
log.error("スケジュール更新中にエラーが発生しました", e);
throw new RuntimeException("スケジュール更新失敗", e);
}
}
/**
* タスクの一時停止
*/
public void pauseTask(String taskName, String taskGroup) {
try {
JobKey jobKey = JobKey.jobKey(taskName, taskGroup);
scheduler.pauseJob(jobKey);
log.info("タスクを一時停止しました: {}", jobKey);
} catch (SchedulerException e) {
log.error("タスク一時停止エラー", e);
}
}
/**
* タスクの再開
*/
public void resumeTask(String taskName, String taskGroup) {
try {
JobKey jobKey = JobKey.jobKey(taskName, taskGroup);
scheduler.resumeJob(jobKey);
log.info("タスクを再開しました: {}", jobKey);
} catch (SchedulerException e) {
log.error("タスク再開エラー", e);
}
}
}
アプリケーション設定ファイル
application.ymlにおいてQuartzの動作特性を設定する。JDBC永続化を使用する場合、データベーススキーマの自動初期化を有効化する。
server:
port: 8080
spring:
datasource:
url: jdbc:postgresql://localhost:5432/scheduler_db
username: scheduler_user
password: secure_password
driver-class-name: org.postgresql.Driver
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: Asia/Tokyo
serialization:
write-dates-as-timestamps: false
quartz:
auto-startup: true
job-store-type: jdbc
jdbc:
initialize-schema: always
properties:
org:
quartz:
threadPool:
threadCount: 5
threadPriority: 5
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
tablePrefix: qrtz_
useProperties: false
まとめ
本稿ではSpring Boot環境におけるQuartzスケジューラの基本的な統合手法を解説した。spring-boot-starter-quartzを使用することで、複雑な設定なしに_quartz機能をアプリケーションに統合できる。スケジューラの動的更新機能を組み合わせることで、 runtimeにおけるタスク管理柔軟なシステムの構築が可能となる。