| この記事の要点 |
|
比較
| エンジン | 拡張子 | Spring Boot | 特徴 |
|---|---|---|---|
| Thymeleaf | .html | 標準 | ブラウザで直接開いても崩れない。情報が多い |
| FreeMarker | .ftlh | 公式対応 | 速い。表現力が高い。Spring 外でも使える |
| Mustache | .mustache | 公式対応 | ロジックを書けない。学習が最も簡単 |
| Groovy Templates | .tpl | 公式対応 | Groovy で書く。使用例は少ない |
| JSP + JSTL | .jsp | 非推奨 | 古い資産向け。jar では動かない |
| Pebble / Jte | — | サードパーティ | 速度重視。Jte は型安全 |
新規なら Thymeleaf、既存の JSP 資産があるなら JSP のまま、というのが実務での分かれ目です。
Thymeleaf
// build.gradle
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
@Controller
public class UserController {
@GetMapping("/users")
public String index(Model model) {
model.addAttribute("users", userService.findAll());
return "users/index"; // templates/users/index.html
}
}
<!-- templates/users/index.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<h1 th:text="${title}">ここは開発中のダミー</h1>
<ul>
<li th:each="u : ${users}" th:text="${u.name}">サンプル</li>
</ul>
<p th:if="${#lists.isEmpty(users)}">該当なし</p>
<a th:href="@{/users/{id}(id=${u.id})}">詳細</a>
<img th:src="@{/img/logo.png}" alt="ロゴ">
</body>
</html>
Thymeleaf の最大の特徴は「そのまま HTML として開ける」ことです。th:text は属性なので、ブラウザで直接開くとタグの中身(ダミー)が表示され、デザイナーがサーバーなしで確認できます。
JSP との書き方の違い
| やりたいこと | JSP + JSTL | Thymeleaf |
|---|---|---|
| 値の表示 | ${user.name} | th:text="${user.name}" |
| 繰り返し | <c:forEach> | th:each |
| 条件 | <c:if> | th:if / th:unless |
| URL | <c:url> | @{/path} |
| 共通部品 | <jsp:include> | th:replace / th:insert |
| エスケープ | <c:out>(${} は素通し) | th:text が既定でエスケープ |
<!-- Thymeleaf: 既定でエスケープされる(安全) -->
<p th:text="${comment}"></p>
<!-- エスケープしない(HTML として解釈させる) -->
<p th:utext="${comment}"></p>
th:utext に利用者の入力を渡すと XSS になります。使ってよいのは、自分で組み立てた安全な HTML か、サニタイズ済みの値だけです。
共通部分をまとめる
<!-- templates/fragments/layout.html -->
<header th:fragment="header">
<nav><a th:href="@{/}">トップ</a></nav>
</header>
<!-- 使う側 -->
<div th:replace="~{fragments/layout :: header}"></div>
th:replace はその要素ごと置き換え、th:insert は中身に差し込みます。詳細は テンプレートフラグメント(ヘッダー等の共有化) を参照してください。
FreeMarker
implementation 'org.springframework.boot:spring-boot-starter-freemarker'
<!-- templates/users/index.ftlh -->
<h1>${title}</h1>
<ul>
<#list users as u>
<li>${u.name}</li>
<#else>
<li>該当なし</li>
</#list>
</ul>
<#-- null なら既定値 -->
${name!"名無し"}
<#if user??>ログイン中</#if>
拡張子を .ftlh にすると自動的に HTML エスケープされます。.ftl はエスケープされないため、必ず .ftlh を使ってください。
Spring Boot で JSP を避ける理由
- 実行可能 jar では動かない。war にして外部の Tomcat に置く必要がある
- 組み込み Jetty / Undertow では動かない
src/main/webapp/WEB-INF/jsp/という Maven / Gradle の標準から外れた場所に置く必要がある${}がエスケープされないため、<c:out>の書き忘れが XSS に直結する
# どうしても JSP を使う場合の設定
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
設定
# 開発中はキャッシュを切る(保存すると即反映される)
spring.thymeleaf.cache=false
# 置き場所と拡張子(既定値)
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.encoding=UTF-8
# テンプレートが見つからないときにエラーにする
spring.thymeleaf.check-template-location=true
本番では cache=true に戻してください。毎回テンプレートを読み直すと大きく遅くなります。
選び方
| 状況 | 選ぶもの |
|---|---|
| Spring Boot で新規に画面を作る | Thymeleaf |
| 大量のページを高速に描画したい | FreeMarker / Jte |
| テンプレートにロジックを書かせたくない | Mustache |
| 既存の JSP が大量にある | JSP のまま(war で運用) |
| 画面は React / Vue で作る | テンプレートエンジンを使わない(@RestController で JSON を返す) |
Thymeleaf でよく使う書き方
| やりたいこと | 書き方 |
|---|---|
| テキストを出す | th:text="${name}" |
| HTML として出す | th:utext="${html}"(XSS 注意) |
| 属性に入れる | th:value th:src th:alt th:placeholder |
| URL を組み立てる | th:href="@{/users/{id}(id=${u.id})}" |
| クラスを付け外し | th:classappend="${active} ? 'on' : ''" |
| 繰り返し | th:each="u, stat : ${users}" |
| 条件 | th:if / th:unless / th:switch |
| 文字列の連結 | th:text="|${a} と ${b}|" |
| 既定値 | ${name} ?: '名無し' |
| タグを出さずに囲む | <th:block th:each="..."> |
<!-- 繰り返しの状態変数 -->
<tr th:each="u, stat : ${users}"
th:class="${stat.odd} ? 'odd' : 'even'">
<td th:text="${stat.count}"></td> <!-- 1 から -->
<td th:text="${stat.index}"></td> <!-- 0 から -->
<td th:text="${u.name}"></td>
</tr>
<!-- 分岐 -->
<div th:switch="${user.role}">
<p th:case="'admin'">管理者</p>
<p th:case="*">一般</p>
</div>
<!-- ユーティリティオブジェクト -->
<span th:text="${#strings.abbreviate(text, 20)}"></span>
<span th:text="${#temporals.format(date, 'yyyy/MM/dd')}"></span>
<span th:text="${#numbers.formatInteger(price, 1, 'COMMA')}"></span>
<span th:if="${#lists.isEmpty(users)}">0 件</span>
JavaScript に値を渡す
<!-- インラインで埋め込む(JSON としてエスケープされる) -->
<script th:inline="javascript">
const user = /*[[${user}]]*/ null;
const name = /*[[${user.name}]]*/ "ダミー";
</script>
<!-- data 属性で渡す(こちらのほうが安全で分かりやすい) -->
<div id="app" th:data-user-id="${user.id}"></div>
<script>
const id = document.getElementById("app").dataset.userId;
</script>
/*[[...]]*/ の後ろに書いたダミー値は、ブラウザで直接開いたときだけ使われます。文字列連結でスクリプトを組み立てないでください。値に </script> や引用符が入ると抜け出されます。
エラー画面のテンプレート
src/main/resources/templates/error.html 全エラー共通
src/main/resources/templates/error/404.html 404 専用
src/main/resources/templates/error/5xx.html 500 番台
この場所にファイルを置くだけで、Spring Boot 既定の Whitelabel Error Page が置き換わります。詳細は 独自のエラーページを定義する方法 を参照してください。
関連
子ページ
同階層のページ
- インストール(eclipseプラグイン)
- クイックスタート
- プロジェクトの作成
- Spring Bootプロジェクトの作成
- Spring Bootプロジェクトの実行
- Spring BootでHello World!
- アノテーション一覧
- DB接続設定からエンティティおよびリポジトリの作成、値の取得まで(JPA編)
- DB接続設定や値の取得(JdbcTemplate編)
- ビューから値をモデルに格納しコントローラーで受け取る方法
- コントローラーにてモデルに値を格納してビューに渡す方法
- テンプレートエンジン
- ModelとModelAndViewの違い
- AOPの使用方法
- classpath: 内部ファイルの読み込み
- file: 外部ファイルの読み込み
- CSVファイルアップロード方法(Ajax)
- CSVファイルダウンロード方法(Ajax)
- Spring Bootプロジェクトのビルドと本番環境へのデプロイ方法(内部tomcat使用)
- Application.propertiesの環境依存設定の分割方法
- JPAにおけるEntityManagerの取得方法
- JPAにおけるjava.sql.Connectionの取得方法
- エラー一覧
- jarの引数を受け取る方法
- Spring BootでGmailからメール送信
- 複数のDBに接続する設定(Spring Boot & JPA編)
- ポート番号の変更
- Basic認証の実装と特定のURLに限定する方法
- Spring SecurityのBasic認証の無効化
- 独自のエラーページを定義する方法
- プロパティファイルの値やjar実行時の引数を取得する方法
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- プロジェクトをTomcatプロジェクトとして認識させる方法 2026-10-07 22:32:50
- MySQLの1366 Incorrect string value|Laravelの文字コード・絵文字エラー 2026-10-07 21:54:03
- curlの証明書ホスト名不一致|旧エラー51・現行60の確認と対処 2026-10-07 21:54:03
- LaravelのMassAssignmentException|fillableの原因と安全な対処 2026-10-07 21:54:03
- Eclipse で Tomcat の起動ログがコンソールに出ない時の確認手順 2026-10-07 21:54:02
- MySQLにおける中央値(Median)の導き方(バージョン8未満) 2026-10-07 13:49:45
- getInputForward 2026-10-07 13:41:15
- JSONから配列に変換 2026-10-07 13:41:15
- ビューから値をモデルに格納しコントローラーで受け取る方法 2026-10-07 13:23:41
- Laravelのテーブル作成と定義変更|マイグレーション・up/down・注意点 2026-10-07 13:23:41
- NumPy 配列に要素を追加する方法 (append / concatenate) 2026-10-07 13:23:41
- MariaDB・MySQLで現在日時を取得する方法|NOW・タイムゾーン・保存型 2026-10-07 13:13:36
- 【django】テンプレートで定数を使用する方法 2026-10-07 13:10:15
- Spring BootにおけるApplication.propertiesの環境依存設定の分割方法 2026-10-07 12:09:35
- Not supported for DML operations【Springエラー】 2026-10-07 11:09:38