Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
13
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

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

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 に書いておくと楽かも

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
13
Help us understand the problem. What are the problem?