CommandLineRunnerでバッチ処理を実装する

Spring BootでWebアプリケーションを作っている際、次のような時にバッチ処理を検討することがあると思います。
・cronなどで定期的に処理を実行したい
・シェルで実行したい
・サーバ側で時間のかかる処理を非同期で行いたい

CommandLineRunnerを使うことで、Spring Bootでバッチ処理のようなものを実装することができるという事で、調べてみました。
ということで今回は、CommandLineRunnerの実装備忘録です。

CommandLineRunnerの利用方法

CommandLineRunnerを実装したBeanを作成することで、名前の通り、関数をコマンドとして実行することができます。

SampleJobA.java

@Component
public class SampleJobA implements CommandLineRunner {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void run(String... args) throws Exception {
        logger.info("処理Aが実行されました。");
    }
}

Beanである必要がある為、@Componentアノテーションを付与する必要があります。
CommandLineRunnerは関数型インタフェースとして定義されており、抽象メソッドrunが定義されています。
runはBeanが実行された時の入り口になるため、オーバーロードして実行したい処理を記述します。

jarファイルを生成することで、以下のように実行することができます。

$ java -jar batchdemo-0.0.1.jar
・・・
INFO 7080 --- [           main] com.example.batchdemo.job.SampleJobA     : 処理Aが実行されました。

複数のCommandLineRunnerの実装

一つのモジュールで複数のバッチ処理を実装したい場合、プロパティで実行対象のBeanを切り替えることができます。

SampleJobA

@Component
@ConditionalOnProperty(value = { "batch.execute" }, havingValue = "sampleA")
public class SampleJobA implements CommandLineRunner {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void run(String... args) throws Exception {
        logger.info("処理Aが実行されました。");
    }
}

SampleJobB

@Component
@ConditionalOnProperty(value = { "batch.execute" }, havingValue = "sampleB")
public class SampleJobB implements CommandLineRunner {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void run(String... args) throws Exception {
        logger.info("処理Bが実行されました。");
    }
}

@ConditionalOnPropertyアノテーションを付与することで、プロパティの値によって実行対象を切り替えることができます。

$ java -jar batchdemo-0.0.1.jar --batch.execute=sampleA
・・・
INFO 7104 --- [           main] com.example.batchdemo.job.SampleJobA     : 処理Aが実行されました。
$ java -jar batchdemo-0.0.1.jar --batch.execute=sampleB
・・・
INFO 7105 --- [           main] com.example.batchdemo.job.SampleJobB     : 処理Bが実行されました。

以下のようにプロパティファイルに記述しても良いかもしれません。
application.properties

batch.execute=sampleB

spring-boot-starter-webを依存関係に追加する場合

CommandLineRunnerの実装に必要な依存関係はspring-boot-starterだけです。
build.gradle

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

しかし依存関係の都合上、spring-boot-starter-webを依存関係に追加する必要がある場合、 build.gradle

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
}

としてしまうとWebアプリケーションとして動作する為、実行時に組み込みwebサーバのtomcatが起動してしまいます。

・・・
INFO 7160 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http)
・・・
INFO 7160 --- [           main] com.example.batchdemo.job.SampleJobB     : 処理Bが実行されました。

このような場合、main関数で以下のように設定することで、非Webアプリケーションとして起動することが可能です。 ※Spring Boot 2.0〜 BatchdemoApplication.java

@SpringBootApplication
public class BatchdemoApplication {

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

        SpringApplication app = new SpringApplication(BatchdemoApplication.class);
        app.setWebApplicationType(WebApplicationType.NONE);
        app.run(args);
    }
}

WebApplicationType.NONEを指定することで、非Webアプリケーションとして起動することが可能で、この場合、組み込みのwebサーバは起動しません。

今回の内容は以上です。

最後に採用の宣伝をさせてください

アソビューでは、一緒に働いてもらえる仲間を募集中です! ゲストにワクワクを届けるため、最高のプロダクトを作っていこう!というマインドをもったメンバーがたくさん働いてます。 話を聞いてみたい!という方ははお気軽にご連絡ください。

www.wantedly.com

www.wantedly.com