Spring Boot 1.3.5 + Thymeleaf + PostgreSQL 9.6 導入メモ (1)

# はじめに
記事を書いていたら思いのほか長くなったので、

* Spring Boot導入～フォームの利用 （本記事）
* 入力のバリデーション、JPAを利用したデータの永続化 http://qiita.com/sashim1343/items/d37d2336f3625b86c141

の2つに分割しました。

# 環境
* JDK 8u91
* Spring Boot 1.3.5
* PostgreSQL 9.6
* NetBeans 8.1
* Maven 3.0.5 (NetBeansバンドル)

# Spring Boot導入～Hello World
NetBeans上で新規プロジェクト作成→カテゴリ「Maven」を選択し、適当にプロジェクトを作る。
今回は以下のように設定した。

* プロジェクト名: SpringBootSample
* グループID: com.example
* パッケージ: com.example.springbootsample
* その他の項目は既定値のまま

プロジェクトを作成したら、`pom.xml`を修正する。

```xml: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/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>SpringBootSample</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <java.version>1.8</java.version>  <!-- Javaのバージョンを明記 -->
    </properties>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.5.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>
```

次に、`com.example.springbootsample.controller`パッケージを作成し、その中に`SampleController.java`を作成する。

```java:SampleController.java
package com.example.springbootsample.controller;

import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/")
public class SampleController {
    @RequestMapping(method = RequestMethod.GET)
    @ResponseBody
    String get() {
        return "Hello, Spring Boot!";
    }
}
```

`@Controller`アノテーションを付与されたクラスはコントローラとして動作し、`@RequestMapping`で設定された条件に基づいてイベントのハンドリングをする。
なお、`@ResponseBody`アノテーションを付与すると、戻り値として直接コンテンツを返すことができる。
つまり、上記のコントローラは、`/`へのGETリクエストに対し"Hello, Spring Boot!"という文字列を返す。

また、これとは別にエントリポイント用のクラス`com.example.springbootsample.Application`を作成する。

```java:Application.java
package com.example.springbootsample;

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

@SpringBootApplication
public class Application {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}
```

ここまで終わったら一度実行してみる。
Spring Bootのlisten portはTomcatのデフォルト設定と同じく8080番である（設定により変更可能）。
Webブラウザ等を用いて`http://localhost:8080/`にアクセスし、"Hello, Spring Boot!"と表示されることを確認する。

# Thymeleaf （テンプレートエンジン）導入
このままでは表示するコンテンツを全てコントローラで作成しなければならず、非効率的かつ大変である。
そこで、ビュー部分にはThymeleafというテンプレートエンジンを導入する。
`pom.xml`の依存関係に下記を追加する。

```xml:pom.xml
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
```

また、`src/main/resources/application.yml`を作成し、以下を記述する。
（src/main/resoucesディレクトリはデフォルトでは存在しないため、手動で作成する必要あり）

```yaml:src/main/resources/application.yml
spring:
    thymeleaf:
        cache: false
```

これでThymeleafのキャッシュが無効化される。

次に、テンプレートとなる`src/main/resources/templates/sample.html`を用意する。

```html:sample.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>Hello, Spring Boot!</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    </head>
    <body>
        <p th:text="${message}"></p>
        <p>using Thymeleaf</p>
    </body>
</html>
```

ポイントは8行目の`th:text="${message}"`である。
このようにすることで、コントローラからビューに値を渡すことができる。
なお、NetBeansでThymeleafを使う場合には、`xmlns="http://www.w3.org/1999/xhtml"`を指定しないとエラーだらけでえらーいことになる。

最後に、`SampleController`に上で作ったビューに対するマッピング設定を記述する。

```java:SampleController.java
package com.example.springbootsample.controller;

import org.springframework.stereotype.*;
import org.springframework.ui.Model;    // 追加
import org.springframework.web.bind.annotation.*;

@Controller
@RequestMapping("/")
public class SampleController {
    @RequestMapping(method = RequestMethod.GET)
//    @ResponseBody // このアノテーションをつけるとレンダリングが行われなくなるので注意
    String get(Model model) {
        model.addAttribute("message", "Hello, Spring Boot!");   // パラメタを渡す
        return "sample"; // 使用するテンプレートの名前を指定する
    }
}
```

ここまで書いたら再び実行する。`http://localhost:8080/`にアクセスし、正しくページが表示されればOK

# フォームを利用した値の送受信
ここまで決め打ちの文章しか表示していなかったので、そろそろ自分で入力したい。ということで、フォームを利用して値を送受信する。
手始めに、先ほど作成したビューにフォームを追加する。

```html:sample.html
<!-- 前略 -->
<form method="POST" th:action="@{/}">
    <label for="name">Your name: </label><input type="text" name="name" />
    <label for="age">Your age: </label><input type="text" name="age" />
    <input type="submit" value="submit" />
</form>
<!-- 後略 -->
```

これで、名前と年齢をPOSTできるようになった。
あとはこれをコントローラで受け取るだけだが、そのための準備として、このフォームに対応するクラスを作成する。
`com.example.springbootsample.controller`の中に`PersonForm`クラスを定義する。

```java:PersonForm.java
package com.example.springbootsample.controller;

public class PersonForm {
    private String name;
    private Integer age;
    // getter/setter略
}
```

`SampleController`を修正し、POSTされたフォームの値を取得する。
コントローラは、`PersonForm`インスタンス経由でフォームの値を取得する。
このインスタンスを生成する部分はアノテーション1つで良い感じにやってくれる。

```java:SampleController.java
// 前略
public class SampleController {
    @RequestMapping(method = RequestMethod.GET)
    String get(Model model) {
        model.addAttribute("message", "Hello, Spring Boot!");
        return "sample";
    }
    
    @RequestMapping(method = RequestMethod.POST)
    String post(@ModelAttribute PersonForm personForm, Model model) {
        String name = personForm.getName();
        Integer age = personForm.getAge();
        model.addAttribute("message", "your name: " + name + ", your age: " + age);
        return "sample";
    }
}
```

# 続き
http://qiita.com/sashim1343/items/d37d2336f3625b86c141

# 参考
http://projects.spring.io/spring-boot/
http://www.riem.nagoya-u.ac.jp/~ohta/etc/springboot-1.html
http://d.hatena.ne.jp/hagi44/20130608/1370667799
http://blog.okazuki.jp/entry/2015/07/05/093915