# Spring Framework Learning 05
## 👀Spring MVC
**Spring MVC**は、Springで**MVCパターン・アーキテクチャ**を実装するときに使う機能です。MVCとはGUIアプリケーションを構築するときに考案されたアーキテクチャです。WEBでも同じ考えで、このパターンを利用することができます。MVCは、Model, View, Controllerという3つの役割のコンポーネントに分割して、クライアントからのリクエストを処理します。各コンポーネントによる典型的な処理の流れは以下の図のようになります。

MVCパターンにおける各コンポーネントの役割は以下の通りです。
|コンポーネント名|役割|
|:------------|:-------|
|Model|アプリケーションの状態(データ)やビジネスロジックを提供するコンポーネント|
|View|Modelが保持するアプリケーションの状態(データ)を参照し、クライアントへ返却するレスポンスデータを生成するコンポーネント|
|Controller|リクエストをハンドリングし、ModelとViewの呼び出しを制御するコンポーネント。コンポーネントの名前が示すとおり、このコンポーネントではリクエスト処理の流れを制御する|
## 👀Thymeleaf
先ほどの章では、MVCパターンについて説明をしました。```Model```はデータそのものやロジックを記述するオブジェクトのため、Springに依存しない純粋なJavaオブジェクトです。```Controller```は、前回までの課題で実装したものとほぼ変わりがありません。それでは、```View```に関してはどうでしょうか。Webにおける主なViewデータは、HTMLになります。しかし、記述したHTMLをそのままクライアント(ブラウザ)に渡しても意味がありません。Springでやり取りされているJavaのオブジェクトをHTMLにマッピングしてあげる必要があります。そこでSpringが利用を推奨している**テンプレートエンジン Thymeleaf**を使用します。
テンプレートエンジンとは、特殊な記法を利用して記述された(テキスト)ファイルを解析し本来必要とされるデータに変換する仕組みのことです。今回の場合、ターゲットとなるデータ形式はHTMLですが、Thymeleafは変換対象となるファイルにもHTMLを利用しています。Thymeleafが提供するHTMLには、Javaオブジェクト(Model)を利用するための特殊な属性が増えており、それらをサーバサイド(Spring)で変換し、HTMLをクライアント側にレンダリング(描画)します。
## 💪 Spring MVC + Thymeleaf
[Spring INITIALIZR](https://start.spring.io)
構成はいつもの構成に加え、Thymeleafを追加します。
Project: Gradle
Language: Java
Spring Boot: 1.5.4
Group: org.[学籍番号]
Artifact: mvc-sample
Dependencies: Web, DevTools, Thymeleaf
### 様々なView
テンプレートエンジンであるThymeleafには、様々な型のオブジェクトを渡すことができます。View(Thymeleaf)から値を参照するためには、Controllerの引数```Model```に値をセットする必要があります。Modelは、StringとObjectのMap形式になっています。オブジェクトをセットするには、```Model#addAttribute```メソッドを利用します。
```src/main/java/org/abab1192/mvcsample/AppController.java```
```java=
package org.abab1192.mvcsample;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
@Controller
public class AppController {
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("msg", "world");
return "hello";
}
@GetMapping("/hello/{who}")
public String helloWho(Model model, @PathVariable("who") String who) {
model.addAttribute("who", who);
return "who";
}
@GetMapping("/time")
public String now(Model model) {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS");
model.addAttribute("now", now.format(dateTimeFormatter));
return "time";
}
@GetMapping("/list3/{a}/{b}/{c}")
public String list(Model model,
@PathVariable("a") String a,
@PathVariable("b") String b,
@PathVariable("c") String c) {
List<String> list = Arrays.asList(a, b, c);
model.addAttribute("list", list);
return "list";
}
@GetMapping("/entities")
public String entities(Model model){
List<Entity> entities = Arrays.asList(
new Entity(1, "a"),
new Entity(2, "b"),
new Entity(3, "c")
);
model.addAttribute("entities", entities);
return "entities";
}
}
```
```/entities```に渡している、```Entity```オブジェクトは以下のような形式とします。
```src/main/java/org/abab1192/mvcsample/Entity.java```
```java=
package org.abab1192.mvcsample;
public class Entity {
private int id;
private String name;
public Entity(){}
public Entity(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
```
Thymeleafは、HTML拡張子をそのまま利用することができます。Thymeleafの機能は、```th```から始まる特殊な属性を使うことで利用することができます。また、以下はHTML5の文法を利用していますが、Thymeleafをそのまま利用するとXHTML文法にしか対応していないため、必ずタグは閉じる必要があります。
以下の例では、```th:text```属性を使うことでオブジェクトの文字列を参照しています。```${msg}```は、ContorollerでModelオブジェクトにセットするときのMapに対応しています。また、コメントに寄る型注釈を付けるとIDEA上からは補完やエラー表示に役立ちます。
```src/main/java/resources/templates/hello.html```
```html=
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Hello World</title>
</head>
<body>
<!--/*@thymesVar id="msg" type="java.lang.String"*/-->
<p th:text="'Hello ' + ${msg}"></p>
</body>
</html>
```
```src/main/java/resources/templates/who.html```
```html=
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Hello Who</title>
</head>
<body>
<!--/*@thymesVar id="who" type="java.lang.String"*/-->
<p th:text="'Hello, ' + ${who}"></p>
</body>
</html>
```
```src/main/java/resources/templates/time.html```
```html=
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Time</title>
</head>
<body>
<h1>Now</h1>
<!--/*@thymesVar id="now" type="java.lang.String"*/-->
<p th:text="${now}"></p>
</body>
</html>
```
Listのような複数からなる値を利用するときは、```th:each```属性を利用します。イテレータのような関係になっており、```str:${list}```のように書いた場合には、List変数```list```のイテレータが```str```という風に読みます。
```src/main/java/resources/templates/list.html```
```html=
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>List</title>
</head>
<body>
<ul>
<!--/*@thymesVar id="list" type="java.util.List<String>"*/-->
<li th:each="str:${list}" th:text="${str}"></li>
</ul>
</body>
</html>
```
独自の型を利用する場合には、Getterメソッドが宣言されている場合、```object.field```の形でアクセスすることができます。
```src/main/java/resources/templates/entities.html```
```html=
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Entity</title>
</head>
<body>
<ul>
<!--/*@thymesVar id="entities" type="java.util.List<Entity>"*/-->
<!--/*@thymesVar id="entity" type="org.abab1192.mvcsample.Entity"*/-->
<li th:each="entity:${entities}" th:text="${entity.id} + ': ' + ${entity.name}"></li>
</ul>
</body>
</html>
```
いつものようにサーバを起動したら、Webブラウザを開き各メソッドを呼び出してみましょう。
### Form
フォームによる値の受け渡しを試してみましょう。以下の例では```/echo```でテキストフォームを表示、そこから```/result```をPOSTメソッドで呼び出し、与えられ値を表示ということをしている。
```/echo```の中で使われるEchoFormをModelにセットしている。このとき初期値は用いないため、デフォルトコンストラクタを利用する。```/result```では、フォームで入力された値を利用したいので、引数に```@ModelAttribute```アノテーションを付けた引数を受け取っている。
```src/main/java/org/abab1192/mvcsample/AppController.java```
```java=
package org.abab1192.mvcsample;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Controller
public class AppController {
@GetMapping("/echo")
public String echo(Model model) {
model.addAttribute("echoForm", new EchoForm());
return "echo";
}
@PostMapping("/result")
public String result(@ModelAttribute EchoForm echoForm) {
return "result";
}
}
```
フォームの取りうる値を定義した```EchoForm```クラスは以下のようになる。```/echo```では、Setter, ```/result```では、Getterを利用するので、どちらも用意しておきましょう。
```src/main/java/org/abab1192/mvcsample/EchoForm.java```
```java=
package org.abab1192.mvcsample;
public class EchoForm {
private String text;
public EchoForm(){}
public EchoForm(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
```
Thymeleafを利用したformは、少し注意が必要です。普段利用する```action```属性は何を入れても構いません(無くても動きます)。代わりに、```th:action```属性をセットしましょう。そしてModelでセットした、フォーム全体で利用するオブジェクトの名前を```th:object```属性に指定します。```input```タグには、```th:field```属性を追加し、フィールド名を```*{フィールド}```の形で指定します。
```src/main/java/resources/templates/echo.html```
```html=
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Echo</title>
</head>
<body>
<!--/*@thymesVar id="echoForm" type="org.abab1192.mvcsample.EchoForm"*/-->
<form method="post" action="#" th:action="@{/result}" th:object="${echoForm}">
<label>text:</label>
<input type="text" th:field="*{text}"/>
<input type="submit"/>
</form>
</body>
</html>
```
```result```は、Entityの値を受け取り表示する形と同様に実装します。
```src/main/java/resources/templates/result.html```
```html=
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Result</title>
</head>
<body>
<!--/*@thymesVar id="echoForm" type="org.abab1192.mvcsample.EchoForm"*/-->
<p th:text="${echoForm.text}"></p>
</body>
</html>
```
## 💪 演習
前回の[フィールドを用いた簡単なCRUD](https://hackmd.io/s/BJqN77qzW#フィールドを用いた簡単なcrud)をSpring MVCを用いた形に直せ。どのようなHTMLタグを用いるかの制限は設けない。なお難しい場合は、GETとPOSTのみを実装せよ。
以下を参考にせよ
- [Thymeleaf(+ Spring MVC) におけるフォーム](http://javazuki.com/articles/thymeleaf-input-form.html)
- [Spring Boot フォーム関連のサンプルコード ](https://www.qoosky.io/techs/a0806e458a)