SpringBootに入門する為の助走本

# 【SpringBootに入門する為の助走本】

## ■おしらせ

本記事はZennに移行して改訂版を出しました。

https://zenn.dev/sugaryo/books/spring-boot-run-up

Zenn版はこのQiita記事を元に分冊化して、一部古すぎる内容をメンテしつつ、Zenn独自記法にあわせて加筆修正したものになります。

今後の加筆修正や改訂はZenn版のみになります。  
**Qiita版は消さずにこのまま残しておく** つもりですが、基本的に更新はせず、最後に「あとがき」だけ追記に来ると思います。


## ■おしながき

- [環境構築編](#環境構築編)
	- SpringBootと統合開発環境
	- JavaのPath確認
	- STSの入手
	- STSの起動
	- STSの日本語化
- [取り敢えずStarterProjectを使ってみる編](#取り敢えずstarterprojectを使ってみる編)
	- 新しいプロジェクトを作る
	- ウィザード１ページめ
	- ウィザード２ページめ
	- ウィザード３ページめ（さいご）
	- プロジェクトが出来上がります。
- [簡単なWebAPIを作ってみよう編](#簡単なwebapiを作ってみよう編)
	- 取り敢えずHelloWorldする
	- パラメータを指定する
	- HttpMethodを指定する
	- 戻り値をString以外にしてみる
	- 共通のエラーハンドラを用意しておく。
- [逆にWebAPIを呼び出してみよう編](#逆にwebapiを呼び出してみよう編)
	- 取り敢えず適当なAPIをコールします。
	- exchangeで色々と細かく指定しながらコールする。
	- 4xx系／5xx系で例外を発生させなくする方法。
	- PATCH だけちょっと注意。
	- タイムアウト設定を弄る方法。
	- RestTemplateラッパー三点セット
- [Thymeleafで画面を作ってみよう編](#thymeleafで画面を作ってみよう編)
	- SpringBootがサポートするテンプレートエンジン
	- とにもかくにも画面だしてみよっか。
	- 取り敢えず日付時刻を出すだけの画面をつくる。
	- 式言語：SpEL - Spring Expression Language
	- 画面をイケメンにしてみよう。
- [スケジュール機能を使ってみよう編](#スケジュール機能を使ってみよう編)
	- スケジュール機能を有効化してみよう。
	- スケジュール機能を使ってみよう。
	- スケジュール（定期実行タスク）機能に関する余談
- [応用編：Thymeleafをプレーンテキストで使ってみよう編](#応用編thymeleafをプレーンテキストで使ってみよう編)
	- Thymeleafとはそもそもなにか？
	- とにかくまずはプレーンテキストで使ってみよう！
	- TemplateEngineをBean登録して使おう。（その１）
	- TemplateEngineをBean登録して使おう。（その２）
	- TemplateをDBに文字列で持ちたいです！
- [ちょっとしたTips集](#ちょっとしたtips集)
	- [自分の書いたプログラムのデバッグログを出す方法。](#自分の書いたプログラムのデバッグログを出す方法)
	- [実行可能JARとして設定してビルドする。](#実行可能jarとして設定してビルドする)
	- [pom.xml の一行目に謎のエラー出んだけど！！](#pomxmlの一行目に謎のエラー出んだけど)
	- [Swagger 使ってみたいんだけど。](#swagger使ってみたいんだけど)
	- [Service（というかTransactional）ロールバック仕様でハマったこと。](#serviceというかtransactionalロールバック仕様でハマったこと)
	- [JSONを SNAME_CASE で返す設定。](#jsonを-sname_case-で返す設定)
	- [JSONを PrettyPrint で出力する設定。](#jsonを-prettyprint-で出力する設定)
	- [本番運用系の便利エンドポイントを簡単に追加する。](#本番運用系の便利エンドポイントを簡単に追加する)

※ ~~肥大化してきたら整理して大見出し毎に別ページに分冊化してリンク張る形に変更・・・するつもりだったけど、どうしようか、どうしたほうがいいかな？~~
**取り敢えず自前でおしながきにリンク貼ってセルフ見出しマップにしました。**

## ■まえがき

興味のない人は **「おしながき」** に書いた見出しまで読み飛ばしちゃっておｋ！！


**きっかけ：**

新しいJavaの開発案件でフレームワーク選定してて  
**(*'▽')　SpringBootやろうぜ！！**  
って事になり、非機能要件について色々と調査・勉強してまして。

調べてプロジェクトで使ってみた感じ  
**(*'▽')　こいつぁ便利で面白いぞ！！**  
って感じで盛り上がりました。

で、折角なので*（プロジェクトでのドキュメンテーションも兼ねて）*公益性のためにQiitaに纏めて公開しよう、という話になった次第。

**これを書いている人について：**

- Javaの経験は３～４年程度。  
*（ ~~実務経験Java6～7、趣味でJava8を使っている。もう9とか10とか出ていて割と焦っている~~ だったけど最近普通にJava11とか使ってんねぇ ）*
- 以前はJavaEE開発をやっていた。  
*（JSF:primefaces, JPA:hibernate を一応触った事がある）*
- もともとはC#屋さんで、WebよりWindowsがメインだった人。
- 本当はJavaよりKotlinに興味ニキだけど、今回は開発の都合でJava使うマン。
- SpringFramewrokの使用経験はなく、今回初めてSpringBootを使う。


**方向性：**

上記と似たような経歴の人や、Spring触った事ないけどこれからSpringBootを始めようとしてる人の参考になればコレ幸い。
入門系のページは既に沢山あって情報が充実してるので、それらへの **「助走本」** を目指す感じ。

~~以前やってたJavaEE開発（JSF／JPA）に絡めて書いていこうと思うので、そっち方面の経験者には親和性が高くなる、、、かもしれません。~~  
**全然絡められてないだるるぉ！いい加減にしろ！**


## ■前提（各種バージョン）

ちなみにこの記事でどこまで書くか決めてないけど、今やってる開発は **` SpringBoot + Thymeleaf + PostgreSQL `** という構成でやってます。  
ただこの記事では、DBまわりの話はほぼ出てきません。  
*（個人的には、SpringDataJPA使うよりはJdbcTemplate使った方が良いかなと思ってる派）*


### ＞ 初稿時点での環境情報

> **【※※ 注意事項 ※※】**
> 
> - 初稿からだいぶ日が経っており、既にSTS3系は古くなってます。現状（2019年時点）ではSTS4系を使った方が良いです。
> - また、記事内容とはあんま関係ないですけど、最近自宅PCをRyzenの64bitマシンで組み直したので、自宅開発環境も初稿から大分変わっています。

- **初項時点での環境情報：**

使用する各バージョンは以下の通り。  
*※自宅環境と開発環境でちょっと違うけど、記事の内容的には概ね問題ないと思う。
注意しないといけないのは、JavaとSTSでbitバージョン（x64/x86）を合わせる必要があるという事くらい。*

|環境|バージョン|
|----|----|
|開発言語|Java8以上|
|開発環境|STS：3.9.4 RELEASE e4.7.3a ※|
|SpringBoot|2.0.2|

ちなみに、個々の細かい説明 *（いきなり出て来たSTSって何やぁ？ とか）* は後述します。


- **現在の環境情報：**

ということで、SpringBoot自体の説明にはあんま関係ないですけど、現在の環境を改めて記載しときます。

|環境|バージョン|
|----|----|
|開発言語|Java11 (open-jdk 11.0.1)|
|開発環境|STS：4.0.2 RELEASE|
|SpringBoot|2.1.1|

- **更にバージョンアップ：**

先日見に行ったら `STS4` がちょっとバージョン上がってて、現時点の最新が `4.4.0` になってました。
何だそんな事か、なんですが、毎回毎回ウザかった `pom.xml の一行目に出て来る謎のエラー` が解消されてました。
やったね！！

- **更に更にバージョンアップ：**

`STS4` が更に更新されており、落とせるのが `zip` から `jar(自己展開形式)` になってました。
環境構築編のところに詳細追記してあります。

- **２０１９年１２月時点の環境：**

という事で、更に日が経って環境も変わり、更にエディタに `VSCode` を使用するようになりましたので、改めて現状の最新バージョンを載せておきます。

|環境|バージョン|
|----|----|
|開発言語|Java11 (open-jdk 11.0.1)|
|開発環境|STS：4.4.1 RELEASE|
|SpringBoot|2.2.1|
|エディタ|VSCode / SpringBoot Extension Pack|

ということで [VSCode - VisualStudioCode](https://azure.microsoft.com/ja-jp/products/visual-studio-code/) を最近使っています。  
拡張機能の [SpringBoot Extension Pack](https://marketplace.visualstudio.com/items?itemName=Pivotal.vscode-boot-dev-pack) を入れる事でSpringBootアプリケーションの開発がサポートされます。

VSCodeの **マルチカーソル機能** が使えたり、もともと **C#出身でVSっ子** のぼくには凄く使い易いエディタですし、**起動もめちゃくちゃ軽い** し、 **編集差分の出し方やgitとの連携・競合発生時のマージ作業** などなど、、、メチャクチャ便利でオススメです。  
**※ と、大絶賛はしてみたものの、この記事はこのまま「助走本」として、初歩的なSTS環境をベースに書いていこうと思います。**


# 【環境構築編】

SpringBootでの開発では **STSというもの** を使うようです。  
ここではとりあえずその **STSというもの** を入手して起動する所までをやってみます。

## ■SpringBootと統合開発環境

**STS（Spring tool suite）** には、IDEとしての ```STS.exe``` と、プラグインとしての ```STS plugin``` の２種類存在します。

- `STS.exe` 
- `Eclipse` + `STS plugin`
- `IntelliJ` + `STS plugin`
- （番外編）`VSCode` + `SpringBoot Extension Pack`

まぁ今回は手っ取り早いので ```STS.exe``` を使います。

ちなみにこの `STS.exe` さんですが、何の事はないただの **Eclipseベースの拡張IDE** です。
要するにSTSを使うということはEclipseにSTSプラグインを適用するのとだいたい同じ事です。

IntelliJ大先生にもプラグインがあったはずなので、IntelliJっ子はソレでいいと思います。
確かCommunityでも使えたと思いますが、あまりガッツリ使わなかったのでちょっと詳しく覚えてません。

`VSCode + SpringBoot Extension Pack` は **ぼくが最近お気に入りの環境** です。
これは厳密な話をするとIDEじゃなくてエディタなんですが、拡張機能を入れる事でIDEとして必要な機能は揃っちゃうので、実質IDEですねこりゃ。

<details>
<summary>余談</summary>
ここではあくまで入門編への助走を手助けする意味で「助走本」と題しており、いわゆる「初学者」を対象読者と考えています。
ので、基本的には標準（？）のSTS系を前提とします。
どうしてもEclipse系のIDEが嫌いだとか、敢えてIntelliJやVSCodeを使おうという人は既に「初学者」ではないので、よしなに読み替えて下さい。

まぁ、基本的なアノテーションの使い方とか、SpringBoot（SpringFramework）自体の使い方としてIDEの違いが問題になることはまず無いと思うので、そこは好きなものを選んで大丈夫だと思います。
</details>

ということで、本稿では基本的にSTSベースで進めていきます。



## ■JavaのPath確認

STSを使う前にJavaのPathが通っているか確認します。

普通にコマンドプロンプト ```cmd.exe``` を立ち上げて、以下のコマンドを叩いてパス通ってるか確認しましょう。

```
C:\>echo %JAVA_HOME%
C:\openjdk\jdk-11.0.1

C:\>java -version
openjdk version "11.0.1" 2018-10-16
OpenJDK Runtime Environment 18.9 (build 11.0.1+13)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.1+13, mixed mode)
```

出て来なかったらパスがおかしいので、**システムの詳細設定**から**環境変数**を調べてパスが通っているか確認してください。  
そう言えば、本題と関係ないですけど **環境変数の `Path`** って、Windows10から *（もしかして知らなかっただけでWindows8から？）* メチャクチャ編集しやすくなりましたよね。


## ■STSの入手

こちらの [公式ダウンロードページ](https://spring.io/tools/sts) から入手し、zipを好きな所に展開するだけでOKです。

~~但し、このページの ```DOWNLOAD STS``` から落とすと x86(32bit)版 が落ちて来たので、x64(64bit)版が欲しい人は ```See All Versions``` リンク先から入手して下さい。~~
![WS000001.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/fa032632-cde0-ab0d-762a-7f1ced8a8461.jpeg)

暫く記事の更新が滞ってるうちにバージョンが上がっちょりました。
![WS000096.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/f9be4203-2c0d-84bb-f769-363572351077.jpeg)
今は SpringTool4 *(現在はsprint-tool-suite 4.1.0)* が出ていて、上記のようにダウンロードページもリニューアルされてます。

STSのダウンロードはここから。
![WS000098.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/f8833f6e-7263-0ead-6645-a39093e459ef.jpeg)

VisualStudioCode用のプラグインも出てました、素晴らしい！
![WS000097.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/969bd082-69f5-332f-1f2a-cb4618b73ce8.jpeg)

前のSTS3系が欲しい人は、ページ下部のリンクから入手可能なようですが、
STS3系は今年、2019年の中頃にEOLになるよって書いてあるので、素直にSTS4系にした方が良さそうです。  
*（なので、敢えてSTS3系のリンクや画像は貼らないでおきます）*

### ＞ zip が jar になってました。

ちょっと最新のSTSを入れ直そうと思って [Spring.io tools](https://spring.io/tools) に取りに行ったら、以前は `zip` だったのが `jar` になってました。

```:こんなやつ。  
spring-tool-suite-4-4.4.1.RELEASE-e4.13.0-win32.win32.x86_64.self-extracting.jar
```

`self-extracting.jar` ということで、なんか自己展開形式？ のjar？ なんですかね？  
Windows環境だと長過ぎるパスを扱えないのでzip形式だと上手く展開出来ないらしく、javaコマンドで実行することでいつものアレが手に入るようになったようです。

```bash
java -jar spring-tool-suite-4-4.4.1.RELEASE-e4.13.0-win32.win32.x86_64.self-extracting.jar
```


## ■STSの起動

~~zipを落として来たら、好きなフォルダに展開します。~~

**書いてる内容が大分古くなりました。**  
**上述の通り、zipじゃなく自己展開形式のjarになっています。**

展開したディレクトリの中（僕の場合は `C:\springboot\sts-bundle\sts-3.9.4.RELEASE`）にある `STS.exe` をダブルクリック。  
この時、Javaへのパスが通っていない場合は怒られるので、前述の通りきちんとパスの確認をしましょう。

すると、こんな **EclipseみたいなIDE** *（ベースがEclipseなので当たり前）* が立ち上がります。
![WS000556.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/35ee1f16-14e1-63dc-0139-79c68ee367c7.jpeg)


## ■STSの日本語化

```STS.exe``` はデフォルトでは日本語化されてないので、別途 ```Pleiades plugin``` を入れないと英語表記になります。
英語表記だと困るよ、っていう方は [Pleiades](http://mergedoc.osdn.jp/) からプラグインを入手して下さい。

### ＞ 日本語化の参考資料

- [Spring Tool Suiteのインストールと日本語化 - Qiita](https://qiita.com/kxn4t/items/2f86c57956fa052d9236)
- [Spring Tool Suite インストールと日本語化 for Windows - Qiita](https://qiita.com/andna0410/items/e9cc07c9e2e34a542245)


### ＞ 【余談】Pleiades適用が物凄く簡単になってた件

ぼくは普段（めんどくさいので）日本語化せずに英語のまま使ってるんですが、ちょっと手順確認の為にと思って最新のPleiades先生を入手して、適用してみようと思ったんです。

![WS000382.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/0a35a7e1-32ba-0cce-62b1-716008ade2f5.jpeg)

![WS000383.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/23b8d7c5-b851-b352-be54-a97559d204c4.jpeg)

![WS000384.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/92761e09-8df1-98c1-7f0e-e91f0c11dde3.jpeg)

![WS000385.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/c252ce52-d170-c8f5-3aae-018c29bb2d9e.jpeg)

![WS000386.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/9f3810e8-d5f6-8825-7b33-8ae3202da66a.jpeg)

![WS000387.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/fca36ac0-3bb3-c471-50bc-f98eb9aa8852.jpeg)


あれ？
Eclipseの日本語化ってこんなに簡単だっけ？

**なんか setup-pleiades.exe で STS.exe 指定したらすぐおわった。**

以前はなんかjarとか手で配置しに行ったりして、結構ダルい作業だった気がするんですけど。
いやぁ、、、すごいですね、Pleiades先生。
物凄く便利になってました！！




# 【取り敢えずStarterProjectを使ってみる編】

## ■新しいプロジェクトを作る

普通にEclipse感覚で、SpringBootの新規プロジェクトを作成してみます。

`[File＞New＞Spring Starter Project]` から選択。
![WS000563.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/d4b53a25-ec3f-9d9b-261f-e4e9276044a0.jpeg)

若しくは `[File＞New＞Other]` で、メニューから `[SpringBoot＞Spring Starter Project]` を選択。
![WS000564.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/b437bd94-547c-82c0-5c8b-8e6d0628bc93.jpeg)

![WS000565.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/5e8f1c77-69b2-17ef-4c14-cc2fb80bf77e.jpeg)

以下、ウィザードに従っていきます。

## ■ウィザード１ページめ

プロジェクトの基本情報を入力します。
![WS000566.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/2b073dc0-e28e-4c32-4cb8-dd04bf673b4e.jpeg)

**さて、いきなり入力項目の多い画面が出て来て嫌になりましたね？**

ざっくり各項目を説明しますが、**結論から言うと「取り合えずデフォルトのまま突き進んでおｋ」です**ので、かったるい説明が嫌な人は次の見出しまでカッ飛んで下さい。

### ＞ ServiceURL

これがSpringBootの正体と言っても過言ではない（多分）、[SPRING INITIALIZR](https://start.spring.io/)大先生です。
取り敢えずデフォルトの `https://start.spring.io` のままで良いです。

SPRING INITIALIZR 大先生については後程詳しく書きます。*（今は深い事気にせずゴー）*
※ ちなみに、 `spring initializer` じゃなくて `initializr(eがない)` みたいです、なんで？


### ＞ Name

プロジェクト名を入力します。
好きな名前を付けて下さい。

今回は面倒くさいのでデフォルトの `demo` のまま行きます。

### ＞ Type：Maven

ビルドツールを選べます。
デフォルトMavenですが、Gradleが好きって人は変更すれば良いよ。

### ＞ Packaging：Jar

実行可能形式JARか、普通にデプロイするWARか選べます。

これも詳しくは後述しますが、SpringBootさんは内蔵Tomcatを持っているので、実行可能形式JARにして単独で立ち上げる事が出来るスグレモノなのです。

### ＞ JavaVersion

好きなJavaバージョンを選んで下さい。
*※面倒くさいのでデフォルトで突き進んでたら 8 選んでたけど気にしない。皆は素直に 9 か 10 選ぶと良いよ。*

### ＞ Language

Javaの他にKotlinも選べるよ！！

[ことりんかわいいよ、ことりん。](https://www.google.co.jp/search?q=%E3%81%93%E3%81%A8%E3%82%8A%E3%82%93&rlz=1C1CHBD_jaJP721JP721&source=lnms&tbm=isch&sa=X&ved=0ahUKEwj867S9_pPbAhUBqJQKHbW5DksQ_AUICigB&biw=1097&bih=491)
*※すいません、言ってみたかっただけです。*

### ＞ Group/Artifact/Version/Description/Package

**適当でおｋ！！**

ぽむぽむ（`pom.xml`）に書き込まれるから変えたくなったら変えればいいよ。
![WS000580.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/e712d96e-222f-cfe9-f9a8-7411afd30640.jpeg)


## ■ウィザード２ページめ

プロジェクト依存関係を選択します。

![WS000574.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/ea606da3-d1ab-2b99-243e-45df4de34414.jpeg)

**プロジェクト依存関係って何やねん？**

ぼくも良く解ってませんが、要するにここで **「使いたいSpringフレームワークを選べ」** という事みたいです。
ここで選んだやつが **`pom.xml`** に書き込まれて、後ほど **Maven大先生が必要なjar一式を落としてくれる** という手筈。
要するにアレですねVisualStudioのxxprojとかで参照設定追加してdll追加するような作業ですね。  
*（勿論、後からpom.xmlに手動で追記して、新しい依存関係ライブラリを追加して落として来るとかでもOK）*

で、取り敢えず今回は **「簡単なWebAPIを作ってみよう編」** と **「Thymeleafで画面を作ってみよう編」** で使用するので、以下の依存関係を選択します。  
**※ STS（というかSpringBoot自体）のバージョンが上がって、依存関係のリスト内容もちょっと変わってますね。よしなに読み替えて下さい。というかこの各依存関係の詳細が載ってる一覧とか欲しい、、、欲しくない？**

- Template Engines
  - Thymeleaf
- Web
  - Web

*Thymeleaf* というのは今回使用するテンプレートエンジンです。
JavaEE開発で使った *JSF(primefaces)* みたいなもんですね。

まぁあれだ、**(jarが) 足りなくなったら (pomに) 足すだけ** やから。
あんま深く考えずにサクッと進みましょう、勢い大事、勢い。
今の所DBに接続する所までは考えてませんが、必要になったら `JDBC` とか `JPA` とか入れましょう。

## ■ウィザード３ページめ（さいご）

設定確認。

![WS000575.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/266e808f-0e99-c832-8a30-a28da086372a.jpeg)

**なんか良く解んない画面が出て来ましたね？**

```cmd:見易く整形してみた。
https://start.spring.io/starter.zip
	?name=demo
	&groupId=com.example
	&artifactId=demo
	&version=0.0.1-SNAPSHOT
	&description=Demo+project+for+Spring+Boot
	&packageName=com.example.demo
	&type=maven-project
	&packaging=jar
	&javaVersion=1.8
	&language=java
	&bootVersion=2.0.2.RELEASE
	&dependencies=thymeleaf
	&dependencies=web
```

どうやらこれは **`Spring Initializr`** と呼ばれるもののようです。

※ ところでどうでも良いですけど、Initializ**e**r じゃなくて Initializr なんですね

詳しい説明は後でやります。
取り敢えず `Finish` を押してウィザードを完了しましょう。

## ■プロジェクトが出来上がります。

暫し待てば `demo` プロジェクトが出来上がります。

プロジェクトエクスプローラを開いてみるとこんな感じ。

![WS000577.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/0f0d05d6-d286-bf5a-98aa-13acf0151a21.jpeg)

`pom.xml` を開いてみると、依存関係選択のページで選択した奴が書かれてますね。
プロジェクトツリーの `Maven Dependencies` を開くと、Maven大先生が依存関係解決で落としてくれたJarがワンサカ詰まっております。

![WS000578.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/b493f022-6876-9b50-a9e0-52fa41fe74d5.jpeg)


# 【簡単なWebAPIを作ってみよう編】

## ■取り敢えずHelloWorldする

まぁまずは適当にコード書いて動かしてみましょう。

### ＞ controller用のパッケージを作ります。

最初は demo パッケージしか無いので `com.exapmle.demo.controller` を作ります。

![WS000584.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/1f926268-cd2a-3759-2624-b1215dfff1a2.jpeg)

![WS000585.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/0935a376-8957-39cf-4d26-fb75b262a0f1.jpeg)


### ＞ WebAPI用のControllerを作ります。

適当に好きな名前でクラスを作って `@RestController` アノテーションを付けます。 

```java:RestAPI用のControllerを作成。

package com.example.demo.controller;

import org.springframework.web.bind.annotation.RestController;


@RestController
public class WebApiController {
    // 内容は後で実装するよ。	
}
```

### ＞ RequestMappingを設定します。

```java:メソッドを作って文字列を返してみる。
package com.example.demo.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("api")
public class WebApiController {
	
	@RequestMapping("hello")
	private String hello() {
		return "SpringBoot!";
	}
	
}
```

で、これにアクセスするために `application.properties`（最初にデフォルトで作られているので、リソース検索してください）に好きなポート番号を設定。

```application.propertis
server.port=8080
```

### ＞ 動かしてアクセスしてみる。

先の実装でこんな感じで設定しました。

- クラスに `@RequestMapping` をアノテートして引数に `"api"` と設定
- メソッドに `@RequestMapping` をアノテートして引数に `"hello"` と設定
- アプリケーション設定に `server.port=8080` と設定

なので、このAPIのURLは `http://localhost:8080/api/hello` となります。

プロジェクトのコンテキストメニューから `Run AS ＞ Spring Boot Application` を選択すると内蔵tomcatが立ち上がって、裏で色々動いてくれます。**（超らくちん！）**

![WS000588.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/26313d93-44f6-905d-d8dd-03132cbcc7c9.jpeg)

ブラウザで先のURLにアクセスすると、文字列が返されます。

![WS000589.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/0cee0be6-8801-2d14-77df-03ef2e24c980.jpeg)

「取り敢えず動かしてみる」と言うだけなら１クラス実装するだけで良い、というお手軽さに鼻血が出そうです。

とは言え、固定のプレーンテキストを返されても嬉しくはない、この後色々と作り込んでいき、戻りをJSONにしたり、幾つかの方法でパラメータを渡してみたりしましょう。

## ■パラメータを指定する

### ＞ パスパラメータ

[@PathVariable](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/PathVariable.html)

```java
	@RequestMapping("/test/{param}")
	private String testPathVariable( @PathVariable String param ) {
		return "受け取ったパラメータ：" + param;
	}
```
このように、`@RequestMapping` でマッピングするURLの中に `{xxxx}`を仕込んでおくと、`@PathVariable` でアノテートしたパラメータにバインドしてくれます。

ちなみに `@PathVariable(name)` を省略した場合は、仮引数名と同じ `name` を指定したのと同じ事になります。
つまり、上記のコードは以下のように記述する事も可能です。

```java
	@RequestMapping("/test/{param}")
	private String testPathVariable( @PathVariable("param") String param ) {
```

```java
	@RequestMapping("/test/{hoge}")
	private String testPathVariable( @PathVariable("hoge") String param ) {
```


### ＞ リクエストパラメータ

[@RequestParam](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html)

```java
	@RequestMapping("/test")
	private String testRequestParam( @RequestParam() String param ) {
		return "受け取ったパラメータ：" + param;
	}
```

`REST API` 作るなら基本的に前述の `@PathVariable` の方を使用するかと思いますが、念の為、普通に（？）URLのクエリパラメータを使うパターンも載せておきます。
強いて言えば、ダミーのHTMLで適当に`<form>`だけ置いて、パラメータを`<input:text>`でsubmit掛ける場合とかに使えると思います。

こちらも先の例と同様に、`@RequestParam(name)` を省略した場合は、仮引数名と同じ `name` を指定したのと同じ事になります。
つまり、上記のコードは以下のように記述する事も可能です。

```java
	@RequestMapping("/test")
	// /test?param=hoge とした場合、paramには "hoge" がバインドされる。
	private String testRequestParam( @RequestParam("param") String param ) {
		return "受け取ったパラメータ：" + param;
	}
```

```java
	@RequestMapping("/test")
	// /test?name=hoge とした場合、paramには "hoge" がバインドされる。
	private String testRequestParam( @RequestParam("name") String param ) {
		return "受け取ったパラメータ：" + param;
	}
```

#### 余談：

と、ここまで書いてちょっと不安になったのが `GET + RequestBody` ってホントに出来ないのか？
いやいやそんな話は無いよなぁ、絶対無いよなぁ、と思ってぐぐってみたら面白い記事が見付かったので、貼っておきます。

- [cURLでbody付きのGETリクエスト - Qiita](https://qiita.com/dozo/items/5bccf146c71bfb5921ec)


### ＞ リクエストボディ

[@RequestBody](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestBody.html)

通常、リクエストボディを送信する以上、メソッドは `POST` になるので、上の２パターンとはちょっと指定が変わります。
（と言っても、上のパターンでも `GET` や `POST` を明示的に指定出来るのを省略してるだけなので、実際は同じですが）

```java
	@RequestMapping(value = "/test", method = RequestMethod.POST)
	private String testRequestBody( @RequestBody String body ) {
		log.info( body );
		return "受け取ったリクエストボディ：" + body;
	}
```

### ＞ 全部纏めたコード

以上、取り敢えず最低限おさえておくべき、パラメータの受け取り３パターンはこんな感じ。
今回は `String` 一個だけという最小構成気味な形だけに限りましたが、まぁ色々出来るんでその辺は色々やってみれば良いと思います。

```java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * REST API のサンプルコントローラ.
 * 
 * <p>
 * {@code application.properties#server.port = 9999} と設定していると仮定して、
 * {@code "localhost:9999/sample/api/test/*"} にアクセスする。
 * </p>
 */
@RestController
@RequestMapping("/sample/api")
public class SampleRestApiController {
	private static final Logger log = LoggerFactory.getLogger( SampleRestApiController.class );

	@RequestMapping("/test/{param}")
	private String testPathVariable( @PathVariable String param ) {
		log.info( param );
		return "受け取ったパラメータ：" + param;
	}
	
	@RequestMapping("/test")
	private String testRequestParam( @RequestParam() String param ) {
		log.info( param );
		return "受け取ったパラメータ：" + param;
	}

	@RequestMapping(value = "/test", method = RequestMethod.POST)
	private String testRequestBody( @RequestBody String body ) {
		log.info( body );
		return "受け取ったリクエストボディ：" + body;
	}
}
```


## ■HttpMethodを指定する

```java

	/** 登録：CRUDでいう <b>C:CREATE</b> を行うAPI */
	@RequestMapping(value="/resource", method=RequestMethod.POST)
	private String create(@RequestBody String data) {
		return "登録だよ";
	}
	/** 参照：CRUDでいう <b>R:READ</b> を行うAPI */
	@RequestMapping(value="/resource/{id}", method=RequestMethod.GET)
	private String read(@PathVariable String id) {
		return "参照だよ";
	}
	/** 削除：CRUDでいう <b>D:DELETE</b> を行うAPI */
	@RequestMapping(value="/resource/{id}", method=RequestMethod.DELETE)
	private String delete(@PathVariable String id) {
		return "削除だよ";
	}

	/** 更新：CRUDでいう <b>U:UPDATE</b> を行うAPI */
	// ※PUTを使うのかPATCHを使うのかと言うのはまた別な話として、、、
	@RequestMapping(value="/resource/{id}", method=RequestMethod.PUT)
	private String update(@PathVariable String id, @RequestBody String data) {
		return "更新だよ";
	}
```

ところで、ソース内のコメントにも記載した通り **更新のAPIに `PUT` を使うのか `PATCH` を使うのか** と言う話ですが。
この話題についてはこの辺が参考になるかと。

- [REST入門 基礎知識 - Qiita](https://qiita.com/TakahiRoyte/items/949f4e88caecb02119aa)
    - [インタフェースの統一 POST/PUT/GET/DELETE](https://qiita.com/TakahiRoyte/items/949f4e88caecb02119aa#%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9%E3%81%AE%E7%B5%B1%E4%B8%80)
- [PUT か POST か PATCH か？ - Qiita](https://qiita.com/suin/items/d17bdfc8dba086d36115)
- [リソースの一部更新におけるURL設計](https://qiita.com/necojackarc/items/fd53c96865d0ef7a02d8)
- [REST API Tutorial HttpMethods #summary](https://restfulapi.net/http-methods/#summary)


## ■戻り値をString以外にしてみる

<details>
<summary>与太話</summary>
別に戻り値 `String` のままでも、中身をJSON形式文字列にしてやればJSONデータを返せるものの、
「本当にJSON形式文字列だけが返却されるのか？」
「どころかHTMLが返って来てたりして？」
「と見せ掛けてエラーメッセージが文字列で返されるかも？」
「HTMLといったな、スマンありゃXMLだった」
「JSONだと思った！？残念、CSV形式文字列でした～！！」
など、不安が尽きませんよね。
この `String` の中身は本当にJSONなのか！？
と言う疑念を抱いてわざわざコードパスを舐めるように追いかけるのは苦痛だし、時間の無駄です。
</details>

結論から言って、**Beanを返すだけ** です。
SpringBoot先生のRestControllerでは、いわゆるJavaBean(POJO)を素直に返すとそのJSONを返すようにしてくれます。
（裏ではjacksonを使ってstringifyしてるはず）

### ＞ JavaBeanを返してよしなにしてもらう。

```java

	public static class HogeMogeBean {
		private String hoge;
		private int moge;
		
		// property と ctor は省略。
	}
	
	@RequestMapping("hogemoge")
	public HogeMogeBean hogemoge() {
		return new HogeMogeBean( "ほげ", 1234 );
	}
```


`http://localhost:8080/api/hogemoge` にアクセスして実行するとこんな感じ。

```text:レスポンス
{"hoge":"ほげ","moge":1234}
```

```text:レスポンスヘッダ
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: アクセス日時
```

ちゃんと `application/json;charset=UTF-8` も付けてくれてます。

### ＞ JSON文字列にちゃんとヘッダ付けてあげる。

逆に、StringでJSON文字列を返してる場合にこのヘッダを付けたい場合はこんな感じ。

```java
	@RequestMapping(value = "hogemoge2", 
			produces = MediaType.APPLICATION_JSON_VALUE)
	public String string() throws Exception {
		HogeMogeBean bean = new HogeMogeBean("もげ", 297);
		String json = new ObjectMapper().writeValueAsString(bean);
		return json;
	}
```

アクセスするとこう。

```json:レスポンス
{"hoge":"もげ","moge":297}
```

```text:レスポンスヘッダ
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Content-Length: 28
Date: アクセス日時
```

### ＞ めんどくさいのでマップで返しちゃう。

機能設計としての是非は置いといて、さくっと `HashMap` を返しても大丈夫。

```java
	@RequestMapping("hogemoge3")
	public Map<String, Object> map() {
		Map<String, Object> map = new HashMap<>();
		map.put("hoge", "ぴよ");
		map.put("moge", 999);
		return map;
	}
```

```json:レスポンス
{"moge":999,"hoge":"ぴよ"}
```

```text:レスポンスヘッダ
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: アクセス日時
```

### ＞ ファイルを返す。

`org.springframework.core.io.FileSystemResource` てのを使うと楽に実装できるらしい。

```java
	@RequestMapping( value = "hogemoge4", 
			produces = MediaType.APPLICATION_OCTET_STREAM_VALUE )
	public Resource file() {
		return new FileSystemResource( new File("C:\\test\\hogemoge.png") );
	}
```

試しに画像を置いてアクセスしてみたところ、画像が表示された。

```text:レスポンスヘッダ
HTTP/1.1 200
Accept-Ranges: bytes
Content-Type: image/png
Content-Length: 1568934
Date: アクセス日時
```

きちんと Content-Type も `image/png` になってる。
試しにテキストファイルに変更してみたらちゃんと `text/plain` になってくれた。
これは便利。

下記参考ページにも書いてあるけど、いちいち自分で `HttpServletResponse` 使ってうにょうにょしなくて良いのが素晴らしい。
というかそのへんを上手いことラップしてるフレームワークなんだから、Http何某の類を直接触るのは極力避けたいですよね。

**参考：**

- [Spring MVCのコントローラでの戻り値いろいろ ＞ ファイルのダウンロード - Qiita](https://qiita.com/tag1216/items/3680b92cf96eb5a170f0#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89)




## ■共通のエラーハンドラを用意しておく。

いわゆる **集約例外ハンドラ** の実装です。

### ＞ コントローラ単一での例外ハンドラ

まずは、単一コントローラに対する例外処理の集約について。

RestController に `@ExceptionHandler` でアノテートしたメソッドを用意します。
これで、コントローラで発生した未トラップの例外（いわゆるunhandling-exception）を纏めて処理できます。
C#とかで言う所の `AppDomain.UnhandledException` みたいな使い方が出来るアレですね。

```java:例外ハンドラ
	@ExceptionHandler
	private ResponseEntity<String> onError( Exception ex ) {
		
		log.error( ex.getMessage(), ex );
		
		HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
		String json = JsonMapper.map()
				.put( "message", "API エラー" )
				.put( "detail", ex.getMessage() )
				.put( "status", status.value() )
				.stringify();
		
		return new ResponseEntity<String>( json, status );
	}
```

登場する `JsonMapper` っていうのはぼくが `com.fasterxml.jackson.databind.ObjectMapper` をラップして作ったユーティリティ（[実装内容はコチラ - Gist](https://gist.github.com/sugaryo/0237c1a334f80e8fc6ca75b36393536a) ）です。

ここではエラー用のサンプルBeanを作るのが面倒だったので、単にJSON文字列を作ってるだけです。
実際の開発ではちゃんと `ErrorResponse` 的なものを作って `ResponseEntity<ErrorResponse>` として全体で統一したほうが良いでしょう。



で、コイツの検証用にこんなエンドポイントを追加。

```java:検証用エンドポイント
	@RequestMapping("test/ex")
	public String testException() throws Exception {
		
		throw new RuntimeException( "エラー発生" );
	}
```

で `http://localhost:8080/api/test/ex` にアクセスしてみるとこんな感じ。

```json:レスポンス
{"detail":"エラー発生","message":"API エラー","status":500}
```

```:詳細
Request URL: http://localhost:8080/api/test/ex
Request Method: GET
Status Code: 500 
Remote Address: [::1]:8080
Referrer Policy: no-referrer-when-downgrade
```

### ＞ コントローラ横断での例外ハンドラ

`@ControllerAdvice/@RestControllerAdvice` を用いることで、複数の `Controller/RestController` で `@ExceptionHandlerメソッド` などの特殊なメソッドを共有出来ます。

処理のライフサイクル的には、本来こっちを使って集約例外ハンドラを実装するべきのようです。

```java:集約例外ハンドラ
@RestControllerAdvice
public class RestExceptionHandler {

	@ExceptionHandler
	private ResponseEntity<String> onError( Exception ex ) {
		
		log.error( ex.getMessage(), ex );

		// 以下略
```

こんな感じで、クラスレベルに `@ControllerAdvice/@RestControllerAdvice` をアノテートすることで、コントローラを横断して反応する `@ExceptionHandlerメソッド` を定義できます。

- 参考情報
	- [Spring MVC(+Spring Boot)上でのリクエスト共通処理の実装方法を理解する - Qiita](https://qiita.com/kazuki43zoo/items/757b557c05f548c6c5db)
		- [@ControllerAdvice / @RestControllerAdvice](https://qiita.com/kazuki43zoo/items/757b557c05f548c6c5db#controlleradvice--restcontrolleradvice)
	- [アドバイスの作成 - 独習Spring](https://yo1000.gitbooks.io/self-study-spring/content/chapter10.html)
	- [アプリケーション層の実装 - TERASOLUNA](https://terasolunaorg.github.io/guideline/5.0.0.RELEASE/ja/ImplementationAtEachLayer/ApplicationLayer.html#id1)
		- [@ControllerAdviceの実装](https://terasolunaorg.github.io/guideline/5.0.0.RELEASE/ja/ImplementationAtEachLayer/ApplicationLayer.html#id160)

### ＞ ErrorControllerを作成する

Spring公式の [エラー処理 - spring.io](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-error-handling) にも記載されているが、デフォルトではすべてのエラーは `/error` にマップされる。
SpringBootには標準で `basic-error-controller : Basic Error Controller` なるコントローラが存在する（ちなみにこれはswagger.uiとかで見ると居るのが解る）が、よしなにカスタムしたい場合は自前で [ErrorController実装](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/web/servlet/error/ErrorController.html) をつくりたまえ。

**って書いてあるっぽい。**

> By default, Spring Boot provides an `/error` mapping that handles all errors in a sensible way, and it is registered as a “global” error page in the servlet container. 

> To replace the default behavior completely, you can implement ErrorController

- 参考情報
	- [Springで例外ハンドリング - Qiita](https://qiita.com/NagaokaKenichi/items/2f199134a881a776b717)
	- [Spring Boot のデフォルトエラーメッセージを制御したい - Qiita](https://qiita.com/leonis_sk/items/c954face2c5c1cbf3802)
	- [エラー処理 - spring.io](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-error-handling)

### ＞ 別解：共通の基底クラスを作って共有させる

基底クラスで実装共有させるってのは賛否が分かれそうですが、一応実現手段のひとつとして紹介。

`@ExceptionHandlerメソッド` を持つ共通基底クラスを作成しておき、コントローラ個別に例外ハンドラを持たせる案。

これはコード例は省略して [実験コードと実験結果を纏めたGist](https://gist.github.com/sugaryo/696c6401d6c64acabc006dc669f59d8e) の方を参照して下さい。

**以下、余談。**

共通実装を **「継承によって共有」** ってのは、本来的には **DIコンテナ管理Beanのインジェクションと言う思想に思いっきりぶつかってる** ので良くない実装ではあるでしょう。

ただ、この実験コードは例外処理の共有だけじゃなく **「RestControllerでもテンプレートメソッドパターンが使える」事を示している** ので、そっちの方向で使いみちがあるかなと思います。


## ★RestAPI関連の参考ページ★

この記事はなんと言っても **助走本** を目指していますので、
走り出したらこの辺の豊富な素晴らしいドキュメントを読みに行きましょう。

**■Spring公式■**
まぁまずは公式ガイド。

- [Spring公式ガイド](https://spring.io/guides)

**■Controller関連■**
SpringMVCやコントローラ周辺の参考資料としてはこの辺が勉強になりました。

- [Spring MVCのコントローラでの戻り値いろいろ - Qiita](https://qiita.com/tag1216/items/3680b92cf96eb5a170f0)
- [SpringBoot使い方メモ＞Spring MVC の簡単な使い方メモ - Qiita](https://qiita.com/opengl-8080/items/05d9490d6f0544e2351a#spring-mvc-%E3%81%AE%E7%B0%A1%E5%8D%98%E3%81%AA%E4%BD%BF%E3%81%84%E6%96%B9%E3%83%A1%E3%83%A2)
- [Spring MVC コントローラの引数 - Qiita](https://qiita.com/MizoguchiKenji/items/2a041f3a3eb13274e55c)
- [REST API設計者のための有名APIのURL例 - Qiita](https://qiita.com/yu1ro/items/f366ded721f03ae91d96)
- [TERASOLUNA Server Framework for Java](http://terasolunaorg.github.io/guideline/5.4.1.RELEASE/ja/)
    - [Spring MVCアーキテクチャ概要](http://terasolunaorg.github.io/guideline/5.4.1.RELEASE/ja/Overview/SpringMVCOverview.html)
    - [Spring Framework理解度チェックテスト](http://terasolunaorg.github.io/guideline/5.4.1.RELEASE/ja/Appendix/SpringComprehensionCheck.html)
   

**■Security関連■**
セキュリティ系に関してはこの辺の記事。

- [SpringBootを使うときに最低限やっておきたいセキュリティ対策 - Qiita](https://qiita.com/tksmaru/items/8b387196ad312f913a2a)
- [Spring Security with Spring Boot 2.0で簡単なRest APIを実装する - Qiita](https://qiita.com/rubytomato@github/items/6c6318c948398fa62275)


**■その他Tips系■**

■POSTが必要なエンドポイントの動作検証＠PowerShell
普通にSpringBootの範囲内でやろうとすると、後述するThymeleafとかで検証用の画面を作ってPOSTするなり、なんか別なプログラム書いてHttpClient使ってPOSTするなり、或いは素直にPostman使うなりになるかと思いますが、PowerShellを使ってコールするというぼく好みの手法が紹介されてました。

- [WebAPIをPowerShellからテストする - Qiita](https://qiita.com/wakoit/items/f11699793b83953adf4f#_reference-d416079d3c71da9f2a74)

一応、Postmanの記事も置いておきます。

- [API開発・テスト便利ツール Postmanの使い方メモ - Qiita](https://qiita.com/zaburo/items/16ac4189d0d1c35e26d1)


# 【逆にWebAPIを呼び出してみよう編】

外部のAPIをコールするHTTPクライアントとして [RestTemplate](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html) というものを使います。

*※JdbsTemplateといい、RestTemplateといい、SpringさんはHogeHogeTemplateってのが好きみたいですね。*



## ■取り敢えず適当なAPIをコールします。

無料で使えて、且つ面倒な認証が必要ない適当なAPIを探して、試しにGETしてみます。

```java:お天気WebサービスのAPIを叩いて返すだけの簡単なお仕事。

	/**
	 * @return 東京の天気情報
	 * 
	 * @see <a href="http://weather.livedoor.com/weather_hacks/webservice">お天気Webサービス - livedoor</a>
	 */
	@RequestMapping( value="weather/tokyo"
			, produces=MediaType.APPLICATION_JSON_VALUE
			, method=RequestMethod.GET)
	private String call() {
		
		// "http://localhost:8080/api/weather/tokyo" でアクセス。
		
		RestTemplate rest = new RestTemplate();
		
		final String cityCode = "130010"; // 東京のCityCode
		final String endpoint = "http://weather.livedoor.com/forecast/webservice/json/v1";
		
		final String url = endpoint + "?city=" + cityCode;
		
		// 直接Beanクラスにマップ出来るけど今回はめんどくさいのでStringで。
		ResponseEntity<String> response = rest.getForEntity(url, String.class);
		
		String json = response.getBody();
		
		return decode(json);
	}
	
	// いわゆる日本語の２バイト文字がunicodeエスケープされてるので解除。
	private static String decode(String string) {
		return StringEscapeUtils.unescapeJava(string);		
	}
```

実用的にするなら取ってきた情報をそのまま返すんじゃなくて、きちんとBeanにマップした上で必要な情報だけ返すとか、複数API組み合わせていい感じに纏めて返すとか、付加価値的な物があった方が良いけど。
取り敢えずここではAPIを単品で叩いてみるってだけ。

`http://localhost:8080/api/weather/tokyo` にアクセスするとこんな感じになりました。

![WS000851.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/cac65cb3-8573-0b7c-d700-42d220e29bc5.jpeg)

```text:返却されたデータのjson.description.text
,"description":{"text":" 本州付近は高気圧に覆われていますが、東海道沖から伊豆諸島南部は気圧
の谷となっています。

【関東甲信地方】
 関東甲信地方は、晴れまたは薄曇りとなっています。

 21日は、高気圧に覆われてはじめ晴れる所もありますが、気圧の谷や湿
った空気の影響で次第に曇るでしょう。夜は、伊豆諸島と沿岸部を中心に、
雨や雷雨となる所がある見込みです。

 22日は、前線や気圧の谷の影響により曇りや雨で、伊豆諸島と沿岸部で
は、はじめ雷を伴う所があるでしょう。夜は、冬型の気圧配置となるため、
おおむね晴れますが、長野県北部と関東地方北部の山沿いでは雨や雪の降る
所があり、長野県では雷を伴う所がある見込みです。

 関東近海では、21日から22日にかけて、うねりを伴って波が高いでし
ょう。船舶は高波に注意してください。

【東京地方】
 21日は、はじめ晴れますが次第に曇りとなり、夜遅くには雨となるでし
ょう。
 22日は、曇りで、昼前から昼過ぎにかけて雨となる見込みです。"
, ...
```

ちなみに、使ったAPIが返してくるJSON文字列内の日本語部分がunicodeエスケープされており、元に戻すのに [apache-StringEscapeUtils](https://commons.apache.org/proper/commons-text/apidocs/org/apache/commons/text/StringEscapeUtils.html) を使ってるので、pomに追加してimportして下さい。


```xml:pom.xml
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-text</artifactId>
   <version>1.6</version>
</dependency>
```

```java:import
import org.apache.commons.text.StringEscapeUtils;
```

- [Apache commons-text](http://commons.apache.org/proper/commons-text/) 

## ■exchangeで色々と細かく指定しながらコールする。

ここまでで紹介した機能で、非常にカンタンなGETやPOSTは出来るようになりました。

が、現実的な難易度の実装で行くと、前述の説明で書いたような **「特定URLに向けてHttpMethodを指定してアクセスするだけ」** というレベルで済む事はまぁまず無いと思います。
実際の開発になると **「認証情報をヘッダに含めてリクエスト飛ばす」** など、他にも色々とやらないといけない事が出て来ますよね。


### ＞ RequestEntity/ResponseEntity

リクエストやレスポンスに関して、細かくやりたいことがある場合は、それぞれ `org.springframework.http.RequestEntity` と `org.springframework.http.ResponseEntity` を使用します。

- spring framewrok
    - [RequestEntity](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/RequestEntity.html)
    - [ResponseEntity](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html)


実際にコード見せた方が解り易いと思うので、 **ヘッダを指定してJSON文字列を指定エンドポイントにPOSTする簡単な実装例** を示します。


```java:import
import java.net.URI;
import java.util.Map;

import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
```


```java:指定エンドポイントに指定のヘッダ情報を付与してPOSTする実装例

	// 後述する幾つかの差し替え実装は省略。（詳しくはこの後の「RestTemplateラッパー三点セット」のあたりを参照）
	private final RestTemplate rest = new RestTemplate();
	
	/**
	 * POST実装例.
	 * 
	 * 指定したエンドポイントに対して {@code json} データをPOSTし、結果を返す。
	 * 
	 * @param url     エンドポイント
	 * @param headers リクエストヘッダ
	 * @param json    送信するJSON文字列
	 * @return 正常に通信出来た場合はレスポンスのJSON文字列を、<br>
	 *         正常に通信出来なかった場合は {@code null} を返す。 
	 */
	public String post(String url, Map<String, String> headers, String json) {
		
		RequestEntity.BodyBuilder builder = RequestEntity.post( uri( url ) );
		
		for ( String name : headers.keySet() ) {
			String header = headers.get( name );
			builder.header( name, header );
		}
		
		RequestEntity<String> request = builder
				.contentType( MediaType.APPLICATION_JSON_UTF8 )
				.body( json );
		
		ResponseEntity<String> response = this.rest.exchange( 
				request, 
				String.class );
		
		return response.getStatusCode().is2xxSuccessful() ? response.getBody() : null;
	}
	
	private static final URI uri( String url ) {
		try {
			return new URI( url );
		}
		// 検査例外はうざいのでランタイム例外でラップして再スロー。
		catch ( Exception ex ) {
			throw new RuntimeException( ex );
		}
	}
```


`RequestEntity` には `#post()` や `#get()` というHTTPメソッドに対応したbuilderを生成するAPIがあり、何となく使い方は見てすぐ解るかと思います。
上記はPOSTの実装例ですが、GETやDELETEでも大きな違いはありません。

こんな感じで、例えば ヘッダに `Authorization Barer` 認証トークンを埋めてリクエストする、みたいな事に対応します。



## ■4xx系／5xx系で例外を発生させなくする方法。

RestTemplate先生は、デフォルトのままだと2xx系以外のHttpStatusコードが返ってきた時に強制的に例外を発生させてきます。

| HttpStatus | スローされる例外 |
|----|----|
| 4xx | HttpClientErrorException |
| 5xx | HttpServerErrorException |
| xxx | UnknownHttpStatusCodeException |

基本的にはこれで困る事は少ないと思いますが、システム要件的にもっと詳細情報を拾う必要があったり、例外ルートではなく正常ルートできちんと処理したいというケースも有り得ます。
*（実際、今回の開発では外部APIコールに対する詳細情報が必要だったので、例外発生だと不都合でした）*

こういった場合は、**RestTemplateのデフォルトエラーハンドラを差し替え** てやります。

```java:RestTemplateをラップしたHttpClientクラス

	private static class QuietlyHandler extends DefaultResponseErrorHandler {
		
		@Override
		public void handleError( ClientHttpResponse response ) throws IOException {
			// 何もしない
		}
	}
	
	protected final RestTemplate rest;
	
	public HttpClient() {
		this.rest = new RestTemplate();
		this.rest.setErrorHandler( new QuietlyHandler() );
	}
```

デフォルトで使用されている `DefaultResponseErrorHandler` を継承して `#handleError` をオーバーライドして **NOP（なにもしない）処理で上書き** しています。
*（デフォルト実装ではここで4xx/5xx系の場合に前述の例外をスローする処理が働きます）*

あとは、使用する `RestTemplate` インスタンスの `ErrorHandler` プロパティに自前のエラーハンドラを設定してやるだけ。
これで、4xx/5xx系のステータスコードの場合でも、2xx系と同じ正常ルートでレスポンスを受け取れるようになります。

なお、この場合はステータスコードが `2xx系` なのかそれ以外なのか判定する必要があるので [ResponseEntity.StatusCodeプロパティ](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.html#getStatusCode) で取得できる [HttpStatus](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/HttpStatus.html) の各判定を参照します。

```java:ResponseEntity.StatusCode

		HttpStatus status = response.getStatusCode();
		boolean information = status.is1xxInformational();
		boolean success     = status.is2xxSuccessful();
		boolean redirection = status.is3xxRedirection();
		boolean clientError = status.is4xxClientError();
		boolean serverError = status.is5xxServerError();
		boolean error       = status.isError(); // clientError || serverError
```

**参考：**

- [【Spring】RestTemplateが投げる例外クラスまとめ - Qiita](https://qiita.com/shotana/items/88b120432e694c9b63f6)
- [RESTクライアント - TERASOLUNA](http://terasolunaorg.github.io/guideline/5.4.1.RELEASE/ja/ArchitectureInDetail/WebServiceDetail/RestClient.html)


## ■PATCH だけちょっと注意。

本件は先に参考を載せておきます。

- [RestTemplateに追加されたpatchForObjectはデフォ実装だと実質使えません - Qiita](https://qiita.com/kazuki43zoo/items/ec5dd8d8ef0aab999b19)
- [RestTemplate PATCH request - StackOverflow](https://stackoverflow.com/questions/29447382/resttemplate-patch-request)
    - **※ 雑な和訳 ※**
    - 「ヘイTom、ボクはPATCHリクエストを投げたいんだがエラーが出るんだ、どうしたらいい？」
    - 「やあBob、キミのRestTemplateインスタンスにHttpRequestFactoryを食わせてやれば、きっと幸せになれるよ。」
    - 【注意】リンク先にはトムもボブも出てきません。

という事で、大変便利な `RestTemplate` 先生ですが、せっかく [patchForObject](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#patchForObject-java.lang.String-java.lang.Object-java.lang.Class-java.util.Map-) や [patchForEntity](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html#postForEntity-java.lang.String-java.lang.Object-java.lang.Class-java.util.Map-) といった **いかにもPATCHメソッドが使えそう** なAPIがあるのに **デフォルトでは使えない** っていうね。

で、`StackOverflow` でトム(仮)が「ApacheのHttpComponentsClientHttpRequestFactoryを使えば良いよ」と言ってpomを貼ってくれてますが、`SpringFramework3.1` からは `org.springframework.http.client.HttpComponentsClientHttpRequestFactory` ってのが追加されてて、どうやらコイツが内部的にApacheの某を使っているのでこれを使えば良さそう。

- [HttpComponentsClientHttpRequestFactory - SpringFramework](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.html)

> ClientHttpRequestFactory implementation that uses Apache HttpComponents HttpClient to create requests.

**雑な和訳**

> コイツはリクエストを生成するのに「Apache HttpComponents HttpClient」を使用したタイプのClientHttpRequestFactory実装だぜ。

的な事が書いてあります。
詳しく知りたい人はChrome先生に翻訳して貰えばいいと思います。
（最近の翻訳ってメチャクチャ精度上がってるよね）

ということで、前置きが長くなったけどRestTemplateでPATCHを使いたい場合はこんな感じ。

```xml:pomに追記
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.10</version>
		</dependency>
```

上記の通り、内部的に `org.apache.httpcomponents` を使用してるので、この依存関係を追加しとかないとあとで `ClassNotFoundException` が出ちゃうよ。

```java:import
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
```

```java:RestTemplateでPATCHを使用する。
		RestTemplate rest = new RestTemplate();
		
		HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
		
		rest.setRequestFactory(requestFactory);
```

これでも別に問題なく動くけど、下記のタイムアウト設定の方もあわせてやっといたほうが良いですね。


## ■タイムアウト設定を弄る方法。

タイムアウト設定に関しても、`RestTemplate` に設定するのではなく、上記PATCHの時に使った `RequestFactory` に対して設定します。

```java:RestTemplateへのtimeoutの設定

		RestTemplate rest = new RestTemplate();
		
		SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
		requestFactory.setConnectTimeout(connectTimeout);
		requestFactory.setReadTimeout(readTimeout);
		
		rest.setRequestFactory(requestFactory);
```

PATCHを使う必要がないのであれば、デフォルトの `SimpleClientHttpRequestFactory` を使えば追加のjarが不要ですね。


## ■RestTemplateラッパー三点セット

という事で、それなりの規模の開発をするのであれば、RestTemplateを各所で直接使用するのではなく、ラップした共通部品を作って以下の３点セット対応をした方が無難かと思われます。

- デフォルトエラーハンドラの差し替え
- PATCH対応
- タイムアウト設定

まぁ、その辺は開発指針やプロジェクトの体制にもよりますけど。

## ★外部API呼び出しの参考ページ★

- 参考情報
	- [SimpleClientHttpRequestFactory - SpringFramework](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/client/SimpleClientHttpRequestFactory.html)
	- [RestClient#ClientHttpRequestFactory - TERASOLUNA](https://terasolunaorg.github.io/guideline/5.2.0.RELEASE/ja/ArchitectureInDetail/WebServiceDetail/RestClient.html#clienthttprequestfactory)
- 参考実装
	- [RestClient - sugaryo@Gist](https://gist.github.com/sugaryo/18653b1bb84bad2d1a1e59f0a68b89c3) 



# 【Thymeleafで画面を作ってみよう編】

## ■SpringBootがサポートするテンプレートエンジン

- SpringBoot のサポートする [テンプレートエンジン](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-spring-mvc-template-engines)
    - Groovy
    - Thymeleaf ★
    - FreeMaker
    - Mustache

今回は `Thymeleaf（タイムリーフ：タイムの葉っぱ）` を使用します。

- タイムリーフの利点
    - 独自タグを使用しない *（メタ属性を仕込む）* ため、HTMLを汚さない。 **★**
    - HTMLを汚さないので、そのままブラウザで表示できる。<br>
	*（サーバ処理なしでも画面表示できる）*
    - そのままブラウザで表示できるので、デザイナと実装の親和性が高い。<br>
	*（と、いわれている）*
	- HTML用 *（view-template）* だけでなく、プレーンテキスト用にも使える。 **★**<br>
	*（いわゆる mail-template のような使い方も出来る）*
    - 値をバインドするEL式が比較的わかりやすく、学習コストが低い。<br>
	*（たぶん。JSFとかやってた人なら「ふーん、なるほどね」って感じで見れると思う。）*
    - [日本語のドキュメント](https://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf_ja.html) が充実している。<br>
	*（英語が苦手なフレンズでもあんしん！！）*

## ■とにもかくにも画面だしてみよっか。

取り敢えずバックエンドのモデル（コントローラ）なしで出来る、まっさらなハリボテ画面だけ出してみましょう。

`resource/template/index.html` を作成し、内容をこんな感じにします。

```html:index.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <title>SpringBoot - テスト用画面</title>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <meta charset="UTF-8" />
  <style type="text/css">
  	form {
  		border-style: solid;
  		border-color: black;
  		border-width: 1px;
  		margin: 5px;
  		padding: 10px;
  	} 
  </style>
</head>
<body>

	<h1 th:text="'これはThymeleafですか？'">ｈｔｍｌ－見出し１</h1>
	はい、SpringBootで推奨されています。
	
</body>
</html>
```

兎にも角にも表示させてみましょう。

デプロイ*（っていうか Run as SpringBootApp）*して `http://localhost:8080` にアクセスします。
すると、こんな画面が出た筈。

![WS000205.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/e6a1ad9c-b723-31d0-8322-a4c49e229738.jpeg)


次に、 **この `resource/template/index.html` を直接ブラウザで** 開いてみましょう。
今度は、こんな画面が出た筈。

![WS000208.JPG](https://qiita-image-store.s3.amazonaws.com/0/170347/0ff2b1b0-41a0-6130-06fe-0a741f19befb.jpeg)

```html

	<h1 th:text="'これはThymeleafですか？'">ｈｔｍｌ－見出し１</h1>
```
この部分に違いが出ていますね。

直接ブラウザでhtmlファイルを表示した場合、`th:text` という属性は標準HTMLに存在しないなので華麗にスルーされ、普通にタグボディに設定されている `ｈｔｍｌ－見出し１` という見出しが表示されます。
対して、`localhost` で（つまりサーバ側のテンプレートエンジンThymeleafを通して）表示したHTMLレスポンスでは、`th:text`属性値が処理されてタグボディに埋め込まれて `これはThymeleafですか？` という見出しが表示された、という形です。

と言ってもこれだけやっても別に何も嬉しくない。
サーバ側で動的な値を埋め込んだりしてこそ意味があるので、なんとなーくその辺を追加していきましょう。



## ■取り敢えず日付時刻を出すだけの画面をつくる。

すげーシンプルに、静的な値ではなく動的な値で、且つインプットを必要としない、最小構成的な物はなにか無いかな。
と思って考えてみた結果 **「取り敢えず日付時刻でいいんじゃネ？」** と思ったので **日付時刻** を出すだけの画面 `what time is it?` を作ってみます。

### ＞ What time is it コントローラ の作成

まず、コントローラのガワだけ作っちゃいましょう。

お好きなパッケージ（ぼくの場合は `[ProjectRoot].app.controller.view` に作りました）にWhatTimeIsItControllerクラス（クラス名は何でも良い）を作り、[@Controller](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Controller.html) をアノテートします。

```java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@RequestMapping("view/what-time-is-it")
@Controller
public class WhatTimeIsItController {
	// まだじっそうしてないよ。
}
```

実装内容はまだりません、これから作り込んで行きますが、その前に **View（html）** の方を用意しちゃいましょう。

**※ コントローラの分割レベルとかも議論になるかと思いますが、ここでは（助走本のレベルを超えてるので）華麗にスルーします。**


### ＞ What time is it 画面HTML の作成

次に、これから作る画面のHTMLファイルを作成しましょう。

`source/main/resources` 配下に `templates` フォルダがあって、現状 `index.html` が置いてあるかと思います。

![WS000914.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/e6dac37b-d1ab-9598-7527-2f7e95869c06.jpeg)

実際の業務開発ではこの辺は良い感じにフォルダ構成とか切っていくと思いますが、面倒なのでここでは直置きしちゃいましょう。

```html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
	<title>sugaryo.spring-boot-tips</title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<meta charset="UTF-8" />
	<link th:href="@{/css/common.css}" rel="stylesheet"></link>
	<script th:src="@{/js/common.js}"></script>
</head>
<body>

	<h1 th:text="'■What time is it?■'"></h1>

	<div class="border">
		まだなにもないよ。
	</div>

</body>
</html>
```

ハイ、こんな感じ。

![WS000915.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/3abf9542-9b7b-33d5-ea9f-5f3df7c61f24.jpeg)

それではこれからコントローラで動的な値を生成し、それを埋め込んでViewテンプレートに渡し、Thymeleafの機能を使ってHTMLに埋め込んでブラウザの画面に表示する所までやってみましょう。

このあとやる事はだいたいこんな感じ。

- コントローラに画面のパス（ `@RequestMapping` ）を設定。
- コントローラ（サーバ処理）で **動的な値** を生成。<br>（今回は単純に日付時刻の文字列を作る）
- SpringMVCのお作法に則って、生成した **動的な値** を設定。
- 設定した **動的な値** を、ThymeleafのViewテンプレートで参照。<br>（詳細は後述）
- ブラウザで画面にアクセスすると、サーバ処理で生成した **動的な値** が表示される、、、 **はず！**

### ＞ What time is it コントローラ の実装

まずコントローラのクラスにこんなメソッドを作ります。

```java
	@GetMapping()
	public String view( Model model ) {
		
		return "wtii"; // ここで返すのはView名、つまり `wtii.html` の "wtii" を返す。 
	}
```

コメントに書いた通り、基本的にViewController（ `@Controller` でアノテートした画面のコントローラ）は、**String（ビュー名）を返すメソッド** を実装します。
厳密には **ビュー名 ＋ メタ情報** で `:forward` を指定したりするんですが、取り敢えずここでは **「遷移したい画面のView名を返すと、SpringBoot先生がよしなに画面遷移してくれるんだな」** とだけ思ってください。

ほんで、これだけだとまだ固定の画面を出すだけで、最初に作った `index.html` と大差ありません。

ので、これから上記メソッドを改造してサーバ側の日付時刻文字列を生成して埋め込む所まで作り込みます。

```java:importの追加
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import org.springframework.ui.Model;
```

```java:viewメソッドの変更
	@GetMapping
	public String view( Model model ) { // ①
		
		String now = LocalDateTime.now().format( DateTimeFormatter.ISO_DATE_TIME ); // ②
				
		model.addAttribute( "datetime", now ); // ③
		
		return "wtii";
	}
```

**コントローラでやる事はこれだけです。**

以下、変更点に関してなんとなく解説。

- **変更点 ①**

まず、メソッドに `org.springframework.ui.Model` を受け取るようにパラメータを追加しています。

SpringBoot先生は大変頭が良いので、我々が **「コレを受け取りたいよー」** ってメソッドパラメータに持たせておけば、渡せるものを渡してくれるのです。
この場合、 `MVC:Controller` が `MVC:View` までデータを渡すために使用する [ui.Model インタフェース](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/ui/Model.html) を受け取るように宣言しています。

- **変更点 ②**

で、サーバ処理として `LocalDateTime.now().format();` でサーバ側の現在時刻をもとに日付時刻の文字列を生成しています。
<br>
**（フォーマットはめんどくさいので、定義済みの `ISO_DATE_TIME` にしてますが、好きなフォーマットにして構わないです。実際のシステムでは「ログインユーザの設定で変更できる」なんて要件もありそうですよね。）**

まぁ、ここは本題とはあまり関係ないので値が動的なら何でも良いです、何なら適当に `UUID.randomUUID().toString();` とかでも良いです。

- **変更点 ③**

そして `model.addAttribute( "datetime", now );` で値を設定しています。

**※ 今回、ここでは詳しく説明しませんが [Model](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/ui/Model.html) のほかに [ModelAndView](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/servlet/ModelAndView.html) というものがあります。<br>（ざっくり概要だけ書いておくと、上記サンプル実装ではView名の指定とModelへのデータ設定が別れていますが、これを単一のオブジェクトへの操作とするのがModelAndViewさんです）**

では、Controllerの実装は終わったので、次にViewの方に手を入れましょう。


### ＞ What time is it 画面HTML の実装

Viewの方はもっと簡単です。

```html:コントローラで生成したデータの表示
<body>

	<h1 th:text="'■What time is it?■'"></h1>
	
	<div class="border">
		<p th:text="${datetime}"><p>
	</div>

</body>
```

これだけ。

追加したのは `<p th:text="${datetime}"><p>` の部分。
タグは `<p>` にしたけど、別に今は表示出来れば何でも良いです。

説明の前に、動きを見てみた方がテンション上がると思うので、前回の `index.html` の時同様、SpringBootApplicationを起動したら `http://localhost:8080/view/what-time-is-it` にアクセスして実際にブラウザで見てみましょう。

![WS000918.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/50ad18d6-8e6b-12b5-5113-b5d688a9ecda.jpeg)

ハイ、Viewテンプレートの `<p th:text="${datetime}"><p>` 部分に、見事サーバ側の日付時刻 *(つってもlocalhostだけどｗ)* が表示されましたね。

- **軽く解説**

ここで指定してる `datetime` というのは、さっきコントローラで `model.addAttribute( "datetime", now );` した時の `"datetime"` をキーに指定して、中身の値を引っ張り出してる訳です。
普通のJavaのコードで `Map.get( "datetime" );` って書いたようなもんです。

要するにContorollerで `Map.put( "datetime", now );` したものをViewが `Map.get("datetime");` して取り出すようなイメージ。

コードの字面が `model.addAttribute( "datetime", now )` と `${datetime}` に変わっただけです。

- **EL式**

さて、この `Map.get` みたいなことをしている `${}` コイツは一体何者なんでしょうか？

コイツは **式言語** 、別名 **EL式** と呼ばれるものです。

## ■式言語：SpEL - Spring Expression Language


### ＞ EL式（SpEL式）の概要

- **概要**

`SpringBoot＋Thymeleaf` での開発では、データモデル`model`からビュー`view`にデータを渡す（データバインドする）には **SpEL式という仕組み** を利用します。

JavaEE(JSF)時代に、ManagedBeanからxhtmlにデータバインドするのにEL式を触ってた人なら馴染み深いかと思います、 **あんな感じのアレ** です。

**※ EL式自体はかなり昔からある技術で、Spring固有のものではないです。**

**Sp**ringの**EL式** として拡張実装されたものなので、正式名称としては **「SpEL式」** というようですが、別にここはそんなに重要じゃないので普通に **「EL式」** で良いでしょう。


- **EL式 is 何？**

EL式を使った経験のない人向けにざっくり説明すると、さっきの [model.addAttribute](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/ui/Model.html#addAttribute-java.lang.String-java.lang.Object-) で仕込んでおいたデータを、ビューであるHTMLに埋め込む時に使ったのが **EL式** です。

この **EL式のルールに則って記述した部分** は、HTMLのビューテンプレート上で **特別な意味を持つ** ようになり、テンプレートエンジン *（今回の場合はThymeleafさん）* によって処理されます。

- **さっきの What time is it での具体例**

具体的には `<p th:text="${datetime}"><p>` の部分ですね。

`${datetime}` この部分がEL式になっており、`datetime` という識別子（キー）のデータに置き換えて貰う、という記述になっています。
つまり、`model.addAttribute( "datetime", now )` で与えたデータ（この場合は `2019-12-04T16:40:10.5164923` という文字列ですね）が使用されます。

そして、その置き換えたデータが `th:text` というThmeleafの独自属性に与えられるので、テンプレートエンジンがHTMLをレンダリングする時にこのデータを使用して `<p>タグ` のテキスト要素として埋め込まれた訳です。

要するに、EL式とは **Thymeleaf先生とお話するための言語** ですね。



### ＞ EL式のいろいろな機能

EL式の最もシンプルで基本的な使い方が、先程の例のような「addAttributeで埋め込んでおいたデータを取り出す」でしょう。

勿論EL式には他にも様々な機能があります。
**条件判定** ができたり、Mathクラスなどの **staticメソッドを利用** できたり、他にもリクエストパラメータ`param`やセッション`session`や下記ユーティリティなど **よく使うオブジェクトがデフォルトで提供** されていたりします。

- [Thymeleafチートシート#ユーティリティオブジェクト - Qiita](https://qiita.com/NagaokaKenichi/items/c6d1b76090ef5ef39482#%E3%83%A6%E3%83%BC%E3%83%86%E3%82%A3%E3%83%AA%E3%83%86%E3%82%A3%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88)
	- `#strings`
	- `#numbers`
	- `#bools`
	- `#dates`
	- `#calendars`
	- `#objects`
	- `#arrays`
	- `#lists`
	- `#maps`
	- `#sets`

これだけ色々と揃っていれば、バックエンドのロジック無しでもEL式だけでも、多少の機能なら作れちゃいますね。

### ＞ ４つの式 [変数式／メッセージ式／リンク式／選択変数式]

ちなみに、先程説明した `${datetime}` というEL式ですが、これはThymeleafに於ける式四天王 **「標準式構文」** のひとり `変数式` というものです。

- 標準式構文
	- `${変数式}`
	- `*{選択変数式}`
	- `#{メッセージ式}`
	- `@{リンク式}`

それぞれざっくり説明すると、こんなかんじ。

- **変数式**

既に説明したとおり、埋め込んでおいた変数を参照する式。

この辺を更に詳しく知りたい人は `OGNL` とかのワードでググると良いでしょう、ここでは省略します。

- **選択変数式**

`th:object` や `th:field` と合わせて使用する、オブジェクトのショートカットポインタみたいな式。

例えば `th:object="$(hoge)"` として `hoge` をオブジェクトに仕込んでいるタグ内で、 `*{id}` とすると `${hoge.id}` 相当、`*{name}` とすると `${hoge.name}` 相当の記述になるようです。

なんかあれですね、VBの `WITHステートメント` みたいなアレですね。

- **メッセージ式** 

これは割と解かりやすい、プロパティファイルの外部定義値をバインドするのに使用するものです。

こｋれが「メッセージ」という名前になってるのは、一般的に多言語対応とかで `message.properties` がよくいるから、ですかね？

- **リンク式**

超便利なのがこのリンク式。

これはコンテキストパスを補完して良い感じにURLを記述できるスグレモノです。

何より便利なのが、パス変数を使って **動的なURLを表現する事も出来る** ということ。

例えば `@{/resource/{id}/detail(id=${resourceId})}` みたいな事ができます。

この記述を細かく見ていくと `/resource/{id}/detail` という相対パスがあり、このパスの中には `{id}` という動的パス要素が含まれており、そのidには `${resourceId}` の変数式で参照した値を使う、、、と言う感じです。


## ■画面をイケメンにしてみよう。

これは趣味ツールの画面なんですけど。

![WS001080.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/12cebbf0-4c1b-6c0f-699c-7dcef36053d6.jpeg)

![WS001081.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/e513eca1-1258-e77a-ad28-bb1cbd35b14b.jpeg)

`What time is it?` 画面はお化粧なしのHTMLすっぴん画面でしたが、Thymeleaf に [Bootstrap](https://getbootstrap.jp/) を組み合わせれば簡単にイケメンは作れます。

### ＞ pomにBootstrapの依存関係を追加

```ini:pom.xml
		<dependency>
			<groupId>org.webjars</groupId>
			<artifactId>bootstrap</artifactId>
			<version>4.5.2</version>
		</dependency>
```

### ＞ ViewテンプレートにBootstrapのCSSを適用

```html
	<link rel="stylesheet" th:href="@{/webjars/bootstrap/4.5.2/css/bootstrap.min.css}">
```

後は普通にBootstrapを利用すれば良き。

ちょっとした管理画面とかなら `Thymeleaf＋Bootstrap` で十分だと思うんだよなぁ。



## ★Thymeleaf画面作成の参考ページ★

**EL/SpEL式 関連**

- [SpringFramework公式（英語）](https://docs.spring.io/spring/docs/4.3.10.RELEASE/spring-framework-reference/html/index.html)
	- [EL式](https://docs.spring.io/spring/docs/4.3.10.RELEASE/spring-framework-reference/html/expressions.html)
- [Spring Boot で Thymeleaf 使い方メモ - Qiita](https://qiita.com/opengl-8080/items/eb3bf3b5301bae398cc2)
	- [SpEL 式](https://qiita.com/opengl-8080/items/eb3bf3b5301bae398cc2#spel-%E5%BC%8F)
- [Thymeleafチートシート - Qiita](https://qiita.com/NagaokaKenichi/items/c6d1b76090ef5ef39482)
- [必要最小限のサンプルでThymeleafを完全マスター - Instructor's memo](http://masatoshitada.hatenadiary.jp/entry/2016/12/11/100528)
	- ４つの記法
- [Thymeleafの標準式構文 - Java好き](https://javazuki.com/articles/thymeleaf-standard-expression-syntax.html)
- [Spring BootでThymeleafを使ってみよう！（応用編）#記述方法 - Marinroad](https://www.marineroad.com/staff-blog/17761.html#i-2)

**Thymeleaf＋Bootstrap 関連**

- [Bootstrap](https://getbootstrap.jp/)
- [SpringBootアプリにBootstrap4を追加（WebJars使用） - One IT Thing](https://one-it-thing.com/2074/)
- [SpringBoot入門 vol.13：Bootstrapでスタイルを整えよう - プログラミング逆引き辞典](https://learning-collection.com/springboot%E5%85%A5%E9%96%80-vol-13%EF%BC%9Abootstrap%E3%81%A7%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB%E3%82%92%E6%95%B4%E3%81%88%E3%82%88%E3%81%86/)
- [Spring Boot ThymeleafにBootstrapを追加 - ITSakura](https://itsakura.com/springboot-bootstrap)

もちろん、他にも **ヘッダフッタの共通化** とか **パーツの部品化** とか色々出来るので、そのへんの作り込みは助走本では割愛します。



# 【スケジュール機能を使ってみよう編】

突然なんですが、ぼくはかなりの **ツイ廃** でして。  
なんかもう呼吸の如くツイートするし、もはや自分でツイートするのも面倒で機械的にツイートするくらいになりました。（病気）

以前、趣味で作ったツールがあって、それをSpringBootでリライトしたものがあるんですが、それに **時報モドキ** の機能があります。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">にゃっぴー。<br>10月1日の 01時 ですよー。<br><br>2020-10-01T01:00:00.0027646 <a href="https://twitter.com/hashtag/%E3%81%AB%E3%82%83%E3%81%A3%E3%81%B4%E3%81%93%E3%83%BC%E3%82%8B?src=hash&amp;ref_src=twsrc%5Etfw">#にゃっぴこーる</a></p>&mdash; エル（絵描きで技術者でカレー屋） 🥐🍙🏴‍☠️☃️ (@ellnore_pad_267) <a href="https://twitter.com/ellnore_pad_267/status/1311334987514351617?ref_src=twsrc%5Etfw">September 30, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

こんな感じで `#にゃっぴこーる` というタグを付けて、毎時ちょうどに時報をぶっ放しています。  
ちなみに、たまに時報以外に切り替えて別なことをツイートするので、時報モドキとなっています。  
まぁそれは置いといて。

この時報ツイートは **SpringBootのスケジュール機能** を使っています。


## ■スケジュール機能を有効化してみよう。

スケジュール機能を利用するには、裏でタイマーを走らせる必要があるので明示的に **機能の有効化** が必要になります。

と言っても別に難しい実装が必要なわけじゃなく、ぶっちゃけ **アノテーション１個付けるだけ** です。

```java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

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

こんな感じで [@EnableScheduling](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/Scheduled.html) でアノテートすると、スケジュール機能を使えるようになります。

確かこの有効化アノテーションを付けるのはアプリケーション配下の *（スキャン範囲に入っていれば）* どこでも良いらしいんですが、  
この手の有効化アノテーションは一箇所に纏まってたほうが解り易い *（管理しやすい）* と思うので、  
ぼくはいつも `@SpringBootApplication` をアノテートしているアプリケーションクラスに纏めて付けています。

同様の有効化アノテーションの例として、非同期処理を行う `@Async` を使うための `@EnableAsync` というものがあります。


## ■スケジュール機能を使ってみよう。

これでスケジュール機能を利用する準備が整いました。  
ということで実際にスケジュール機能を使ってみましょう。

適当なRestControllerや任意のComponentクラスを作って、定期的に実行したいメソッドに `@Scheduled` アノテーションを付与します。

これはもうサンプルコードを見た方が圧倒的に理解が早いと思うので、さっさと参考実装を見てみましょう。

**参考実装**

```java:スケジューラの参考実装
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class NyappiScheduler {
	
	@Autowired
	NyappiCall nyappi;
	
	@Scheduled(cron = "${schedule.nyappi_call.cron}")
	public void cron() {
		this.nyappi.randomcall();
	}
}
```

```ini:properties
schedule.nyappi_call.cron=0 0 * * * *
```

Spring大先生のアノテーションは神ですので、外部定義ファイルの設定値でアノテート出来ます。

上記参考実装の場合、外部定義ファイルの `schedule.nyappi_call.cron` に設定している `0 0 * * * *` という **cronライクな記法** に従って定期実行されます。
つまりこの場合は **「毎時０分０秒に実行しなさい」** ということですね。  
*（もちろん、外部定義ファイルを使用せず、直接リテラルで指定しても構いません）*

なお、`@Scheduled` アノテーションに指定できるのはcron記法だけではなく、以下のようなパラメータを指定できます。

- cron系
	- cron
		- *second*　**※ SpringのScheduled.cronには「秒」フィールドが必要です、crontabに慣れてる人は注意かも。**
		- *minute*
		- *hour*
		- *day of month*
		- *month*
		- *day of week*
	- zone
- 一定間隔系
	- initialDelay
	- fixedRate
	- fixedDelay
	- initialDelay**String**
	- fixedRate**String**
	- fixedDelay**String**

### ＞ cron系

参考実装で示した通り、cronライクな記法で **実行スケジュールを曜日月日時分秒で指定** するパターンです。

なお `zone` は `cron` とセットで指定するプロパティで、cronスケジュールの実行タイムゾーンを指定するものですね。

### ＞ 一定間隔系

一定間隔系は大きく **２種類** に別れます。

- `fixedRate` 一定周期でタスク実行
- `fixedDelay` 一定間隔でタスク実行

**どう違うねん！！**

どちらも一定のスパンでタスク実行される点では同じですが、その **判定基準** が異なります。

- `fixedRate` 前回タスク**開始** を基準に次のタスクを実行。
- `fixedDelay` 前回タスク**終了** を基準に次のタスクを実行。

つまり **「タスクとタスクの間隔が一定；fixedDelay」** なのか、 **「タスク毎の開始間隔が一定；fixedRate」** なのか、ということですね。

ちなみに、タスク自体の実行時間が長くて実行間隔を超えるような場合はタスクが詰まりますが、話が複雑になるのでここでは割愛します。  
前述の `@Async` を併用するなどして、上手いこと設計しましょう。

- `initialDelay`

`initialDelay` は `fixedRate/fixedDelay` に付属するオプションみたいなもので、 **アプリケーションをデプロイしてから初回実行までの間隔** を指定します。



## ■スケジュール（定期実行タスク）機能に関する余談

実際のシステム開発に於いては **スケジュール（定期実行タスク）** を実装する上では以下の選択になるかと思います。

- Scheduledアノテーション使って内部的にcronを回す。
- エンドポイントを用意しといて外部でcronを回してcurl叩く。

個人的には、**後者** の方がスケジュールのハンドリングや管理のしやすさという意味で良いかなと思ってます。  
基本的に、実務でのシステム開発ではほぼ後者を選択する事になるかなと思います。  
よくあるケースとして運用部門の `JP1` とか `SystemWalker` とかの方が主導権握ってて、そっちからキックするのが既定路線だったりしますしね。

趣味ツールに於いても、定期実行した結果をファイルに蓄積して、、、みたいな、いわゆるクローラ的な事をやる上でも後者の方が好都合ですし。

特にそういうバッチ処理的な色合いがなく、シンプルに **定期的にアクションを起こしたいだけ** みたいな時に手軽に実装出来るのが `@Scheduled` アノテーションのいい所ですね。  
外部にキックする「頭脳」を作らず、一人で勝手に動いてくれるのでラクに実装できるのがメリットですかね。

つまり、、、実務ではあまり使わない可能性が微レ存・・・？

あと、実際には `@Scheduled` だけで事足りる事はなく `@Async` でワーカースレッドと組み合わせるのが現実的かと思います。


## ★スケジューラ関連の参考ページ★

ついでなんで趣味ツールのGitHubリポジトリも参考実装として載せときます。

- 参考情報
	- [Spring Framework RELEASE API (current)](https://docs.spring.io/spring-framework/docs/current/javadoc-api/overview-summary.html)
		- [Scheduledアノテーション](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/Scheduled.html)
		- [EnabledSchedulingアノテーション](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/EnableScheduling.html)
		- [CronSequenceGeneratorクラス](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/support/CronSequenceGenerator.html)
	- [crontab](https://www.manpagez.com/man/5/crontab/)
	- [Spring Bootでtaskを定期実行する方法 - Qiita](https://qiita.com/rubytomato@github/items/4f0c64eb9a24eaceaa6e)
	- [Spring Boot: Scheduledアノテーションを使用して、スケジュールされたタイミングでメソッドを実行する - Enjoy*Study](https://blog.enjoyxstudy.com/entry/2017/02/19/000000)
- 参考実装
	- [趣味ツール - sugaryo@GitHub](https://github.com/sugaryo/t4j-boot)
		- [scheduler package - develop](https://github.com/sugaryo/t4j-boot/tree/develop/t4j-boot/src/main/java/sugaryo/t4jboot/app/scheduler)

ちなみに、、、。

まぁ、当たり前の話なんですが。  
タイマーの分解能とかはデプロイしている実行環境のマシンスペックに依存するので、処理が実行されるタイミングの正確さにはある程度バラツキが生じます。

# 【応用編：Thymeleafをプレーンテキストで使ってみよう編】

さて、スケジューラと順番が前後しましたがThymeleafのお話に戻ります。

## ■Thymeleafとはそもそもなにか？

Thymeleafは **汎用のテキストテンプレートエンジン** です。

**汎用** と言っているからには、用途はWeb画面 *（HTMLテンプレート）* だけではありません。  
Thymeleafを使っていれば、単なる定型文 *（プレーンテキスト）* に対しても利用出来るので、
いわゆる **メールテンプレート** のような機能が必要になった場合も簡単に実装できます。

とにかくまず **「動くコードを見て、動きを理解する」** というのが **助走本のモットー** ですので。  
取り敢えずさっさとベタ書きでも動くコードを見てみましょう。


## ■とにかくまずはプレーンテキストで使ってみよう！

専用のネタを考えるのが面倒だったので、今回も趣味ツールの `#にゃっぴこーる` 氏を題材にしてみます。  
*（この話の流れにするために、章構成を前後する必要が、あったんですねぇ）*

スケジュール機能の方でも話題に出したこの **時報モドキ** 機能。  
最初はただの文字列ベタ書きのひっでぇ泥実装だったんですが、今はThymeleaf対応しています。

必要なのは以下の２点

- テンプレートファイルを自作する。
- テンプレートエンジンのインスタンスを生成して使う。

です。

### ＞テンプレートファイルを自作する

とりあえず、にゃっぴこーるの **時報ツイートのテンプレートファイル** はこうなっています。

```thymeleaf:nyappi_call.message
にゃっぴー。
[[${hour}]]ですよー。

[[${timestamp}]] #にゃっぴこーる
```

このファイルをですね、取り敢えず `src/main/resource/templates` の下に `messages` ってフォルダを掘ってブチ込みます。

で、まぁ見たら想像つくと思いますが `[[${識別子}]]` の部分が **プレースホルダ** です。  
アノテーションのあたりでもよく `${識別子}` ってのはよく出て来るので、皆さんもうお馴染みのアレですね。

ここではなんとなく `hour` 及び `timestamp` という識別子でデータバインドするんだなー、って事を何となく理解してくれればよきです。


### ＞ テンプレートエンジンを使って文字列化する

では、上記テンプレートファイルを実際にデータバインドしてテンプレート処理するサンプルコードを見てみましょう。  
まずはシンプルに **そのままコピペして動く** レベルの雑なベタ書きコードを載せます。  
適当な `@RestController` にでも貼り付けて動かして下さい。

```java:テンプレートエンジンの実験
	// これはただの動作確認用
	@GetMapping("test")
	public String test() { 
		
		LocalDateTime now = LocalDateTime.now();
		
		String hour = now.getHour() + "時";
		String timestamp = DateTimeFormatter.ofPattern( "yyyy/MM/dd HH:mm:ss" ).format( now );
		return process( hour, timestamp );
	}
	// 重要なのはこっち。
	private String process( String hour, String timestamp ) {
		
		var resolver = new ClassLoaderTemplateResolver(); // ①
		resolver.setTemplateMode( TemplateMode.TEXT );
		resolver.setPrefix( "templates/messages/" ); // src/mail/resources/templates/messages
		resolver.setSuffix( ".message" );
		resolver.setCharacterEncoding( "UTF-8" );
		resolver.setCacheable( true );
		
		var engine = new SpringTemplateEngine(); // ②
		engine.setTemplateResolver( resolver );
		
		var context = new Context(); // ③
		context.setVariable( "hour", hour );
		context.setVariable( "timestamp", timestamp );
		
		final String message = engine.process( "nyappi_call", context ); // ④
		log.debug( message );
		return message;
	}
```

ではざっくり実装内容を見ていきます。  
せっかちさんは **「実行結果」** を載せておくので、そこまで飛んで下さいｗ

- ① `new ClassLoaderTemplateResolver()`

まず作っているのが [ClassLoaderTemplateResolver](https://www.thymeleaf.org/apidocs/thymeleaf/3.0.1.RELEASE/org/thymeleaf/templateresolver/ClassLoaderTemplateResolver.html)
**テンプレートリゾルバ** と呼ばれるものです。

`Resolver;リゾルバ` とは **「解決するもの」** なので、
テンプレートリゾルバとは **「テンプレートファイルを解決するためのもの」** ですね。  
なるほどわからん、という人は **みんな大好きWikipedia先生** に聞こう！！  
＞ [リゾルバ(Resolver)とは - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%AA%E3%82%BE%E3%83%AB%E3%83%90)

まぁ、なんかカッコイイ名前を名乗ってやがりますが、
要するに **テンプレートファイルの置き場所やなんかを指示してやる設定オブジェクト** ですね。

実際に設定している内容を見てみれば **「ふ～ん、はいはい、なるほどね」** となるでしょう。

|設定|設定値|説明|
|----|----|----|
|TemplateMode      |`TemplateMode.TEXT`     |テンプレートモード。※|
|Prefix            |`"templates/messages/"` |プリフィックス、要するに**フォルダ**。<br> *（/src/main/resources からの相対パス）*|
|Suffix            |`".message"`            |サフィックス、要するに**拡張子**。|
|CharacterEncoding |`"UTF-8"`               |文字エンコーディング|
|Cacheable         |`true`                  |キャッシュの有効／無効|

なるほどなるほど、、、Web画面実装編で `@Controller` が最期に **「View名をreturnしていた」** 意味が何とな～く見えてきましたね。

- [※org.thymeleaf.templatemode.TemplateMode](https://www.thymeleaf.org/apidocs/thymeleaf/3.0.1.RELEASE/org/thymeleaf/templatemode/TemplateMode.html)
	- HTML
	- XML
	- TEXT
	- JAVASCRIPT
	- CSS
	- RAW
	- ~~HTML5~~ @deprecated
	- ~~LEGACYHTML5~~ @deprecated
	- ~~XHTML~~ @deprecated
	- ~~VALIDXHTML~~ @deprecated
	- ~~VALIDXML~~ @deprecated

ちなみになんですけど。  
`ClassLoaderTemplateResolver` コイツ、結構継承階層が深くて。

- ITemplateResolver
	- AbstractTemplateResolver
		- AbstractConfigurableTemplateResolver
			- ClassLoaderTemplateResolver

って感じなんですよね。  
実装上も `ITemplateResolver` で受けたい所ですが、設定するプロパティが案外深いところで実装されてるので、ここは素直に `var` で受けてます。

更にちなみになんですけど。  
ぼくはもともとC#屋さんなので全く違和感ないんですけど、
Javaでinterfaceに `ITemplateResolver` って **Iプリフィックス** 付けてるの珍しくないですか？

個人的にはこれ凄く好きです。  
というか `XxxXxxImpl` っていう **Implサフィックス文化** が嫌いです。  
*（まぁでも、一番アレなのは一貫性がないことだよなぁ？）*



- ② `new SpringTemplateEngine()`

お次が [SpringTemplateEngine](view-source:https://www.thymeleaf.org/apidocs/thymeleaf-spring5/3.0.9.RELEASE/org/thymeleaf/spring5/SpringTemplateEngine.html)
主役の **テンプレートエンジン** と呼ばれるものです。

実行しているのは `engine.setTemplateResolver( resolver );` で、さっき作ったテンプレートリゾルバを設定しているだけです。  
こいつが、与えられたテンプレートリゾルバの設定に従って、テンプレートファイルを探しに行って色々やってくれるって事でしょう。  
解りやすいですね。


- ③ `new Context()`

お次が [Context](https://www.thymeleaf.org/apidocs/thymeleaf/3.0.1.RELEASE/org/thymeleaf/context/Context.html) 
**コンテキスト** ですね。

コンテキスト？文脈？はて？？  
なんてことはない、ただの **パラメータ** ですね。

ここではテンプレートのプレースホルダに対応して、以下のパラメータを与えているだけです。

- `[[${hour}]]`      : `context.setVariable( "hour", hour );`
- `[[${timestamp}]]` : `context.setVariable( "timestamp", timestamp );`


- ④ `engine.process( "nyappi_call", context );`

第一オペランドに **テンプレート名** を、第二オペランドに **パラメータ（Context）** を指定して `SpringTemplateEngine#process` を実行します。

こうすると、

1. **TemplateEngine;テンプレートエンジン** に与えておいた **TemplateResolver;テンプレートリゾルバ** を使って、
1. 指定された **テンプレート名** に対応 *（＝ プレフィックス ＋ サフィックス を付与したパスにマッチ）* する **テンプレートファイル** を探しに行き、
1. 与えられた **パラメータ** でテンプレート処理した結果を返してくれる。

という流れですね。



- **実行結果**

```bash:TEST用のAPIをコールした結果
$ curl -XGET -H "Content-Type: application/json" 172.19.32.1:8989/nyappi/api/test
にゃっぴー。
20時ですよー。

2020/10/29 20:02:28 #にゃっぴこーる
```

ログにもこんな感じで出ています。

```log:デバッグログ
2020-10-29 20:02:28.256 DEBUG 10532 --- [nio-8989-exec-2] s.t.a.controller.rest.TestApiController  : にゃっぴー。
20時ですよー。

2020/10/29 20:02:28 #にゃっぴこーる
```

良い感じですね。




## ■TemplateEngineをBean登録して使おう。（その１）

さて、先のコピペ用の参考実装で気になった人もいるでしょう。  
**この `ClassLoaderTemplateResolver` と `SpringTemplateEngine` 、都度都度 `new` しなくてよくね？**  
何らかの形で **シングルトン** にするなり、 **コンテナ管理Beanとして登録** してオートワイヤリングするなり、何かしたいですよね？  
ということで、雑に **Bean定義** してコンテナに突っ込んじゃいましょう。  
実装内容はさっきのサンプルコードの **テンプレートエンジンのインスタンスを生成する所まで** ですね。

```java:テンプレートエンジンのBean定義（ダメなパターン）
    @Bean()
    public SpringTemplateEngine messageTemplateEngine() {
        var resolver = new ClassLoaderTemplateResolver();
        resolver.setTemplateMode( TemplateMode.TEXT );
        resolver.setPrefix( "templates/messages/" ); // src/mail/resources/templates/messages
        resolver.setSuffix( ".message" );
        resolver.setCharacterEncoding( "UTF-8" );
        resolver.setCacheable( true );
		
        var engine = new SpringTemplateEngine();
        engine.setTemplateResolver( resolver );
		
        return engine;
    }
```

何気に、助走本の説明では初登場の `@Bean` アノテーションさん。  
一言でいうと **「コンテナ管理Beanのファクトリメソッドであること」** を示すアノテーションです。  
このファクトリメソッドを使って生成したインスタンスを **Springコンテナさんが管理してくれる** ようになります。  
取り敢えず **「`@Bean` でアノテートしておくと `@Autowire` でインジェクションして使えるよ」** ということだけ理解しとけば取り敢えず良きです。

**※ この「Bean定義」はSpringフレームワークを使用する上で極めて重要な概念になります。後々、詳しく書こうかと思いますが、今はこのくらいで。**

で、このコードを **適当なところ** に実装して下さい。  
適当なって言われても・・・って人は、一旦取り敢えず `@SpringBootApplication` でアノテートしているApplicationクラスに実装しちゃって下さい。

ほんで、さっきのサンプルコードを以下のように変更します。

```java:テンプレートエンジンをコンテナ管理Beanに変更
	@Autowired
	SpringTemplateEngine engine; // ⑤
	
	@GetMapping("test")
	public String test() {
		
		LocalDateTime now = LocalDateTime.now();
		
		String hour = now.getHour() + "時";
		String time = DateTimeFormatter.ofPattern( "yyyy/MM/dd HH:mm:ss" ).format( now );
		return process( hour, time );
	}
	
	private String process( String hour, String time ) {
		
		var context = new Context();
		context.setVariable( "hour", hour );
		context.setVariable( "timestamp", time );
		
		final String message = this.engine.process( "nyappi_call", context ); // ⑥
		log.debug( message );
		return message;
	}
```

変更点は二箇所。

- ⑤ `@Autowired SpringTemplateEngine`

テンプレートエンジンを `@Autowire` でフィールドインジェクションするよう変更。

- ⑥ `this.engine.process`

さっきは `#process` メソッド内で生成していたテンプレートエンジンを、インジェクションしたコンテナ管理Beanを使うように変更。

- **実行結果**

```bash:TEST用のAPIをコールした結果
$ curl -XGET -H "Content-Type: application/json" 172.19.32.1:8989/nyappi/api/test
にゃっぴー。
22時ですよー。

2020/10/29 22:05:58 #にゃっぴこーる
```

はい、上手く行った・・・

**と思ったか？**

この状態で、画面にアクセスしてみてください。

> There was an unexpected error (type=Internal Server Error, status=500).
> 
> An error happened during template parsing (template: "templates/messages/index.message")

**画面が出なくなってるやないけぇ！？！？**

画面なんか弄ってないのに何で動かなくなるの！！

## ■TemplateEngineをBean登録して使おう。（その２）

さて、困った事に **（その１）** でテンプレートエンジンをBean登録して使ったら何故か画面がバグってしまいました、 **バタフライエフェクト** かな？

`An error happened during template parsing` つってるんで、テンプレートをパースする時にコケたぜって言ってますね。  
で、コケたテンプレートは `templates/messages/index.message` だと言っています。

ん？

なんすかこいつ。  
画面なんだからお前は `templates/index.html` じゃないのかと。  
もしかして `templates/messages` で `.message` って、こ、これは、さっき作ったテンプレートリゾルバの設定じゃないか！ *（わざとらしい）*

どうやら、 **もともとHTML用のテンプレートエンジンが動いていた所に巻き込む形で、さっき作ったテンプレートエンジンを差し込んでしまった** ようです。

ということで、まずは先程のコードをこのように修正して下さい。

```java:テンプレートエンジンのBean定義（まだダメなパターン）
	// ⑦
	@Bean
	public SpringTemplateEngine pageTemplateEngine() {
		var resolver = new ClassLoaderTemplateResolver();
		resolver.setTemplateMode( TemplateMode.HTML );
		resolver.setPrefix( "templates/" ); // src/mail/resources/templates
		resolver.setSuffix( ".html" );
		resolver.setCharacterEncoding( "UTF-8" );
		resolver.setCacheable( false );
		
		var engine = new SpringTemplateEngine();
		engine.setTemplateResolver( resolver );
		
		return engine;
	}
	
    @Bean("messageTemplateEngine") // ⑧
    public SpringTemplateEngine messageTemplateEngine() {
        var resolver = new ClassLoaderTemplateResolver();
        resolver.setTemplateMode( TemplateMode.TEXT );
        resolver.setPrefix( "templates/messages/" ); // src/mail/resources/templates/messages
        resolver.setSuffix( ".message" );
        resolver.setCharacterEncoding( "UTF-8" );
        resolver.setCacheable( true );

        var engine = new SpringTemplateEngine();
        engine.setTemplateResolver( resolver );

        return engine;
    }
```

インジェクションしていたところもこのように変更。

```java:
	@Qualifier("messageTemplateEngine") // ⑨
	@Autowired()
	SpringTemplateEngine engine;
```

変更点を纏めてざっくり。

- ⑦  **HTML用に設定したテンプレートエンジンのBean定義を追加。**
- ⑧  **プレーンテキスト用のテンプレートエンジンのBean定義に明示的に管理名を設定。**
- ⑨  **`@Autowire` インジェクションポイントに、⑧で設定した管理名を `@Qualifier` 指定。**

で、これで動かしてみると、

**なんとまだエラーが出ます。**

なかなか見ることのない珍しいエラーだと思うので、まるっと拾ってきました。

```log:SpringBootApplication起動時のエラー
***************************
APPLICATION FAILED TO START
***************************

Description:

Parameter 1 of method thymeleafViewResolver in org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration$ThymeleafWebMvcConfiguration$ThymeleafViewResolverConfiguration required a single bean, but 2 were found:
	- pageTemplateEngine: defined by method 'pageTemplateEngine' in class path resource [sugaryo/t4jboot/SpringBeans.class]
	- messageTemplateEngine: defined by method 'messageTemplateEngine' in class path resource [sugaryo/t4jboot/SpringBeans.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
```

**実に親切なエラーメッセージで感動しますよね！！**

これを載せたかったがために、この章では回りくどい説明をしていました。

ということで有り難いエラーメッセージ様を読んでみましょう、なになに？

> Parameter 1 of method thymeleafViewResolver in ほにゃらら required a single bean, but 2 were found:

**「ほにゃらら.thymeleafViewResolverメソッドでは単一のBeanが必要だが２個指定してんじゃねえかおめぇ」** と。

> - [sugaryo/t4jboot/SpringBeans.class] で定義されてる `pageTemplateEngine` コイツ
> 
> - [sugaryo/t4jboot/SpringBeans.class] で定義されてる `messageTemplateEngine` コイツ

**「２個指定されたのはこれとこれだぞ」** と。

なるほどなるほど。

で、有り難いことにエラーに対する修正アクションが提案されています、神かな？

> Consider marking one of the beans as @Primary,
> 
> updating the consumer to accept multiple beans,
> 
> or using @Qualifier to identify the bean that should be consumed

- どれか一つのBeanに `@Primary` を付けるか
- 複数の `@Bean` が扱えるように修正するか
- 注入する方で `@Qualifier` でBean管理名を明示するか

いずれかを検討しろと。

要するに **「複数のBean（この場合テンプレートエンジン）が見付かってどれ使えばいいか解らんから、どれを使えばいいか解るようにせい」** ということですね。

で、今回は `@Primary` を付けることでエラーを回避します。

```java:テンプレートエンジンのBean定義（OKパターン）
	@Primary // ⑩
	@Bean
	public SpringTemplateEngine pageTemplateEngine() {
		var resolver = new ClassLoaderTemplateResolver();
		resolver.setTemplateMode( TemplateMode.HTML );
		resolver.setPrefix( "templates/" ); // src/mail/resources/templates
		resolver.setSuffix( ".html" );
		resolver.setCharacterEncoding( "UTF-8" );
		resolver.setCacheable( false );
		
		var engine = new SpringTemplateEngine();
		engine.setTemplateResolver( resolver );
		
		return engine;
	}
	
    @Bean("messageTemplateEngine") // ⑧
    public SpringTemplateEngine messageTemplateEngine() {
        var resolver = new ClassLoaderTemplateResolver();
        resolver.setTemplateMode( TemplateMode.TEXT );
        resolver.setPrefix( "templates/messages/" ); // src/mail/resources/templates/messages
        resolver.setSuffix( ".message" );
        resolver.setCharacterEncoding( "UTF-8" );
        resolver.setCacheable( true );
		
        var engine = new SpringTemplateEngine();
        engine.setTemplateResolver( resolver );
		
        return engine;
    }
```

- ⑩  **Thymeleafに使って貰うHTML用のテンプレートエンジンの方に `@Primary` を付与。**

で、これで動かすと、

**漸く正しく動くようになります。**



## ■TemplateをDBに文字列で持ちたいです！

はい、ちょっと脱線しましたが、これでプレーンテキストモードでのテンプレートエンジンを利用出来るようになりました。

これで `src/mail/resources/templates/messages` にテンプレートファイルをばしばし突っ込んでおけば、いろんなテンプレートを使えます。

はい、そうですね。

**ここまで来るとテンプレートファイルを用意するのすらめんどくさい、テンプレートをファイルじゃなくDBか何かで文字列としてそのまま持っておくようにしたくなるのが人情ですよね。**

Thymeleaf3系から追加された [StringTemplateResolver](https://www.thymeleaf.org/apidocs/thymeleaf/3.0.9.RELEASE/org/thymeleaf/templateresolver/StringTemplateResolver.html) というリゾルバを使えば行けるようです。

早速試してみましょう。

```java:import
import org.thymeleaf.templateresolver.StringTemplateResolver;
```

```java:Stringリゾルバを使ったテンプレートエンジンのBean定義
	
    @Bean("stringTemplateEngine")
    public SpringTemplateEngine stringTemplateEngine() {
    	
    	var resolver = new StringTemplateResolver();
    	resolver.setTemplateMode( TemplateMode.TEXT );
    	
        var engine = new SpringTemplateEngine();
        engine.setTemplateResolver( resolver );
		
        return engine;    	
    }
```

デフォルトのテンプレートモード `DEFAULT_TEMPLATE_MODE` が `TemplateMode.HTML` らしいので、TEXTモードだけ変更してリゾルバを設定しました。

```java:参考実装のAPIエンドポイント
	@Qualifier("stringTemplateEngine")
	@Autowired
	SpringTemplateEngine string;
	
	@GetMapping("test/string/{message}") 
	public String test_string_template(@PathVariable String message) {
		
		final String template = "path var message＜[[${message}]]＞, by string template engine.";
		
		var context = new Context();
		context.setVariable( "message", message );
		
		return this.string.process( template, context );		
	}
```

`"path var message＜[[${message}]]＞, by string template engine."` というテンプレートを文字列リテラルで定義し、パス引数を埋めて返すだけの簡単なお仕事。

```bash:参考実装のAPI実行結果
$ curl -XGET -H "Content-Type: application/json" 172.19.32.1:8989/nyappi/api/test/string/hogehoge
path var message＜hogehoge＞, by string template engine.
```

良い感じですね。

例えばこれで、メールテンプレートをマスタ管理しておいて、、、みたいなことも簡単に出来そうですね。

テンプレートの変更頻度にも依存しますが、DB管理しといた方が変更を反映しやすいので便利ですよね。


## ★Thymeleafテンプレートエンジン関連の参考ページ★

- Thymeleaf公式ドキュメント
	- [テンプレートリゾルバ](https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#the-template-resolver)
	- [テンプレートエンジン](https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#the-template-engine)
	- [テキストテンプレートモード](https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#textual-template-modes)



# 【ちょっとしたTips集】

章建てするほどの事もない小ネタや、自分が過去にハマって困った事など。

- Tips内目次
	- [自分の書いたプログラムのデバッグログを出す方法。](#自分の書いたプログラムのデバッグログを出す方法)
	- [実行可能JARとして設定してビルドする。](#実行可能jarとして設定してビルドする)
	- [pom.xml の一行目に謎のエラー出んだけど！！](#pomxmlの一行目に謎のエラー出んだけど)
	- [Swagger 使ってみたいんだけど。](#swagger使ってみたいんだけど)
	- [Service（というかTransactional）ロールバック仕様でハマったこと。](#serviceというかtransactionalロールバック仕様でハマったこと)
	- [JSONを SNAME_CASE で返す設定。](#jsonを-sname_case-で返す設定)
	- [JSONを PrettyPrint で出力する設定。](#jsonを-prettyprint-で出力する設定)
	- [本番運用系の便利エンドポイントを簡単に追加する。](#本番運用系の便利エンドポイントを簡単に追加する)

## ■自分の書いたプログラムのデバッグログを出す方法

ログ自体の設定（`application.properties` や `logback-spring.xml`）の話ではなく、ログレベルの設定の話。

アプリ全体のログレベル設定じゃなく、自分の書いたプログラムのログレベル設定にぷちハマったので書いておきます。
結論から言って普通に `application.properties` に設定出来るんですが **「えっ、そんなのアリなの？」** とちょっと意外だったので。

ルートレベルから指定する方法とかは普通にサラッと紹介されてると思うんですが。

```properties:application.properties
logging.level.root=INFO
```

これだと自分の組んだプログラムにDEBUGレベルが適用できなかったり、
そもそもルートにDEBUGレベル指定するとどっかで永久ループハマって立ち上がらなかったり、
なんか意味解らん動きをしたんですけど、細かい説明が書かれてる記事が無くて困ったんですよね。

で、自分のプログラムのDEBUGログを有効にするにはどうやらこうするようです。

```properties:application.properties
logging.level.root=INFO
logging.level.sugaryo.t4j.demo=DEBUG
!             ^^^^^^^^^^^^^^^^
!             この部分は自分のアプリケーションの名前空間になります
```


この設定だと自分のアプリケーション全体にログレベルが掛かりますが、更にピンポイントで設定も出来るようです。

![WS000355.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/f8f65950-8447-e2ad-0307-ad71ebbddb5a.jpeg)

この設定で実際にログを出してみました。

```java:ログ出力テストコード
		log.info( "INFO ログ" );
		log.debug( "DEBUG ログ" );
		log.warn( "WARN ログ" );
		log.error( "ERROR ログ" );
```
 
```terminal:ログ出力結果
2019-05-16 03:09:46.617  INFO 10036 --- [nio-8080-exec-1] s.t4j.demo.controller.WebApiController   : INFO ログ
2019-05-16 03:09:46.617 DEBUG 10036 --- [nio-8080-exec-1] s.t4j.demo.controller.WebApiController   : DEBUG ログ
2019-05-16 03:09:46.617  WARN 10036 --- [nio-8080-exec-1] s.t4j.demo.controller.WebApiController   : WARN ログ
2019-05-16 03:09:46.617 ERROR 10036 --- [nio-8080-exec-1] s.t4j.demo.controller.WebApiController   : ERROR ログ
```

はい、こんな感じでちゃんとDEBUGレベルでログが出てますね。
この辺ってSpringFramework経験者からすると常識なんでしょうけど、SpringBootから入った自分にはハマりポイントでした。

## ■実行可能JARとして設定してビルドする

実装中に動作確認する意味で動かすには、前述の通り `Run AS ＞ Spring Boot Application` メニューで起動すると思いますが、リリース用とかで実行可能JARをビルドしたいだけの場合。

びっくりしたんですが、デフォルトのままだとダメでした。

実行可能JARとしてビルドするため `pom.xml` に以下の設定追加が必要になります。

`build.plugins.plugin.configuration.executable = true` 

```xml:pom.xml（変更前）
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
```
こいつを

```xml:pom.xml（変更後）
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<executable>true</executable>
				</configuration>
			</plugin>
		</plugins>
	</build>
```
こうします。
この設定をやっておかないと、CentOSに持ってって `service` コマンドでサービス登録して動かそうと思った時に上手くいきませんでした。

あとは `Run As ＞ Maven build` でビルドするとJARが出来上がるんで、持ってってください。

## ■pom.xmlの一行目に謎のエラー出んだけど！！

上の方の `環境構築編` にも書いた通りですが、最新のSTS4（少なくともぼくが確認したのは4.4.0）では解消してました。
軽く調べてみると `m2e connector` のバージョンを変える事で発生しなくなったって人が結構いましたが、STS4本体側で何かしら修正が入ったようです。
（このエラー、出ていても無視して突き進んで問題なかったけど、気持ち悪いは気持ち悪かったから、消えて良かったね）

ということでSTS4のバージョン上げれば消えるよ。

## ■Swagger使ってみたいんだけど

> 取り敢えず最短でSwagger導入してswagger.ui（エンドポイント一覧の画面）を出すところまでやりたい、詳しい作り込みは後でやるから取り敢えずサクッと済ませたい。

っていう人向けの導入手順。

- pom.xmlに依存関係追加
- Swagger有効化
- Swaggerのコンフィグ設定

基本、以下の実装を適当にコピペしてけば動くことは動く。

### ＞ swagger 依存関係追加

```properties:pom.xmlへの追加
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.6.1</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.6.1</version>
		</dependency>
```

### ＞ swagger 有効化

と言っても [@EnableSwagger2](http://springfox.github.io/springfox/javadoc/2.6.1/springfox/documentation/swagger2/annotations/EnableSwagger2.html) でアノテートするだけ。

```java:EnableSwagger2
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@SpringBootApplication
@EnableSwagger2
public class SpringBootTipsApplication {
	// 以下略
```

**※ 後述するコンフィグクラスの方に付けても良いけど、ぼくは基本的にこの手の `EnableXxx` 系のアノテーション（スケジューラを有効化させる `EnableScheduling` とかね）は全部Applicationクラスに付ける事にしてます。そこだけみれば何が有効化されてるのかがわかるように。**

### ＞ swagger コンフィグ設定

```java:SwaggerConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

@Configuration
public class SwaggerConfig {
	
	@Bean
	public Docket doclet() {
		return new Docket( DocumentationType.SWAGGER_2 )
				.select()
				.apis( RequestHandlerSelectors.any() )
				.paths( PathSelectors.any() )
				.build()
				.apiInfo( Api.info() );
	}
	
	private static class Api {
		
		private static ApiInfo info() {
			return new ApiInfoBuilder()
					.title( "タイトル" )
					.description( "説明" )
					.version( "バージョン" )
					.contact( new Contact( "開発者・サイト名", "開発サイトURL", "メールアドレス" ) )
					.license( "ライセンス" )
					.licenseUrl( "ライセンスURL" )
					.termsOfServiceUrl( "利用規約ページURL" )
					.build();
		}
	}
}
```

ちなみに、ベタ書きなのは流石にアレなのでpropertiesファイルから読み込んで来る形にした実装サンプルを [GitHubの方に上げてある](https://github.com/sugaryo/spring-boot-tips/tree/feature/configuration) ので、参考にどうぞ。

### ＞ swagger-ui 動作確認

`http://localhost:8080/swagger-ui.html` にアクセスすれば例の画面がでてきます。
（ポート番号とかは各自のapplication.properties設定にあわせて読み替えて下さい）

![WS000180.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/ed9078ea-1a3d-64d3-158f-15aee113c60f.jpeg)

コントローラ毎にアコーディオンパネルになってて、開くとこんな感じ。

![WS000181.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/170347/2854c762-3657-25a2-0415-6d86edecd20b.jpeg)

前述のコンフィグ設定で、表示するしないの制御とかが細く設定出来るし、エンドポイント実装の方に `@ApiOperation` などのメタ情報を埋めとけばその辺の情報を出すことも出来ます。

細かくは、詳しく解説してくれている記事を読んで下さい。

- 参考資料
    - [Spring Boot + SpringFoxでSwaggerを利用してソースからAPIキュメントを生成する - Qiita](https://qiita.com/NagaokaKenichi/items/b6d4d55a202e6a93d047)
    - [SpringBootを使ってSwaggerに入門してみた - Qiita](https://qiita.com/shuhey_fujiwara/items/e3bfd8d6deaaa22d618c)
    - [Spring BootでSpringFox（Swagger）を試す - abcdefg...](http://pppurple.hatenablog.com/entry/2016/12/18/195253)
    - [SpringfoxでSwaggerAPI仕様書を自動生成する（環境設定編） - developers.io](https://dev.classmethod.jp/beginners/springfox_create_api_specification/)
	- [swagger.core annotations - GitHub](https://github.com/swagger-api/swagger-core/wiki/Annotations)


## ■Service（というかTransactional）ロールバック仕様でハマったこと

### ＞ Transactionalのデフォルトのロールバック仕様

Serviceクラスには `@Transactional` アノテーションを付けてトランザクションをコンテナ管理して貰いますが。

最初気付いた時びっくりしたんですけど、このTransactionalアノテーションは **何故か検査例外の場合にロールバックしない** というのが **デフォルト動作** だったようで。

しかもちょっと調べてみたら、正確には以下の動作だそうで。

- ランタイム例外が発生した場合は普通にロールバックされる。
- でも **検査例外** が発生した場合はロールバックされない。
- でも **SQLException** が発生した場合はロールバックされる。
	- 正確にはSpringフレームワークがDB関連処理で発生する例外を [springframework.dao.DataAccessException](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/dao/DataAccessException.html) のサブクラスに置き換えており、この場合は特別にロールバックしてくれている模様。

なるほど、、、。

### ＞ Transactionalへのロールバック動作指定

Transactionalアノテーションには `rollbackFor` というオプション指定があり **検査例外発生時にもロールバックさせたい場合** は、明示的に `@Transactional(rollbackFor = Exception.class)` とする必要があります。

ちなみに。

ロールバック関連に関しては、上記のような `Class<?>` を指定するタイプの `rollbackFor` をはじめ、以下の４つの指定可能なパターンがあります。


- 例外発生時にロールバックさせたい例外クラスを `rollbackFor` に指定する。
- 例外発生時にロールバックさせたい例外クラス名を `rollbackForClassName` に指定する。
- 例外発生時にロールバック **させたくない** 例外クラスを `noRollbackFor` に指定する。
- 例外発生時にロールバック **させたくない** 例外クラス名を `noRollbackForClassName` に指定する。

Spring経験者にとっては常識なんでしょうけど、案外これってハマりポイントだと思うなぁ、、、。


- 参考情報
	- [【Spring】@Transactionalは検査例外をコミットしてしまうがSQLExceptionはロールバックされる - じゃけぇのあれこれ](https://freelance-jak.com/technology/java/1154/)
	- Springでトランザクション管理 - Qiita
		- [Transactionalアノテーションのプロパティと設定例](https://qiita.com/NagaokaKenichi/items/a279857cc2d22a35d0dd#transactional%E3%82%A2%E3%83%8E%E3%83%86%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E3%81%A8%E8%A8%AD%E5%AE%9A%E4%BE%8B)
	- DataAccess - Spring Framework Documentation
		- [Transactionalアノテーションを使用する](https://docs.spring.io/autorepo/docs/spring/5.1.7.RELEASE/spring-framework-reference/data-access.html#transaction-declarative-annotations)


## ■JSONを SNAME_CASE で返す設定

`@RestController` のメソッドからBeanを返すと、裏で `jackson` 先生が動いてBeanをJSON形式で返してくれますが。  
デフォルトだとこれがJavaライクなキャメルケースのレスポンスになっちゃうんですよね。

これをJSONで一般的な **CAMEL_CASE** にしたいな。  
でも `@JsonProperty` でイチイチ指定するのもダルいし、、、。

### ＞ RestControllerのレスポンス設定

という時は `application.properties (yml)` で以下のように指定すれば良きです。

```ini:application.properties
spring.jackson.property-naming-strategy=SNAKE_CASE
```

### ＞ ObjectMapperのレスポンス設定

また、プログラム中に [ObjectMapper](com.fasterxml.jackson.databind.ObjectMapper) を独自に使用している部分があれば、こちらも以下の設定でスネークケースに変更可能です。

```java
ObjectMapper mapper = new ObjectMapper().setPropertyNamingStrategy( PropertyNamingStrategy.SNAKE_CASE );
```

勿論、スネークケースの他にもケバブやパスカル（アッパーキャメル）、小文字のドット繋ぎなんかも選択可能です。


**＞ 参考** 

- 参考情報
	- [jackson PropertyNamingStrategy](https://fasterxml.github.io/jackson-databind/javadoc/2.7/com/fasterxml/jackson/databind/PropertyNamingStrategy.html)
- Gistコード
	- [JsonMapper - sugaryo.Gist](https://gist.github.com/sugaryo/0237c1a334f80e8fc6ca75b36393536a)


## ■JSONを PrettyPrint で出力する設定

JSONの整形出力、いわゆる `pretty-print` ですね。

### ＞ SerializationFeature.INDENT_OUTPUT

jacksonのObjectMapper先生に [SerializationFeature.INDENT_OUTPUT](https://fasterxml.github.io/jackson-databind/javadoc/2.8/com/fasterxml/jackson/databind/SerializationFeature.html#INDENT_OUTPUT) を食わせてやればよきです。

```java:import
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
```

```java:ObjectMapperの設定
		/** PrettyPrint設定のObjectMapper */
		private static final ObjectMapper pretty = new ObjectMapper().enable( SerializationFeature.INDENT_OUTPUT );
```

この設定にしてやると、ObjectMapperでwriteValueAsStringしたJSON文字列が整形されて出て来ます。  
多くの場合、整形するかしないかは `api-endpoint-url?pretty` みたいな感じで **オプション的に指定したい** という要望が一般的なので、
PrettyPrint用のObjectMapperと通常出力用のObjectMapperで分けて使うことになるのかなと思います。

ぼくはこんな感じでシングルトンインスタンスを２個用意してます。

```java:ObjectMapperシングルトンホルダ
	public static String stringify( Object obj, boolean pretty ) {
		try {
			return pretty 
					? SingletonHolder.pretty.writeValueAsString( obj )
					: SingletonHolder.mapper.writeValueAsString( obj );
		} catch ( Exception ex ) {
			throw new RuntimeException( ex );
		}
	}
	
	private static final class SingletonHolder {
		// Initialization-on-demand-holder idiom
		
		/** デフォルトのObjectMapper */
		private static final ObjectMapper mapper = new ObjectMapper();
		
		/** PrettyPrint設定のObjectMapper */
		private static final ObjectMapper pretty = new ObjectMapper().enable( SerializationFeature.INDENT_OUTPUT );
	}
```

### ＞ pretty オプションエンドポイントの参考実装

参考実装はこんな感じ。

```java:エンドポイント
	@GetMapping(path="test/json")
	public String test_json_n() {
		
		return test_json( false );
	}
	@GetMapping(path="test/json", params="pretty")
	public String test_json_p() {
		
		return test_json( true );
	}
	private String test_json( boolean pretty ) {
		
		@SuppressWarnings("serial")
		Map<String, Object> map = new HashMap<String, Object>() {
			{
				put( "id", UUID.randomUUID() );
				put( "name", "testdata" );
				put( "value", 123 );
			}
		};
		
		return JsonMapper.stringify( map, pretty );
	}
```

同じエンドポイントパスにGetMappingしつつ `pretty` のパラメータ指定ありなしで用意してみました。  
それぞれ `curl` 叩いてみましょう。

```bash:prettyなし
$ curl -XGET -H "Content-Type: application/json" localhost:8989/t4j-boot/api/test/json
{"name":"testdata","id":"1f2a80e2-95b8-4064-bde8-bc2d22e13df4","value":123}
```

```bash:prettyあり
$ curl -XGET -H "Content-Type: application/json" localhost:8989/t4j-boot/api/test/json?pretty
{
  "name" : "testdata",
  "id" : "9e93d15e-430f-4efd-bfa2-e79cb28aae8e",
  "value" : 123
}
```

良い感じですね。


**＞ 参考** 

列挙体定義を見てみると解ると思いますが整形出力だけでなく色々設定出来るので、開発要件に応じて細かく調整すれば良いでしょう。

- 参考情報
	- [SerializationFeature列挙体](https://fasterxml.github.io/jackson-databind/javadoc/2.8/com/fasterxml/jackson/databind/SerializationFeature.html)
	- [Jackson使い方メモ - Qiita](https://qiita.com/opengl-8080/items/b613b9b3bc5d796c840c)
- Gistコード
	- [JsonMapper - sugaryo.Gist](https://gist.github.com/sugaryo/0237c1a334f80e8fc6ca75b36393536a)






## ■本番運用系の便利エンドポイントを簡単に追加する

SpringBoot大先生には `spring-boot-starter-actuator` という神機能がございまして。  
これは素直に [公式ドキュメント](https://spring.pleiades.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready) を読むのが一番だと思うんですけど、
まぁ簡単に言うと **システム運用で欲しくなりそうな一般的な管理エンドポイントを自動的に作ってくれる** というスグレモノです。  
**なるほど、神だな。**

なお、公式的にはこの機能は **「本番対応機能」** というようですね。

### ＞ actuator の有効化

公式に書いてある通り `spring-boot-starter-actuator` の依存関係を追加します。

```xml:pom
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
```

ほんで、取り敢えず試しに `application.properties` に以下を追記してください。

```ini
management.endpoints.web.base-path=/system
management.endpoints.web.exposure.include=*
management.endpoints.web.exposure.exclude=shutdown
management.endpoint.health.show-details=always
```

`management.endpoints.web.xxx` 系の設定が actuator エンドポイントに関する設定群です。

`base-path` はエンドポイントのベースパスで、
デフォルトでは `/actuator` になっているので変更したい場合はこれで指定します。  
ぼくの場合 `localhost:8989/system` が actuator エンドポイントのURLになります。  
`exposure.include` は有効化したい機能を指定するプロパティ、  
`exposure.exclude` は無効化したい機能を指定するプロパティ、  
まぁ、見てそのまんまなのですぐ解ると思います。  
`health.show-details` は `/health` エンドポイントのレスポンスを詳細に返す設定ですね。

提供されている機能エンドポイントとかは先の公式ドキュメントにも載っているので、それを確認してください。


### ＞ actuator エンドポイント

じゃ、実際動かしてアクセスしてみましょう。  
取り敢えず actuator エンドポイントのベースパスにGETしてみます。

```bash
$ curl -XGET -H "Content-Type: application/json" localhost:8989/system
{"_links":{"self":{"href":"localhost:8989/system","templated":false},"beans":{"href":"localhost:8989/system/beans","templated":false},"caches-cache":{"href":"localhost:8989/system/caches/{cache}","templated":true},"caches":{"href":"localhost:8989/system/caches","templated":false},"health":{"href":"localhost:8989/system/health","templated":false},"health-path":{"href":"localhost:8989/system/health/{*path}","templated":true},"info":{"href":"localhost:8989/system/info","templated":false},"conditions":{"href":"localhost:8989/system/conditions","templated":false},"configprops":{"href":"localhost:8989/system/configprops","templated":false},"env":{"href":"localhost:8989/system/env","templated":false},"env-toMatch":{"href":"localhost:8989/system/env/{toMatch}","templated":true},"loggers":{"href":"localhost:8989/system/loggers","templated":false},"loggers-name":{"href":"localhost:8989/system/loggers/{name}","templated":true},"heapdump":{"href":"localhost:8989/system/heapdump","templated":false},"threaddump":{"href":"localhost:8989/system/threaddump","templated":false},"metrics-requiredMetricName":{"href":"localhost:8989/system/metrics/{requiredMetricName}","templated":true},"metrics":{"href":"localhost:8989/system/metrics","templated":false},"scheduledtasks":{"href":"localhost:8989/system/scheduledtasks","templated":false},"mappings":{"href":"localhost:8989/system/mappings","templated":false}}}
```

使えるエンドポイント一覧がでてきました、便利ですねぇ！！  
管理機能はこうじゃないとなぁ！！

で、有効化されている各機能のエンドポイントを叩くと、色んなものが見れます。

`/health` にアクセスすると、よくあるヘルスチェック結果（DB接続してる場合はDB情報も）が返ってきます。  
`/beans` にアクセスすると、SpringのDIコンテナが管理しているBean情報が見れます。  
`/mappings` にアクセスすると、コントローラで `@RequestMapping系` アノテートしたURLマッピングの詳細情報が見れます。  
`/loggers` にアクセスすると、出力されているログが見れるのでこれはすごく便利ですね。
多くの場合、本番環境のログを見に行くったら結構面倒な手順を踏むと思いますが、これで雑に確認できるのはとても良いですね。

ただ、こうした内部情報を赤裸々に出してしまうので、きちんとセキュリティ保護を施す必要があります。  


**＞ 参考** 

- 参考情報
	- [Spring Boot Actuator 本番対応機能 - 公式ドキュメント](https://spring.pleiades.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready)
	- [Spring Boot 2.0のActuator、とりあえず動かすために知っておきたい変更点3つ - Qiita](https://qiita.com/suke_masa/items/acfdf52019538fd8ccc6)
		- [セキュリティ保護設定](https://qiita.com/suke_masa/items/acfdf52019538fd8ccc6#%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E3%81%A7%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E4%BF%9D%E8%AD%B7%E5%AF%BE%E8%B1%A1%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%8F%E3%81%AA%E3%81%A3%E3%81%9F)
	- [Spring Boot Actuator 2のエンドポイントを叩いてみる - 川上徹.jp](https://xn--fhqq99a3lb.jp/374)



# あとがき「SpringBootってなんだろう？」

（この記事のあとがきはいつ書かれるんですかねぇ・・・）