[Spring Boot][Java] アノテーション@Scheduledを使用してタスクを定期実行する

はじめに

日次処理でJarを定期実行する場合にはcronを使ったりしますが、Spring Bootのアノテーション「scheduling」を使用した方法について調べた事を備忘録として書いておきます。

環境

Mac OSX 10.10.5 Yosemite
Eclipse Mars2 4.5.2
Java 1.8.0_91

@Scheduledについて

Spring | Scheduling Tasks

必要な環境

・JDK1.8以上
・Gradle2.3以上、もしくはMaven3.0以上

使用方法

方法は以下の2つが存在する様です。
①アノテーションに記述
②設定ファイル(YAML)を使用

パラメータ

34. Task Execution and Scheduling

句・引数 説明
fixedDelay メソッドの前回の実行完了時刻から何ミリ秒後に実行するか。
fixedRate メソッドの前回の実行開始時刻から何ミリ秒後に実行するか。
initialDelay メソッドの初回実行時の待機時間。
cron cronを記述してスケジュールを設定。
zoneを使用して時間帯の指定も可能。

コード

build.gradle(依存関係)

lombokだけ追加しました。

dependencies {
	compile('org.springframework.boot:spring-boot-starter')
	compileOnly('org.projectlombok:lombok')
	testCompile('org.springframework.boot:spring-boot-starter-test')
}

方法①②共通のコード

@EnableSchedulingを追記。

package com.schedule;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class SchedulingApplication {

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

}

方法① アノテーションに記述

スケジュールで実行するクラス: Run

メソッド execute() をアプリケーション起動時に実行し、移行は3秒後ごとに実行する様にしました。
SimpleDateFormat を使用して実行時刻も表示します。

package com.schedule;

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

@Component
public class Run {

	private int i = 0;
	private static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");

	@Scheduled(initialDelay=0, fixedDelay=3000)
	public void execute() {
		System.out.println("実行回数: " + ++i + ", 実行時間: " + sdf.format(new Date()));
	}
	
}

実行結果


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.3.RELEASE)

2017-01-14 17:28:19.725  INFO 26029 --- [           main] com.schedule.SchedulingApplication       : Starting SchedulingApplication on HL00094-2 with PID 26029 (/Users/takahara.reo/Documents/workspace_eclipse_spring/Scheduling/bin started by takahara.reo in /Users/takahara.reo/Documents/workspace_eclipse_spring/Scheduling)
2017-01-14 17:28:19.730  INFO 26029 --- [           main] com.schedule.SchedulingApplication       : No active profile set, falling back to default profiles: default
2017-01-14 17:28:19.790  INFO 26029 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@149e0f5d: startup date [Sat Jan 14 17:28:19 JST 2017]; root of context hierarchy
2017-01-14 17:28:20.627  INFO 26029 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-01-14 17:28:20.636  INFO 26029 --- [           main] s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
実行回数: 1, 実行時間: 17:28:20
2017-01-14 17:28:20.647  INFO 26029 --- [           main] com.schedule.SchedulingApplication       : Started SchedulingApplication in 1.373 seconds (JVM running for 2.99)
実行回数: 2, 実行時間: 17:28:23
実行回数: 3, 実行時間: 17:28:26
.
.
.

方法② 設定ファイル(YAML)を使用

設定ファイル: application.yml

src/main/resources/に配置しました。
アスタリスクの意味は左から、「秒/分/時/日/月/曜日」となっています。
cron1は時刻が0秒で実行。
cron2は時刻が1秒か31秒で実行。

# 秒/分/時/日/月/曜日
cron:
  cron1: 0 * * * * *
  cron2: 1,31 * * * * *

設定ファイルを読み込むクラス

package com.schedule;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Component
@ConfigurationProperties(prefix="cron")
public class CronSetting {
	private String cron1;
	private String cron2;
}

Setter/GetterはLombokで設定。

スケジュールで実行するクラス: Run

以下に書き換えました。
メソッド execute1() は、cron1を参照するので時刻が0秒で実行。
メソッド execute2() は、cron2を参照するので時刻が1秒か31秒で実行

package com.schedule;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class Run {

	@Autowired
	CronSetting cron;
	private int i1 = 0;
	private int i2 = 0;
	private static final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");

	@Scheduled(cron="${cron.cron1}")
	public void execute1() {
		System.out.println("cron1 実行回数: " + ++i1 + ", 実行時間: " + sdf.format(new Date()));
	}

	@Scheduled(cron="${cron.cron2}")
	public void execute2() {
		System.out.println("cron2 実行回数: " + ++i2 + ", 実行時間: " + sdf.format(new Date()));
	}

}

①との違いは、 @Autowired で CronSetting を接続している事と、各メソッドの @Scheduled で CronSetting のcronを読み込んでいる事です。

実行結果


  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.3.RELEASE)

2017-01-14 17:29:14.813  INFO 26032 --- [           main] com.schedule.SchedulingApplication       : Starting SchedulingApplication on HL00094-2 with PID 26032 (/Users/takahara.reo/Documents/workspace_eclipse_spring/Scheduling/bin started by takahara.reo in /Users/takahara.reo/Documents/workspace_eclipse_spring/Scheduling)
2017-01-14 17:29:14.816  INFO 26032 --- [           main] com.schedule.SchedulingApplication       : No active profile set, falling back to default profiles: default
2017-01-14 17:29:14.904  INFO 26032 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@149e0f5d: startup date [Sat Jan 14 17:29:14 JST 2017]; root of context hierarchy
2017-01-14 17:29:15.960  INFO 26032 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-01-14 17:29:15.970  INFO 26032 --- [           main] s.a.ScheduledAnnotationBeanPostProcessor : No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
2017-01-14 17:29:15.987  INFO 26032 --- [           main] com.schedule.SchedulingApplication       : Started SchedulingApplication in 1.653 seconds (JVM running for 2.807)
cron2 実行回数: 1, 実行時間: 17:29:31
cron1 実行回数: 1, 実行時間: 17:30:00
cron2 実行回数: 2, 実行時間: 17:30:01
cron2 実行回数: 3, 実行時間: 17:30:31
cron1 実行回数: 2, 実行時間: 17:31:00
cron2 実行回数: 4, 実行時間: 17:31:01
cron2 実行回数: 5, 実行時間: 17:31:31
.
.
.

さいごに

かなり簡単に設定できました。実際に使用する際にアプリケーションがクラッシュした場合を考慮すると、サーバ内でアプリケーションのJarをcronで起動するのが良いとも考えますが、使い方次第でしょうか。