CLOVER🍀

That was when it all began.

Scala×Spring Bootで、Spring Loadedしてみる

Spring Bootで、コンパイル後のクラスの変更を反映するHot Reloadingというものができるという、Spring Loadedを試してみました。

Scalaで。

Spring Boot+Spring Loaded

用意したpom.xmlはこのような感じ。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.littlewings</groupId>
  <artifactId>spring-boot-loaded</artifactId>
  <packaging>jar</packaging>
  <version>0.0.1-SNAPSHOT</version>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.1.RELEASE</version>
  </parent>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>        
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-library</artifactId>
      <version>${scala.version}</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <dependencies>
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
            <version>1.2.1.RELEASE</version>
          </dependency>
        </dependencies>
      </plugin>

      <plugin>
        <groupId>net.alchim31.maven</groupId>
        <artifactId>scala-maven-plugin</artifactId>
        <version>3.2.0</version>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>testCompile</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <scalaVersion>${scala.version}</scalaVersion>
          <args>
            <arg>-Xlint</arg>
            <arg>-unchecked</arg>
            <arg>-deprecation</arg>
            <arg>-feature</arg>
          </args>
          <recompileMode>incremental</recompileMode>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <scala.major.version>2.11</scala.major.version>
    <scala.version>${scala.major.version}.5</scala.version>
  </properties>
</project>

Spring BootのMavenプラグインの依存関係に、Spring Loadedが入ることがポイントみたいです。

      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <dependencies>
          <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
            <version>1.2.1.RELEASE</version>
          </dependency>
        </dependencies>
      </plugin>

あとは、アプリケーションを実装します。Spring MVCで。

アプリケーションのエントリポイント。
src/main/scala/org/littlewing/spring/loaded/App.scala

package org.littlewings.spring.loaded

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

object App {
  def main(args: Array[String]): Unit =
    SpringApplication.run(classOf[App], args: _*)
}

@SpringBootApplication
class App

RestController。
src/main/scala/org/littlewing/spring/loaded/HelloController.scala

package org.littlewings.spring.loaded

import org.springframework.web.bind.annotation.{ RequestMapping, RestController }

@RestController
class HelloController {
  @RequestMapping(Array("/message"))
  def message: String =
    "Hello World!!"
    // "こんにちは、世界!!"
}

すでにコメントアウトが見えていますが、これをHot Reloadingで変更を反映してみます。

アプリケーションを起動。

$ mvn spring-boot:run

動作確認。

$ curl http://localhost:8080/message
Hello World!!

Hello World!!」が返ってきました。

次に、RestControllerのソースコードを修正して

  @RequestMapping(Array("/message"))
  def message: String =
    // "Hello World!!"
    "こんにちは、世界!!"

アプリケーションを起動したまま、別のターミナルでコンパイル

$ mvn compile

すると、起動中のSpring Bootアプリケーションが変更を検知するようで

2015-01-31 00:27:00.417  INFO 91345 --- [Loader@58644d46] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/message],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.lang.String org.littlewings.spring.loaded.HelloController.message()
2015-01-31 00:27:00.419  INFO 91345 --- [Loader@58644d46] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[text/html],custom=[]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
2015-01-31 00:27:00.420  INFO 91345 --- [Loader@58644d46] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)

確認してみると、変更が反映されています。

$ curl http://localhost:8080/message
こんにちは、世界!!

できましたね。

継続的コンパイル

先ほどは、もうひとつのターミナルで「mvn compile」を実行しましたが、今度はscala-maven-pluginにソース変更を検知してもらい、自動的にコンパイルされるようにしてみます。

Continuous Compilation of Scala sources
http://davidb.github.io/scala-maven-plugin/example_cc.html

scala:cc
http://davidb.github.io/scala-maven-plugin/cc-mojo.html

起動してみます。

$ mvn scala:cc

このまま、ターミナルに制御が戻らず、ソースコードの変更を待っている状態になります。

[INFO] wait for files to compile...

終了する時は、Ctl-Cなどで。

あとは、ソースコードを変更すればそれを検知して、自動的にコンパイルしてくれます。その後は、再びソースコードの変更待ちとなります。

なお、デフォルトではfscを使用するみたいで、fscを使わない場合はこうするらしいです。

$ mvn scala:cc -Dfsc=false

なんですが、なんか変化が見えないなーと思ったら、インクリメンタルコンパイルモードだとfscを使わないようになっているみたいです。
https://github.com/davidB/scala-maven-plugin/blob/3.2.0/src/main/java/scala_maven/ScalaContinuousCompileMojo.java#L133

pomでrecompileModeをコメントアウトして

          <!-- <recompileMode>incremental</recompileMode> -->

普通に起動すると、

$ mvn scala:cc

fscを使うよ、とメッセージが表示されるようになり

[INFO] use fsc for compilation

「-Dfsc=false」にするとメッセージが出なくなりました。

$ mvn scala:cc -Dfsc=false

なるほど。