⏰ Spring Framework 講座【第32回】定期処理を自動実行!〜スケジューリング(@Scheduled)の活用〜

docs

前回、非同期処理を利用して、アプリケーションの応答性を向上させる方法を学びました。

しかし、アプリケーションの処理の中には、ユーザーのリクエストとは関係なく、「毎日深夜3時に実行する」「5分おきに実行する」といった定期的な自動処理が必要なものがあります(これをバッチ処理と呼ぶこともあります)。

今回は、Spring Bootで、このような**スケジューリング(定期実行)**処理を非常に簡単に実現する @Scheduled アノテーションの使い方を学びます。


1. スケジューリング機能の有効化:@EnableScheduling

非同期処理と同様に、スケジューリング機能を利用するためには、まずSpring Bootにその機能を有効にするよう伝えます。

メインクラス、または任意の @Configuration クラスに @EnableScheduling アノテーションを付与するだけです。

Java

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling; // ① これを追加

@SpringBootApplication
@EnableScheduling // ② スケジューリング機能を有効化
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

2. スケジュール実行の定義:@Scheduled

定期的に実行したいメソッドに @Scheduled アノテーションを付与します。このメソッドは、引数を取らず、戻り値がない(void)のが一般的です。

@Scheduled は、以下の3つの主要な実行指定方法を提供します。

2-1. 固定遅延(fixedDelay)

前回の処理が完了した時点から、指定された時間が経過後に次の処理を開始します。

  • 用途: 前回の処理が終わるまで次の処理を開始したくない、確実に間隔を空けたい場合に最適です。

Java

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledTasks {

    // 処理が完了した後、5000ミリ秒(5秒)後に再度実行
    @Scheduled(fixedDelay = 5000)
    public void executeAfterDelay() {
        System.out.println("【fixedDelay】処理を実行しました: " + System.currentTimeMillis() / 1000);
        // ここに時間のかかる処理(例:外部API連携など)が入る
    }
}

2-2. 固定レート(fixedRate)

前回の処理が開始した時点から、指定された時間が経過後に次の処理を開始します。

  • 用途: 処理時間が長くなっても、実行間隔を一定に保ちたい場合に最適です。ただし、前回の処理が終わる前に次が開始される可能性がある点に注意が必要です。

Java

// 処理が開始した後、10000ミリ秒(10秒)後に再度実行
@Scheduled(fixedRate = 10000)
public void executeAtFixedRate() {
    System.out.println("【fixedRate】処理を実行しました: " + System.currentTimeMillis() / 1000);
}

2-3. Cron式(Cron Expression)

最も柔軟性が高く、複雑なスケジュールを設定できます。

  • 用途: 「毎日午前2時30分」「毎週月曜日の朝8時」など、特定の日時に実行したい場合に最適です。

Cron式の書式は、以下の6つの要素で構成され、半角スペースで区切ります。

秒 | 分 | 時 | 日 | 月 | 曜日

位置許容値意味
0-590毎分0秒に実行
0-5930毎時30分に実行
0-232午前2時に実行
1-31*毎日実行
1-12*毎月実行
曜日0-7 (0,7は日曜日)MON-FRI月曜から金曜まで

【Cron式の例】

Java

// 毎日深夜2時30分0秒に実行
@Scheduled(cron = "0 30 2 * * *")
public void executeDailyBackup() {
    System.out.println("【Cron】日次バックアップを実行します。");
}

// 毎週日曜日の午前6時0分0秒に実行
@Scheduled(cron = "0 0 6 * * 0")
public void executeWeeklyReport() {
    System.out.println("【Cron】週次レポート処理を実行します。");
}

3. スケジューリングと非同期処理の連携

@Scheduled が付いたメソッドは、基本的に単一のスレッドで順番に実行されます。そのため、もし処理時間が長いメソッドが複数あると、後続の処理が前の処理の完了を待ってしまい、遅延が発生します。

この問題を解決するため、@Scheduled メソッドに @Async を併用することが有効です。

Java

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;

// fixedRate で実行間隔を厳密にしたいが、処理時間が長い場合

@Async // このメソッドを別のスレッドプールで実行
@Scheduled(fixedRate = 10000) // 10秒おきに開始
public void executeLongRunningTask() {
    // 処理時間が5秒を超える場合でも、@Asyncのおかげで、
    // 次のタスクは前のタスクの完了を待たずに10秒後に開始できる
    System.out.println("【Async & Scheduled】重いタスクを実行中...");
    try {
        Thread.sleep(8000); // 8秒かかる処理
    } catch (InterruptedException e) {}
    System.out.println("【Async & Scheduled】タスク完了。");
}

このように併用することで、スケジューリングの正確さを保ちつつ、実行をバックグラウンドに任せることができ、システム全体の効率が向上します。

✅ 本日のまとめ

  • スケジューリングは、ユーザーのリクエストとは無関係に、定期的な処理を自動実行する機能である。
  • @EnableScheduling を設定クラスに付与して、機能を有効化する。
  • @Scheduled を使って実行タイミングを定義する。主な指定方法は以下の3つである。
    • fixedDelay: 前回の終了から一定時間後。
    • fixedRate: 前回の開始から一定時間後。
    • cron: 特定の日時を指定する(最も柔軟性が高い)。
  • @Scheduled@Async を併用することで、重いタスクが他の定期実行タスクの遅延を引き起こすのを防ぐことができる。

🔔 次回予告

非同期処理とスケジューリングを習得し、アプリケーションの性能自動化が進みました。次は、Webアプリケーションに不可欠な外部連携のテクニックに進みます。

次回は、外部のAPI(Webサービス)と連携するために、HTTPリクエストを送信する際の標準的なクライアントである RestTemplate の代わりとして推奨されている WebClient の使い方を学びます。

次回:【第33回】外部APIとの連携(WebClientの基本) にご期待ください!

コメント

タイトルとURLをコピーしました