IDEを使わずSpring Boot DevToolsのAutomatic Restartを実現する

今日は時間がなくて小ネタ。

Spring Bootにはspring-boot-devtoolsというものがあります。これをいれると./gradlew bootRunで起動中にソースコードを変更しても./gradlew bootRunを一度終了して再度起動するということをせずとも反映させることができます。これをAutomatic Restartといいます。

docs.spring.io

ここでは、以下の環境で試しました。

  • Spring Boot 2.6.3
  • Gradle 7.3.3

導入

build.gradledependenciesにて以下を追記するだけです。

    developmentOnly("org.springframework.boot:spring-boot-devtools")

EclipseとかのIDEとかを導入しているとこれだけでOKなようなのですが、IDEを使わずエディタだけで開発していた場合はこれだけではAutomatic Restartはうまく動きません。

実行

Automatic Restartをうまく動かすには、./gradlew bootRun以外に別のコンソールにて./gradlew -t classesを実行します。

ここでポイントは以下の二つです。

  • -tオプションを指定する
  • classesタスクを実行する

-tオプションは./gradlew -hを実行してみると以下のような説明です。

-t, --continuous                   Enables continuous build. Gradle does not exit and will re-execute tasks when task file inputs change.

-tオプションを指定した場合はタスクを終了してもプロンプトを返さず待機してファイルの変更を待ち受ける状態です。

classesタスクは以下に説明があります。

docs.gradle.org

あとはクラスパス内にあるJavaファイルやHTMLファイルを変更するとそれを検出して自動的にAutomatic Restartが動きます。 例えばPostsController.javaというプログラムを更新すると、./gradlew -t classesを実行している画面には以下のような表示が行われます。

BUILD SUCCESSFUL in 4s
2 actionable tasks: 1 executed, 1 up-to-date

Waiting for changes to input files of tasks... (ctrl-d to exit)
modified: /Users/hogehoge/work/app_modernization_example/webapp/src/main/java/com/example/webapp/PostsController.java
Change detected, executing build...

BUILD SUCCESSFUL in 486ms
2 actionable tasks: 1 executed, 1 up-to-date

Waiting for changes to input files of tasks... (ctrl-d to exit)

./gradlew bootRunを実行している画面はアプリケーションが再起動されていることが確認できます。

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.6.3)

2022-01-30 16:02:50.789  INFO 19683 --- [  restartedMain] com.example.webapp.WebappApplication     : Starting WebappApplication using Java 11.0.11 on hikari.local with PID 19683 (/Users/hogehoge/work/app_modernization_example/webapp/build/classes/java/main started by hogehoge in /Users/hogehoge/work/app_modernization_example/webapp)
2022-01-30 16:02:50.789  INFO 19683 --- [  restartedMain] com.example.webapp.WebappApplication     : No active profile set, falling back to default profiles: default
2022-01-30 16:02:50.899  INFO 19683 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JDBC repositories in DEFAULT mode.
2022-01-30 16:02:50.900  INFO 19683 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 0 ms. Found 0 JDBC repository interfaces.
2022-01-30 16:02:50.945  INFO 19683 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2022-01-30 16:02:50.946  INFO 19683 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-01-30 16:02:50.946  INFO 19683 --- [  restartedMain] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.56]
2022-01-30 16:02:50.953  INFO 19683 --- [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2022-01-30 16:02:50.953  INFO 19683 --- [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 162 ms
2022-01-30 16:02:51.001  INFO 19683 --- [  restartedMain] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
2022-01-30 16:02:51.078  INFO 19683 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Starting...
2022-01-30 16:02:51.091  INFO 19683 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Start completed.
2022-01-30 16:02:51.101  INFO 19683 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2022-01-30 16:02:51.106  INFO 19683 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2022-01-30 16:02:51.109  INFO 19683 --- [  restartedMain] com.example.webapp.WebappApplication     : Started WebappApplication in 0.34 seconds (JVM running for 35.92)
2022-01-30 16:02:51.110  INFO 19683 --- [  restartedMain] .ConditionEvaluationDeltaLoggingListener : Condition evaluation unchanged
2022-01-30 16:02:59.474  INFO 19683 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-01-30 16:02:59.475  INFO 19683 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2022-01-30 16:02:59.475  INFO 19683 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
aaa
2022-01-30 16:03:20.680  INFO 19683 --- [       Thread-7] com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Shutdown initiated...
2022-01-30 16:03:20.684  INFO 19683 --- [       Thread-7] com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Shutdown completed.

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.6.3)

2022-01-30 16:03:20.729  INFO 19683 --- [  restartedMain] com.example.webapp.WebappApplication     : Starting WebappApplication using Java 11.0.11 on hikari.local with PID 19683 (/Users/hogehoge/work/app_modernization_example/webapp/build/classes/java/main started by hogehoge in /Users/hogehoge/work/app_modernization_example/webapp)
2022-01-30 16:03:20.729  INFO 19683 --- [  restartedMain] com.example.webapp.WebappApplication     : No active profile set, falling back to default profiles: default
2022-01-30 16:03:20.814  INFO 19683 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JDBC repositories in DEFAULT mode.
2022-01-30 16:03:20.814  INFO 19683 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 0 ms. Found 0 JDBC repository interfaces.
2022-01-30 16:03:20.850  INFO 19683 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2022-01-30 16:03:20.851  INFO 19683 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-01-30 16:03:20.851  INFO 19683 --- [  restartedMain] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.56]
2022-01-30 16:03:20.858  INFO 19683 --- [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2022-01-30 16:03:20.858  INFO 19683 --- [  restartedMain] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 127 ms
2022-01-30 16:03:20.903  INFO 19683 --- [  restartedMain] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
2022-01-30 16:03:20.997  INFO 19683 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-3 - Starting...
2022-01-30 16:03:21.008  INFO 19683 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-3 - Start completed.
2022-01-30 16:03:21.017  INFO 19683 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2022-01-30 16:03:21.020  INFO 19683 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2022-01-30 16:03:21.023  INFO 19683 --- [  restartedMain] com.example.webapp.WebappApplication     : Started WebappApplication in 0.309 seconds (JVM running for 65.834)
2022-01-30 16:03:21.024  INFO 19683 --- [  restartedMain] .ConditionEvaluationDeltaLoggingListener : Condition evaluation unchanged
2022-01-30 16:03:24.452  INFO 19683 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2022-01-30 16:03:24.453  INFO 19683 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2022-01-30 16:03:24.453  INFO 19683 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
aaa1
<==========---> 80% EXECUTING [1m 18s]

自分が手元で試した感じ、ファイルを保存したらすぐさま変更が反映されるというわけではなく、数秒ぐらいの待ちが発生する感じがしましたが、いちいち自分で./gradlew bootRunを終了して再度実行する手間を考えると些細な待ち時間かなと思います。