Spring Boot: Scheduledアノテーションを使用して、スケジュールされたタイミングでメソッドを実行する

Scheduledアノテーションを使うと、スケジュールされたタイミングでメソッドを実行することができます。 これで周期実行的なものは、簡単に実装できます。

実装方法

実行したいメソッドにScheduledアノテーションを付けます。

@Component
public class Scheduler {

    @Scheduled(fixedRate = 5000)
    public void doSomething() {
        // 5秒周期で行いたい処理
    }
}

Scheduledアノテーションによる実行を有効とするためには、EableSchedulingアノテーションを付けます。

@SpringBootApplication
@EnableScheduling
public class SchedulerApplication {

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

これだけで周期実行が実装できます。簡単ですね!

Scheduledで指定できるパターン

指定方法は大きく分けて3パターン用意されています。

  1. fixedDelay : 前回タスクの実行完了時点から指定時間後にタスクを実行する。単位はms。
  2. fixedRate : 前回タスクの実行開始時点から指定時間後にタスクを実行する。単位はms。
  3. cron : 指定した周期でタスクを実行する。(Linuxのcronと似た書式)

また、fixedDelayfixedRateでは、初回のタスクの実行開始時間を指定するものとして、initialDelayがあります。

下記のようにすると、初回はアプリケーション起動から30秒後に実行され、その次からは、前回タスクの実行完了から60秒後に実行されることになります。

@Scheduled(fixedDelay = 60000, initialDelay = 30000)
public void doSomething() {
}

cronでは、zoneというフィールドで、cronの起動時間のタイムゾーンを指定できます。(未指定の場合は、デフォルトのタイムゾーン)

下記のようにすると、東京のタイムゾーンで、8時と9時と10時に実行されます。(8時から10時の間で、0秒、0分のタイミングで実行といった指定になっている)

@Scheduled(cron = "0 0 8-10 * * *", zone = "Asia/Tokyo")
public void doSomething() {
}

設定ファイルで指定する

ソース上に固定値で設定するのではなく、設定ファイルに記載することができます。

${設定名}で指定しておいて、

@Scheduled(cron = "${scheduler.cron}")
public void doSomething() {
}

application.properties で設定値を書きます。

scheduler.cron=*/5 * * * * *

fixedDelayfixedRateinitialDelayも、設定値とできるように、fixedDelayStringfixedRateStringinitialDelayStringというものがフィールドとしてあるので、そちらを使えばOKです。

@Scheduled(fixedRateString = "${scheduler.fixed-rate}")
public void doFixedRateString() throws InterruptedException {
}

ユニットテストの際に、周期実行を抑止する

テストの時には、勝手に周期実行が動いて欲しく無い場合もあるかと思います。

fixedDelayStringfixedRateString ならば、テスト時の設定値を変えてとても大きな数字にしておけば、抑止と同等のことができそうですが、cronだと、ぜったいに実行されないタイミングを指定するのは難しそうです。

こういった場合は、EnableSchedulingが指定されないように設定してあげれば回避できます。

下記のようなクラスでSpring Bootを起動しているならば、

@SpringBootApplication
@EnableScheduling
public class SchedulerApplication {

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

テスト用に別の起動クラスを作成し、そちらでは@EnableSchedulingを指定しないようにします。 また、コンポーネントスキャンで、@EnableSchedulingを指定しているものを拾ってしまうと有効になってしまうので、該当のクラスを除外するように指定しておきます。

@SpringBootApplication
@ComponentScan(excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = SchedulerApplication.class))
public class TestApplication {

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

このクラスを@SpringBootTestに指定してあげれば、@EnableSchedulingが指定されずに、周期実行を抑止することが出来ます。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = SchedulerTest.TestApplication.class)
public class SchedulerTest {