1. なぜThymeleaf(テンプレートエンジン)が必要なのか?

近年のSpring BootアプリケーションではThymeleafが用いられることが多いです。

Thymeleafを使うと、HTMLファイルの中に式(EL的なもの)や制御構文を書くことで、Javaのオブジェクトの値を動的に埋め込んだり、条件分岐や繰り返し表示ができます。

なぜThymeleaf?

  • デザイナーとの協業がしやすい:HTMLファイルをそのままブラウザでプレビューしやすい
  • Spring Bootとの相性が良い
  • JSTLやServletスクリプトレットを使わずにテンプレートを作成できる

2. Thymeleafの基本(式や変数展開)

ThymeleafではHTMLタグ属性に対してth:*を使います。

2.1. 変数展開の基本

<p th:text="${msg}">ここにメッセージが入ります</p>
  • ${msg} → コントローラからmodel.addAttribute("msg", "Hello") された文字列などを埋め込み

Controller側(Spring Boot例)

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class Main {
	@GetMapping("/sample")
	public String sample(Model model) {
		model.addAttribute("msg", "Hello World");
		model.addAttribute("age", 20);
		return "sample"; // sample.html
	}
}

sample.html(抜粋)

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"></head>
<body>
  <p th:text="${msg}">ここにメッセージ</p>
</body>
</html>

これで /sample にアクセスすると <p>Hello World</p> が表示されます。

"ここにメッセージ" は、通常 表示されない 文字列です。なぜなら、Thymeleafの th:text 属性は、指定された変数 ${msg} の値でタグの内容を置き換えるからです。つまり、msg に値がある場合は、<p> の中身が msg の値に置き換えられ、元の "ここにメッセージ" は消えてしまいます。


3. Thymeleafでの演算子・条件分岐・繰り返し

3.1. 演算子

Thymeleafの式内では、演算子が使えます。

  • 算術演算子: + - * / %
  • 比較演算子: == != < > <= >=
  • 論理演算子: and, or, ! (あるいは &&, || も使えます)

例)演算子

<p th:text="${1+2}"></p>            <!-- 3 -->
<p th:text="${5/2}"></p>            <!-- 2 -->
<p th:text="${(1 == 1) and (2 == 3)}"></p> <!-- false -->
<p th:text="${(age >= 20) ? '成人' : '未成年'}"></p> <!-- 三項演算子 -->

3.2. 条件分岐

3.2.1. th:if, th:unless

Thymeleafで単純なif/elseを実現するには、th:ifth:unless を使うのが最も簡単です。

<p th:if="${age >= 20}">お酒が飲めます</p>
<p th:unless="${age >= 20}">まだ飲めません</p>

  • th:if="${条件式}" → 条件式がtrueならタグを表示、falseなら非表示
  • th:unless="${条件式}"th:ifの逆で、条件式がfalseなら表示、trueなら非表示

3.2.2. th:switch / th:case

JSPの <c:choose> に近いことをするには、Thymeleafの th:switch / th:case を使えます。

<div th:switch="${bloodType}">
  <p th:case="'A'">慎重な性格</p>
  <p th:case="'B'">大胆な性格</p>
  <p th:case="'O'">おおらかな性格</p>
  <p th:case="*">不思議な性格</p> <!-- default相当 -->
</div>

  • th:switch="${変数}"
  • th:case="'A'"Aに一致する場合のみ表示
  • th:case="*" → マッチしない場合(デフォルト)

3.3. 繰り返し

JSTLの <c:forEach> に相当するのは、th:each 属性です。

<ul>
  <li th:each="name : ${names}" th:text="${name}">名前</li>
</ul>
  • ${names} は、Controller側で List<String> などをmodel.addAttribute("names", namesList) しておく。
  • name : ${names} → 変数nameにリストの要素が順番に入る
  • th:text="${name}" → 文字列を表示

例)オブジェクトのリストをテーブル表示

<table>
  <thead>
    <tr><th>ID</th><th>名前</th><th>価格</th></tr>
  </thead>
  <tbody>
    <tr th:each="car : ${cars}">
      <td th:text="${car.id}"></td>
      <td th:text="${car.name}"></td>
      <td th:text="${car.price}"></td>
    </tr>
  </tbody>
</table>


4. ThymeleafとJavaBeans(プロパティアクセス)

4.1. JavaBeansのgetter/setterを使う

Thymeleafで ${bean.field} と書くと、getField() メソッドを呼び出します。

public class CustomerBean implements Serializable {
    private int customerId;
    private String name;

    // getter/setter ...
}

<p th:text="${customer.customerId}"></p>
<p th:text="${customer.name}"></p>

これで getCustomerId()getName() が呼ばれます。
nullの場合は何も表示されません(th:textは空文字となる)。


5. 式を記述できる場所

ThymeleafではHTMLタグの属性に対して th:text, th:if, th:class, th:valueなど様々な形で式を入れられます。
また、単純に文字列としてタグ本文に${}を書く場合は、[[${}]][(...)]Inline Expressionsを使う方法があります(ただしth:inline="text"などの設定が必要)。

例)CSSに動的値を埋め込みたい場合

<body th:style="'background-color:' + ${color}">
   ...
</body>

例)JavaScriptに文字列を埋め込む

<script th:inline="javascript">
  let userName = [[${name}]];
  alert("こんにちは、" + userName + "さん");
</script>

  • th:inline="javascript" と書くと [[]] 内でThymeleaf式を展開し、JavaScript文字列として安全にエスケープしてくれます。

6. 文字列のフォーマット(数値、日付など)

Thymeleafでは#numbers#temporals (Spring ELのユーティリティオブジェクト)などを使って書式制御ができます。

6.1. 数値の書式

<p th:text="${#numbers.formatDecimal(price, 0, 0)}"></p>

  • #numbers.formatDecimal(元値, 小数最小桁, 小数最大桁)
  • ここでは0,0 → 整数表示し、3桁区切りはLocale設定による
  • #numbers.formatCurrency(value) などもありますが、ロケールや通貨設定が必要です。

例)カンマ付きで円マークを付けたい場合

Thymeleaf単体で円マークを付ける簡易例:

<p th:text="'¥' + #numbers.formatDecimal(price, 0, 0)"></p>

  • 先頭に '¥' + を付加し、#numbers.formatDecimal() でカンマ区切り。
  • Javaでより細かい制御をしたい場合は、DecimalFormatを使ってフォーマット済み文字列を渡す方法もあります。

6.2. 日付の書式

<p th:text="${#temporals.format(date, 'yyyy年MM月dd日(E)')}"></p>

  • #temporals.format(元のDate/LocalDate, "パターン")
  • yyyy (年), MM (月), dd (日), E(曜日) などはJSTLと同様

例)Controllerで渡す

@GetMapping("/dateSample")
public String dateSample(Model model) {
    model.addAttribute("today", LocalDate.now());
    return "dateSample";
}

dateSample.html

<p th:text="${#temporals.format(today, 'yyyy年MM月dd日(E)')}"></p>


まとめ

  • JSP + JSTLのELやタグ機能は、Thymeleafではth:*属性SpELを使って実現
  • 条件分岐: th:if, th:unless, th:switch / th:case
  • 繰り返し: th:each (JSTL <c:forEach> 相当)
  • JavaBeansプロパティ: ${bean.field}getField()呼び出し
  • 数値や日付のフォーマット: #numbers, #temporals などを利用
  • 式の書ける場所: HTML属性やth:inlineしたスクリプト内など様々
  • JSPでの <%@ taglib prefix="c" ...><c:if>, <c:choose>, <fmt:formatNumber> といった構文はThymeleafでは不要。かわりにth:*属性やユーティリティオブジェクト#numbers, #temporalsを使う。

このように、ThymeleafはHTMLテンプレートをベースに動的なデータを埋め込む仕組みが整っています。


参考:JSTLのコードをThymeleafに書き換える対応表イメージ

機能Thymeleaf (HTML)
変数表示 (EL)th:text="${var}" または [[${var}]]
if文<p th:if="${cond}"></p>
unless(逆条件)<p th:unless="${cond}"></p>
choose/switch<div th:switch="${var}"><p th:case="'val'">…</p>
forEach(繰り返し)<tr th:each="x : ${list}">…</tr>
formatNumber(数値)th:text="${#numbers.formatDecimal(val,0,0)}"
formatDate(日付)th:text="${#temporals.format(val, 'yyyy/MM/dd')}"/>

Thymeleafの方がHTML側で可読性高く、一連の処理を属性ベースで記述できるメリットがあります。


今後のステップ

  • Spring Bootでアプリケーションを作成し、ControllerクラスでModelへデータをセット
  • Thymeleafテンプレートにてth:if, th:eachなどを駆使して表示制御
  • JavaBeansクラスを使い、プロパティ(getter/setter)を通じてテンプレートから値を参照
  • 数値や日付、通貨などの表示フォーマットも#numbers, #temporals でコントロール

これにより、JDBCを使って取得したデータベースの内容をBeansに入れ、th:eachで一覧表示する、といった処理まで完成できます。

以上が、「Spring Boot + Thymeleaf」です。