Spring Bootで自分用テンプレートプロジェクトを作ってみた


https://github.com/eiryu/spring-boot-template

結構前に作っていたけど書いてなかったので、せっかくだし書いておく。こだわりを以下に列挙。

## ビルド

今グイグイ来てるGradleにしてみた。色々見てると、Mavenでやった方がハマりどころは少なそうではあるが。
Gradle Wrapperの設定もしてあるので、ビルドは楽だと思う。
Mavenに比べてビルドは遅いので、`--daemon`付けたり、ホットデプロイ駆使する必要がある。


## 設定ファイル

YAMLでやる方法もあるみたいだが、自分は`application.properties`でやることにした。

## Thymeleafの設定

キャッシュオフとテンプレートモードをLEGACYHTML5にする設定をした。尚、このためにnekohtmlを依存性に追加。


```
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.cache=false
```

LEGACYHTML5ならテンプレートをxhtmlにする必要がない。（HTML5だとxhtmlとして正しくないとエラーが発生する。。）
最初はHTML5でやっていたのだが、結局デザイナーがxhtmlにし忘れたところを直したり、IEの一部コンディショナルコメントがdialectを使わないと効かなかったりして結構難儀した。（xhtml的にダメなやつがある）
http://forum.thymeleaf.org/Conditional-Stylesheets-and-Thymeleaf-s-XML-Parser-HTML5-tp3468198p4025036.html


デザイナーの本もパラパラと見てみたのだが、あまりxhtmlで説明されているものも少ないように感じたので、最近は方針転換してLEGACYHTML5派になった。

htmlとxhtml、動的言語と静的言語のような関係っぽい。


## CSRF対策

依存関係にspring-boot-starter-securityを追加。
@ EnableWebMvcSecurityアノテーションが付いた設定クラスを用意。WebSecurityConfigurerAdapterを継承。
パスワード認証は要らないのでsecurityContextはdisableに。（雰囲気で書いてるのでSpring Securityについてもっと調べる。。）

```java
package hello;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;

@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.securityContext().disable();
    }
}
```

これだけでformタグ中にCSRFトークンがhiddenで含まれるようになる。
しかし注意点があって、ざっと試した感じだと、`th:action`としてactionが書いてあるpostのformタグだけが自動挿入対象になるらしい。ただの`action`ではダメ。
尚、テンプレート中では`_csrf`という変数で参照出来るらしい。

```html
<p th:text="${_csrf.token}"></p>
```

画面を表示してソースを見るとCSRFトークンが設定されていることが確認出来る。

![スクリーンショット 2015-05-12 23.16.55.png](https://qiita-image-store.s3.amazonaws.com/0/51910/c5d031ee-298e-4398-cd10-cce8746dcf89.png)


CSRFトークンを改ざんしてリクエストすると403エラーになることを確認。チェックは透過的にSpring Securityによって行われている模様。

https://github.com/eiryu/spring-boot-template/commit/16974ebac06ef0c44850b4b16178b91cf2350fc3


## JavaScriptなどの静的コンテンツ

やり方は設定と同じく数パターンあるみたいだが、`src/main/resources`にstaticディレクトリを作ってそこに置くことにした。


## i18n

`src/main/resources/i18n`にi18n用のメッセージファイルを置くようにした。
ja_JPとen用のファイルを配備。

https://github.com/eiryu/spring-boot-template/commit/05a500771c1bdaf03ca7cc384d9692426df406c5


## Spring Data JPAでデータベースアクセス

依存関係にspring-boot-starter-data-jpa、H2Databaseを追加。
トランザクション境界はServiceアノテーションが付いたクラスから始まるようにし、チェック例外も含め、例外が発生した場合にはロールバックするようにした。
データが既に存在する、などの状況はServiceから例外によってControllerに通知するのが楽だし、チェック例外を用いることにより、どういう例外が発生し得るかメソッドのシグネチャで予め知ることが出来るので便利。Controllerではキャッチして適切な処理をする。

https://github.com/eiryu/spring-boot-template/commit/ecfddedf09911ac366689a0addaee18602d101d8


## その他

起動時のバナーを作ってみたり。

```
＿人人人人人人人人人人人人人人＿
＞　Spring Boot 起動します！　＜
￣Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y￣
```

## 今後

色々さらに拡張していく予定。
WebAppをさくっと作るにはSpring Boot非常に良い。
既にQiita API v2 Hackathonでも利用してその作成スピードは体感済み。
http://eiryu.hatenablog.com/entry/2014/12/21/163531


### Spring Boot 1.12から1.2.3に上げたときのメモ

build.gradleのバージョンを上げてbootRunしたら以下のようなエラーが出た。

```java
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to read candidate component class: URL [jar:file:/Users/eiryu/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-autoconfigure/1.2.3.RELEASE/11175f217ac34bdacd6282cd44310f211121e270/spring-boot-autoconfigure-1.2.3.RELEASE.jar!/org/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration$JdbcTemplateConfiguration.class]; nested exception is java.lang.IllegalStateException: Could not evaluate condition on org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$JdbcTemplateConfiguration due to internal class not found. This can happen if you are @ComponentScanning a springframework package (e.g. if you put a @ComponentScan in the default package by mistake)
```

Applicationクラスがデフォルトパッケージに置いてあるのがダメらしい。任意のパッケージ配下に移動したら解決。

## 参考

- http://spring.io/guides/gs/rest-service/
- http://is4cafe.hatenablog.com/entry/20140403/1396535920
- http://blog.scheakur.com/post/91500044242/spring-boot-spring-loaded-gradle-intellij
- http://docs.spring.io/spring-boot/docs/current/reference/html/howto-hotswapping.html#howto-reload-springloaded-gradle-and-intellij
- http://dev.classmethod.jp/server-side/java/thymeleaf-spring-i18n-1/
- http://qiita.com/ksby/items/2ea40c5455c31870bd7c
- http://blog.nnasaki.com/entry/spring4/2
- http://docs.spring.io/spring-security/site/docs/current/reference/html/csrf.html