Spring boot -- Hot swapping for idea--

f:id:Naotsugu:20150110034558p:plain

Hot swapping を利用するには Spring Loaded が使える。

Spring Loaded

Spring Loaded はJVMの停止なしにクラスファイルの変更を行う hot code replace ツール

デバック実行時にメソッドの中身を書き換える hot swap では、メソッドの中身の書き換えだけしかできない。 Spring Loaded では メソッドやフィールド、コンストラクタの追加/変更/削除ができる。

Hot Swap ? Hot Deploy?

Hot Swap は java 1.4 で Java Platform Debugger Architecture (JPDA) としてJVMに追加された機能。Debug モードで起動した JVM に外部から変更コードを送り込んでクラスのメソッドの中身を書き換えられる IDEデバッグモードでよくある例のやつ。クラス構造を壊すようなメソッド追加やフィールド追加などはできず、あくまでメソッドボディのみが書き換え対象となる。

S2界隈の Hot Deploy とか アプリケーションコンテナの Hot Reload とか言われるのは、クラスローダを破棄することでクラスローダ配下のクラスをアンロード対象として、別バージョンのクラスを読み込む。S2 の Hot Deploy なんかは、リクエスト毎にクラスローダを破棄してたりする。 アプリケーションが必要な全クラスまるっと読み直すので、初期化のコストなどがネックになることがある。

Spring Loaded では

Instrumentation API を使って、クラスのロード時に agent をかまして、後のリロードに備えるように ASM 使ってバイトコード書き換えてる。 あらかじめディスパッチ用のバイトコードを仕込んでおいて、リロード時にディスパッチ先を変えてあげているみたい。

java -javaagent:<pathTo>/springloaded-{VERSION}.jar -noverify SomeJavaClass

-javaagent 指定して、かつ -noverify(JVM側でクラスファイルの検証を無効)と。

・・・
Premain-Class: org.springsource.loaded.agent.SpringLoadedAgent
Agent-Class: org.springsource.loaded.agent.SpringLoadedAgent
Can-Redefine-Classes: true

MANIFEST.MF で指定されている agent はorg.springsource.loaded.agent.SpringLoadedAgent。実装みるとかなりしんどそう。

話が脇道にそれたので戻すと

Gradle の設定

こんな感じでビルドスクリプトに springloaded の依存追加する。

buildscript {
    repositories {
        maven { url "https://repo.spring.io/libs-release" }
        mavenLocal()
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.1.RELEASE")
        classpath("org.springframework:springloaded:1.2.0.RELEASE")
    }
}

Gradle と IntelliJ で開発する場合には、IntelliJ による class ファイルの出力先を Gradle のそれに合わせるため、idea プラグインの設定を変更する。

apply plugin: 'idea'

idea {
    module {
        inheritOutputDirs = false
        outputDir = file("$buildDir/classes/main/")
    }
}

Intellj の設定

Intellij の設定で、Make Project Automatically を有効にする。

f:id:Naotsugu:20150112191955p:plain

以下で起動すると、編集が即座に反映される。

gradle bootRun

静的ファイル

静的コンテンツのテンプレートのキャッシュ設定を変更。

  • Thymeleafテンプレート - spring.thymeleaf.cache を false
  • FreeMarkerテンプレート - spring.freemarker.cache を false
  • Groovyテンプレート - spring.groovy.template.cache を false
  • Velocityテンプレート - spring.velocity.cache を false