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