LoginSignup
13
14

More than 5 years have passed since last update.

Spring Boot でコマンドラインツールを作る個人的ベストプラクティス

Posted at

TL; DR

  • CommandLineRunner は使うけど、CommandLineRunner#run(String... args) は、Application 起動完了前に呼び出されてしまうため、初期化に読み替えて、サブクラスでオーバーライドする
  • 複数のコマンドは @ConditionalOnProperty(name = "command", havingValue = "xxx") で起動し分けるとよさげ。

Motivation

Spring boot 、Web サーバーをさくっと上げるところから本格的なサービスにも使えて、Batch もある程度あります。

Web サービスに使ってるロジックや、Batch 用ロジックなんかを手元で実行したかったり(何かおこったときの再実行とか。。。。)にそのまま実行できる .jar を作っておくと便利なのですが、やり方が色々あって悩む事多しの状況だったのでメモです。

Sampleコード全部

package echo;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@SpringBootApplication
public class EchoApplication {
    public abstract static class Command implements CommandLineRunner {
        public final void run(final String... args) throws Exception {
            log.debug("Initialize started on {}", getClass());
            // 実際、CommandLineRunner#run(String... args) は、Application 起動完了前に呼び出されてしまう。
            // ここでは初期化に読み替えて、サブクラスでオーバーライドする
            init(args);
        }

        void init(final String... args) throws Exception {
            // 生引数を引き取って何かする
        }

        /**
         * サブクラスで実装
         * @throws Exception
         */
        abstract void execute() throws Exception;
    }

    public static void main(final String... args) throws Exception {
        try (ConfigurableApplicationContext context = SpringApplication.run(EchoApplication.class, args)) {
            context.getBean(Command.class).execute();
            // Command を取って実行
        }
    }

    @Service
    @ConditionalOnProperty(name = "command", havingValue = "foo")
    public static class FooFeature extends Command {
        @Override
        void execute() throws Exception {
            log.info("Foo");
        }
    }

    @Service
    @ConditionalOnProperty(name = "command", havingValue = "bar")
    public static class BarFeature extends Command {
        @Override
        void execute() throws Exception {
            log.info("Bar");
        }
    }
}

実行するとこんな感じに

% java -jar xxxx.jar --command=foo
2017-09-09 20:58:48.464  INFO 20274 --- [           main] echo.EchoApplication      : Started EchoApplication in 2.895 seconds (JVM running for 3.342)
2017-09-09 20:58:48.464  INFO 20274 --- [           main] echo.EchoApplication      : Foo

% java -jar xxxx.jar --command=bar
2017-09-09 20:58:48.464  INFO 20274 --- [           main] echo.EchoApplication      : Started EchoApplication in 2.895 seconds (JVM running for 3.342)
2017-09-09 20:58:48.464  INFO 20274 --- [           main] echo.EchoApplication      : Bar

その他

WebApp とまとめる

WebApp, CommandLine Tool をまとめたいときは、command line tool として実行するときは
--spring.main.web-environment=false で Web Context が起動しなくなります。

command profile を作って application.yml に書いておくと楽かも

13
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
14