4.1. テンプレートエンジン(Thymeleaf)

4.1.1. Overview

4.1.1.1. Thymeleafとは

Thymeleafは、Javaで実装されたテンプレートエンジンである。 Thymeleafは、その特性により主にHTML生成用のテンプレートエンジンに分類される。

Spring MVCでViewに採用可能なテンプレートエンジンには、他にもApache Velocity、Apache FreeMarker等が存在する。 また以前から利用されている類似の技術としては、Java EE標準で規定されているJSPがある。

本節では、これらの既存のテンプレートエンジンと異なるThymeleafの特性を説明し、ThymeleafをSpring MVCと連携してWebアプリケーションのView(画面)に適用する方法について説明する。

4.1.1.1.1. Thymeleafの特性

  1. Thymeleafのテンプレートは、ブラウザでの静的描画が可能

既存のテンプレートエンジンと比較してThymeleafの特筆すべき点は、HTMLの文法に則ってテンプレートを書ける仕様となっている事である。 この仕様によりThymeleafではテンプレートをHTMLファイルとして作成する事が可能であり、テンプレート自体をWebブラウザで描画する事ができる。(以降、テンプレートファイルをブラウザで直接開く事を静的表示と呼ぶ。)

ここで単純なテンプレートファイルをブラウザで静的表示した場合の例を示す。

  • Thymeleaf以外 のテンプレートファイル(JSP、 Apache FreeMarker等)

    <!-- ommited -->
    
      <h1>Chapter ${number}</h1>  <!-- (1) -->
    
    <!-- ommited -->
    
    項番 説明
    (1)
    <h1>要素にnumber変数を埋め込み、”Chapter “と連結した文字列をHTMLに出力する。実装は要素のコンテンツ部分に記述している。

テンプレートファイルをブラウザで開いた場合には、テンプレートファイルのロジックが表示されてしまう。

thymeleaf other template
  • Thymeleaf のテンプレートファイル

    <!-- ommited -->
    
      <h1 th:text="'Chapter ' + ${number}">Chapter 1</h1>  <!-- (1) -->
    
    <!-- ommited -->
    
    項番 説明
    (1)
    <h1>要素にnumber変数を埋め込み”Chapter “と連結した文字列をHTMLに出力する。実装は属性値に記述している。
    また要素のコンテンツ部分には、固定文字列”Chapter 1”を記述している。

    テンプレートファイルをブラウザで開いた場合にもテンプレートファイルのロジックは表示されず、<h1>要素に記述したコンテンツが表示される。

    thymeleaf template

Thymeleafのテンプレートファイルを確認すると分かるが、Thymeleafでは出力内容を変更するために固有の属性を用いている。 これはブラウザで表示する際に、ブラウザが解釈出来ない属性が無視される事を前提としている。 この仕様により、静的表示の際にThymeleafのテンプレートファイルの実装をブラウザに無視させる事が出来、Thymeleafで動的に変更しないデザインの確認が可能となる。

この特性を活かして、Thymeleafを採用した開発では設計工程で作成したHTMLに対してブラウザでデザインの確認をしつつ、動的表示の為のロジックを実装していく事ができる。(以降、画面設計時に作成するHTMLをThymeleafでの呼称に合わせてプロトタイプと呼び、HTML形式のThymeleafテンプレートファイルをテンプレートHTMLと呼ぶこととする。)

  1. Thymeleafの実行環境
Thymeleafでは、JSPのようにサーブレットコンテナで提供されるテンプレートエンジンを利用して動作するのではなく、アプリケーションに含まれるThymeleafのテンプレートエンジンがテンプレートHTMLの解釈を行う。 このため、アプリケーションサーバごとにテンプレートの解釈が異なり、動作しなくなるといった問題が発生しにくい。 ただし、HttpServletRequestなどのサーブレットAPIを利用しているため、アプリケーションサーバごとの挙動の違いを完全に排除することはできない点に注意されたい。
  1. Thymeleafテンプレート

Thymeleafでは、HTML形式でテンプレートファイルを作成できる。 ThymeleafのテンプレートHTMLに記述できるHTMLの書式は、HTML5に対応しておりHTML5より追加された属性の解釈が可能である。 また、Thymeleafの固有属性をHTML5のカスタムデータ属性として記述する事も可能である。

なお、Thymeleafが提供するテンプレートモードを変える事でJAVASCRIPTやCSS用のテンプレートも作成する事が可能である。

Note

Thymeleafで選択可能なテンプレートモード

本ガイドラインでは、ThymeleafでHTMLを生成する為のテンプレートモードである”HTML”モードについて記述するが、他にも出力するテンプレートに応じたモードが定義されている。 Thymeleafのテンプレートとして選択可能なテンプレートモードについては、 公式リファレンス を参照されたい。

4.1.1.2. Thymeleafが提供する基本的な機能

Thymeleafのテンプレートファイルを記述する為の基本機能である、Thymeleafスタンダードダイアレクトについて説明する。

4.1.1.2.1. Thymeleafスタンダードダイアレクト

Thymeleafは、テンプレートファイルを記述する為に スタンダードダイアレクト を提供している。 スタンダードダイアレクトとは、テンプレートファイルに記述して動的に出力を生成する為の各種プロセッサや、式、式オブジェクトを包含した機能群である。

スタンダードダイアレクトは複数の要素により構成されているため、構成要素とその概要について下表に示す。 なお、各要素の機能詳細についてはThymeleaf公式リファレンスを参照されたい。

構成要素 説明
属性プロセッサ
Thymeleafテンプレート中の要素(タグ)に 属性 として記述するプロセッサ。
Thymeleafテンプレートを記述する為の基本的な文法であり、開発者は属性プロセッサを介してHTML出力処理を実装する。
th:textth:if
要素プロセッサ
Thymeleafテンプレートに 要素(タグ) として記述するプロセッサ。
汎用要素である<th:block>のみが提供されている。<th:block>は、属性プロセッサを記述する為のHTML文法上の土台として用意されている。<th:block>は、Webブラウザで不明なタグとして扱われる為、HTMLの文法に則った属性プロセッサの使用だけでは実現できない場合に限定的に使用されるべきである
<th:block>
属性プロセッサの値に記述する事で、固有の処理を提供する式。
Thymeleafが独自に解釈するトークン及び演算子も提供している。
変数式 ${}、 メッセージ式 #{}、 リンクURL式 @{}
テキストリテラル、数値リテラル、算術演算子、条件式等
式オブジェクト
1. Web コンテキスト ネームスペース : Webオブジェクトにアクセスする為の別名
2. Web コンテキスト ネームスペース : Webオブジェクトにアクセスする為の別名
3. Web オブジェクト : HttpServletRequest、 HttpSession等のServlet API
4. Thymeleafが提供するユーティリティ機能群

1. #ctx, #local
2. param, session, application
3. #request, #session, #servletContext

4. #arrays, #strings

Note

インライン処理について

テンプレートHTMLでは、多くの場合属性プロセッサに式を記述してHTML生成処理を実装する。 その一方で、 インライン処理機能 が用意されており、 属性プロセッサを介さずに要素内のコンテンツを動的に変更する事が可能である。 ただし、静的表示した場合にインライン処理の記述がブラウザに表示される為、ブラウザで静的表示が可能であるThymeleafの利点を損なう事となる。 その為、本ガイドラインでは利用を推奨しない。

Note

th:remove属性について

テンプレートHTMLでは、多くの場合属性プロセッサに式を記述してHTML生成処理を実装する。 その為、属性プロセッサを記述する目的だけのためにデザイン上不要なHTML要素が必要となるケースがある。 またプロトタイプにおいてダミーデータを表示する為の記述についても、Thymeleafによるテンプレート解釈時に削除したいケースがある。 これらのような場合にth:remove属性を用いて、不要なHTML要素やコンテンツを削除する事ができる。 th:remove属性は、属性値に削除する範囲を設定でき動的処理時に柔軟に削除範囲を決める事が可能である。

th:remove属性の詳細については、 Thymeleaf公式リファレンス を参照されたい。

4.1.1.3. Thymeleaf + Spring

Thymeleaf + Spring は、Thymeleafチームが提供するSpring MVCとの連携機能である。 ブランクプロジェクトを利用した場合、Thymeleaf + Springを適用した状態で開発を進める事が出来るようになっている。 設定の詳細は ブランクプロジェクトの設定 を参照されたい。

ここでは、Thymeleaf + Springを適用した場合の処理フローやThymeleaf + Springが提供する機能について説明する。

4.1.1.3.1. Thymeleaf + Springを適用した処理フロー

Thymeleaf + Springを利用し、ThymeleafとSpring MVCを連携させた場合のリクエストを受けてからレスポンスを返すまでの処理フローを以下の図に示す。 なお、 Spring MVCアーキテクチャ概要 にて解説済みのController周りの処理については省略する。

spring thymeleaf
項番 説明
(1)
DispatcherServletが、リクエストを受け取る。
(2)
DispatcherServletは、ビュー名に対応するViewの解決をViewResolverに委譲する。
(3)
DispatcherServletは、返却されたViewにレンダリング処理を委譲する。
(4)
Viewは、TemplateEngineにレンダリング処理を委譲する。
(5)
TemplateEngineは、TemplateManagerにレンダリング処理を委譲し、処理結果のレスポンスをコミットする。
(6)
TemplateManagerは、テンプレートがキャッシュされていない場合はTemplateResolverにテンプレートファイルのロード処理を委譲する。
(7)
TemplateManagerは、パース済みのテンプレートをキャッシュする。
(8)
TemplateManagerは、TemplateHandlerにレンダリング処理を委譲する。
(9)
Viewは、Thymeleafのレンダリング処理結果を返却する。

4.1.1.3.2. Thymeleaf + Springの機能

  1. Springスタンダードダイアレクト

Thymeleaf + Springを利用する場合、Springスタンダードダイアレクト を用いてテンプレートを記述できる。 Springスタンダードダイアレクトは、Thymeleafスタンダードダイアレクトを拡張した機能群であり、 Spring MVCと連携する為の属性プロセッサの拡張と新規追加、及び新たな式オブジェクトを提供している。 追加された機能はHTMLモードに特化しており、Spring MVCのタグライブラリで実現する機能やEL関数を補完する機能を提供している。

追加された属性プロセッサの内、最も特徴的なのはth:field属性でinput要素のtype属性値毎に出力結果を変える。 これにより、Spring Framework JSP Form Tag Libraryが提供する<form:input><form:select><form:checkbox>等の機能をカバーしている。 また<form:errors>の代替機能を実現するth:errors属性、 th:errorClass属性も提供されており、Spring MVCの利点を享受できるように設計されている。

機能の詳細については、Creating a Form 及び Validation and Error Messages を参照されたい。

  1. その他機能
Thymeleaf + Springを適用する場合、Thymeleaf単体で利用する場合とは以下の点で異なる。
  • 式言語として、OGNL(Object Graph Navigation Language)の代わりにSpEL(Spring Expression Language)を利用する。
  • メッセージリソースとして、SpringのMessageSourceを利用する。
  • Thymeleafが提供するフォーマット機能の代わりに、SpringのConversionサービスを利用する。 The Conversion Service を参照されたい。

4.1.1.4. Thymeleafテンプレートの実装

4.1.1.4.1. テンプレートHTMLの実装

これよりThymeleafのテンプレートHTMLの実装例を説明する。 ここでは、設計時に画面のデザインが決定している前提とし、作成済みのHTMLファイルにThymeleaf及びThymeleaf + Springのスタンダードダイアレクトを適用してテンプレートHTMLを作成する。

単純な検索画面と検索結果画面を例とする。以下が対象の画面である。(なお、以下の画面は作成後のテンプレートHTMLを静的表示したものである。)

thymeleaf screen transition
  • 検索画面

HTML

<html>
<head>
  <link rel="stylesheet" href="../../../resources/app/css/styles.css">
  <title>Search Screen</title>
</head>
<body>
  <h1>Search Screen</h1>
  <form id="searchForm" action="searchResult.html"> <!-- (1) -->
    <label>fruits name:</label> <input type="text" name="fruitsName" />
    <button>Search</button>
  </form>
</body>
</html>
項番 説明
(1)
テキスト入力要素を1つ保持する、単純な<form>要素のみを定義している。
action属性には、検索結果画面のHTMLファイルへの静的リンクを記述している。

テンプレートHTML

<html xmlns:th="http://www.thymeleaf.org"> <!--/* (1) */-->
<head>
  <link rel="stylesheet" href="../../../resources/app/css/styles.css" th:href="@{/resources/app/css/styles.css}"> <!--/* (2) */-->
  <title>Search Screen</title>
</head>
<body>
  <h1>Search Screen</h1>
  <form id="searchForm" action="searchResult.html" th:action="@{/searchResult}" th:object="${searchForm}"> <!--/* (3)、(4) */-->
    <label>fruits name:</label> <input type="text" th:field="*{fruitsName}"/> <!--/* (5) */-->
    <button>Search</button>
  </form>
</body>
</html>
項番 説明
(1)
xmlns宣言を追加し、プロセッサに付与する名前空間(”th”)を定義する。
この定義は無くても構わないが、定義が無い場合Eclipse等のIDEによるHTML構文バリデーションで警告される為、記述する事を推奨する。
(2)
<link>要素にth:href属性を追加する。
th:href属性のようにHTMLの属性に”th:”を付加した属性プロセッサは、テンプレートの解釈時に対象のHTML属性値を上書きする。
th:href属性値には、リンクURL式@{}を用いている。リンクURL式は、指定されたパスにWebアプリケーションのコンテキストパスを付加した値を生成する。
(3)
<form>要素にth:action属性を追加する。
th:action属性値には、リンクURL式@{}を用いている。
(4)
<form>要素にth:object属性を追加し、<form>要素内からアクセスするプロパティを格納したオブジェクトを指定する。
th:object属性値には、変数式${}を用いThymeleafのコンテキストに格納したオブジェクトを参照する。
変数式中の記述は、SpELによって処理される。
(5)
<input>要素にth:field属性を追加し、サーバサイドで保持されているデータが有る場合に出力する。
th:field属性値を<input>要素に適用すると、id属性、name属性、value属性が付加される。
th:field属性値には選択変数式*{}を用い、th:object属性で指定したオブジェクトのプロパティを参照している。
これは変数式を利用し、${searchForm.fruitsName}と実装した場合と同様の結果を得る。
選択変数式中の記述も変数式と同様にSpELによって処理される。
  • 検索結果画面

HTML

<html>
<head>
  <link rel="stylesheet" href="../../../resources/app/css/styles.css">
  <title>Search Result Screen</title>
</head>
<body>
  <h1>Search Result</h1>
  <!-- (1) -->
  <table>
    <thead>
      <tr>
        <th>name</th>
        <th>price</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Apple</td>
        <td>300</td>
      </tr>
      <tr>
        <td>Apple Juice</td>
        <td>100</td>
      </tr>
      <tr>
        <td>Apple Pie</td>
        <td>500</td>
      </tr>
    </tbody>
  </table>
  <a href="search.html">Back</a> <!-- (2) -->
</body>
</html>
項番 説明
(1)
name、 priceヘッダを持ったn行×2列のテーブルを定義する。ここでは3行分のデータを記述している。
(2)
<a>要素のhref属性には、検索画面のHTMLファイルへの静的リンクを記述している。

テンプレートHTML

<html xmlns:th="http://www.thymeleaf.org"> <!--/* (1) */-->
<head>
  <link rel="stylesheet" href="../../../resources/app/css/styles.css" th:href="@{/resources/app/css/styles.css}"> <!--/* (2) */-->
  <title>Search Result Screen</title>
</head>
<body>
  <h1>Search Result</h1>
  <table>
    <thead>
      <tr>
        <th>name</th>
        <th>price</th>
      </tr>
    </thead>
    <tbody>
      <tr th:each="item : ${items}"> <!--/* (3) */-->
        <td th:text="${item.name}">Apple</td> <!--/* (4) */-->
        <td th:text="${item.price}">300</td> <!--/* (4) */-->
      </tr>
       <!--/* (5) */-->
      <!--/* -->
      <tr>
        <td>Apple Juice</td>
        <td>100</td>
      </tr>
      <tr>
        <td>Apple Pie</td>
        <td>500</td>
      </tr>
      <!-- */-->
    </tbody>
  </table>
  <a href="search.html" th:href="@{/search}">Back</a> <!--/* (6) */-->
</body>
</html>
項番 説明
(1)
xmlns宣言を追加し、プロセッサに付与する名前空間(”th”)を定義する。
(2)
<link>要素にth:href属性を追加する。
th:href属性値には、リンクURL式@{}を用いている。
(3)
<tr>要素にth:each属性を追加し、テーブルの<tr>要素及び配下の子要素を繰り返し出力している。
th:each属性では、変数式を用いitemsリストを参照し、リスト内のオブジェクトをitem変数に格納している。
(4)
th:text属性値には変数式を用い、th:each属性で定義したitem変数のnameフィールド、priceフィールドを参照している。
th:text属性は、記述した要素のコンテンツを属性値で上書きする。
(5)
Thymeleafのパーサーレベルコメントブロックを用いて、静的表示の為に記述したテーブル内の要素をThymeleafによるテンプレート解釈時に削除するようにしている。
コメントブロックについては、後述する コメント文 の「2. Thymeleafパーサーレベルコメントブロック」を参照されたい。
(6)
<a>要素にth:href属性を追加する。
th:href属性値には、リンクURL式@{}を用いている。

Note

テンプレートHTMLの実装において静的表示を意識すべきかについて

Thymeleafの最も大きな特徴であり魅力であるのが、テンプレートファイルが静的表示可能な事である。 この特徴は、設計時に作成したプロトタイプを元にブラウザでデザインを確認しつつ、サーバ上で動的にHTMLを生成する機能を組み込む事を可能とする。 新規に画面テンプレートを作成する際にサーバ側のプログラムを実装する必要なくデザインが確認できるのは、他のテンプレートエンジンには無い優れた点である。 また、開発途中にデザインの変更が生じた場合もHTMLテンプレートを修正すれば良いため、他テンプレートエンジンでの開発のようなプロトタイプとテンプレートの二重管理が不要となる。 一方で、当機能の副作用についても考慮しておく必要がある。 新規開発時のUI開発の効率化や管理対象資材の削減による利点は先に述べたとおりで否定のしようがないが、エンタープライズ系のシステムでは5~10年(或いはそれ以上)システムを運用する事は当然であり、 その間に機能追加による改修を複数回行う。

このようなシステムに対して、静的表示可能なテンプレートを採用した場合に考慮すべき点を挙げる。

  1. ソースコードの可読性
静的表示が可能なテンプレートHTMLには、商用環境におけるHTML生成後には読み込まれないCSSや遷移する事のない画面へのリンク、削除される要素等の無駄なコンテンツが含まれる。 画面の複雑度が低い場合は気にならないが、複雑度が高くなるほど静的表示用の記述も増える為、テンプレート中に本質的には不要な実装が増えていく。 また静的表示用の記述と動的に解釈させる記述に明確な境界が無い為、実装を読み解かない限りは、いずれの為の記述なのかは判断出来ない。 これらの要因により、静的表示用の実装が無い場合と比較してソースコードの可読性が下がり、メンテナンスコストが高くなる恐れが有る。
  1. メンテナンスに伴うデグレードのリスク
静的表示用の記述の修正によりテンプレートにバグを埋め込む可能性があり、逆にテンプレートの修正により静的表示を損なう可能性もあり、 両者の品質を確保し続けるには高いスキルと冗長なコストが必要とされる。 また、エンタープライズ向けのシステムでは一度リリースしたソースコードを修正するハードルが高く、 静的表示のみの修正のために十分なテストを行ないリリースするコストを避ける為、テンプレートHTMLとプロトタイプの二重管理が発生する危険性も否定できない。 これらの品質・コスト面でのリスクを許容してプロトタイプをメンテナンスし続けるかどうかは、慎重に判断する必要がある。

テンプレートファイルに静的表示用の実装を含める事については、ソースコードの可読性を損ない技術的な負債を作らないか、 プロジェクトで採用する開発プロセスと合致するか、どのように品質を保持していくのか等を勘案のうえ決定するべきである。 また、静的表示可能なテンプレートを採用する場合においては、後述する コメント文 の機能を用い静的表示部と動的にHTMLを生成させるための処理を可能な限り分ける検討をすること。

Todo

Thymeleafには、Decoupled Template Logic という機能が存在する。 Decoupled Template Logicを適用すると、プロトタイプと動的処理を別ファイルに実装できる為、デザインとテンプレートエンジンによる処理を分離可能である。 当機能については、ガイドラインの次版以降で紹介する予定である。

4.1.1.4.2. コメント文

Thymeleafのテンプレートでは、プロトタイプとの両立をサポートするため3種類のコメント文が記述可能である。 ここでは、各々のコメント文の特性及び利用場面を紹介する。

  1. HTMLコメント

通常のHTMLコメント文は、Thymeleafで特別な処理をされず、そのまま生成したHTMLに出力される。 その為、利用者に見られても問題が無い内容以外をHTMLコメントで記述するべきではない。

  • 記述例
<!-- This is Search Form --> <!--/* (1) */-->
<form id="searchForm" action="searchResult.html" th:action="@{/searchResult}">
項番 説明
(1)
通常のHTMLコメント文は、生成したHTMLに出力される。
  • 出力例
<!-- This is Search Form -->
<form id="searchForm" action="/search/searchResult">
  1. Thymeleafパーサーレベルコメントブロック

Thymeleafパーサーレベルコメントブロックは、Thymeleafでの処理時に削除され生成したHTMLには出力されない。 これは、コメントブロック自体もコメントブロックで囲ったコンテンツに対しても同様である。 その為、静的表示用のみに使用する。

  • 記述例
<tbody>
  <tr th:each="item : ${items}">
    <td th:text="${item.name}">Apple</td>
    <td th:text="${item.price}">300</td>
  </tr>
  <!--/* (1) */-->
  <!--/* -->
  <tr>
    <td>Banana</td>
    <td>300</td>
  </tr>
  <tr>
    <td>Strawberry</td>
    <td>300</td>
  </tr>
  <!-- */-->
</tbody>
項番 説明
(1)
Thymeleafパーサーレベルコメントブロックは、Thymeleafでの処理時に削除され生成したHTMLには出力されない。
本例の場合は、コメントブロックで囲われたテーブルの2~3行目は、静的表示時のみ有効でThymeleafが生成したHTMLからは削除されている。
また<!--/* (1) */-->も同様に削除される。
  • 出力例
<tbody>
  <tr>
    <td>Grape</td>
    <td>300</td>
  </tr>
  <tr>
    <td>Grape Juice</td>
    <td>100</td>
  </tr>
  <tr>
    <td>Grape Soda</td>
    <td>100</td>
  </tr>
</tbody>
  1. Thymeleafプロトタイプのみのコメントブロック

Thymeleafプロトタイプのみのコメントブロックは、コメントブロック内部の記述がThymeleafで処理される。 一方で静的表示する場合には、HTMLコメントと判断される為ブラウザには表示されない。 静的表示した場合に解釈不可能な<th:block>タグを用いた場合や、th:if属性を用いた分岐制御の為だけに、デザイン上不要なタグを使用する場合に有用なコメントブロックである。

  • 記述例
<tbody>
  <!--/* (1) */-->
  <!--/*/ <th:block th:each="item : ${items}"> /*/-->
  <tr>
    <td th:text="${item.name}">Apple</td>
    <td th:text="${item.price}">300</td>
  </tr>
  <tr>
    <td colspan="2" th:text="${item.amount}">10</td>
  </tr>
  <!--/*/ </th:block> /*/-->

  <!--/* omitted */-->

</tbody>
項番 説明
(1)
プロトタイプのみのコメントブロックを使用して<th:block>を用いた繰り返し処理を静的表示の際には、描画されないようにしている。
Thymeleafによる処理時には、コメントブロックが削除され<th:block>に記述したth:each属性が処理される。
  • 出力例
<tbody>
  <tr>
    <td>Orange</td>
    <td>300</td>
  </tr>
  <tr>
    <td colspan="2">5</td>
  </tr>
  <tr>
    <td>Orange Juice</td>
    <td>100</td>
  </tr>
  <tr>
    <td colspan="2">15</td>
  </tr>
  <tr>
    <td>Orange Sherbet</td>
    <td>200</td>
  </tr>
  <tr>
    <td colspan="2">20</td>
  </tr>
</tbody>

4.1.2. How to use

4.1.2.1. アプリケーションの設定

本節では、ThymeleafをSpring MVCと連携して使用する為の設定の説明をする。

4.1.2.1.1. ブランクプロジェクトの設定

Thymeleafを利用する為の初期設定をブランクプロジェクトで提供している。 ここでは、Spring MVCと組み合わせてThymeleafを使用する為のブランクプロジェクトの設定の説明をする。 Thymeleafに係るブランクプロジェクトの設定は、以下の4点である。

  1. Thymeleaf及び推奨ライブラリの依存関係の設定
  2. ThymeleafをSpring MVCのViewとして用いる為のBean定義
  3. テンプレートHTMLのレイアウト化 (テンプレートHTMLのレイアウト機能については、Thymeleafにおける画面レイアウトを参照されたい。)
  4. エラー画面のテンプレートHTMLをThymeleafで処理する為の設定及びControllerの実装
テンプレートHTMLに直接遷移した場合、Thymeleafによるテンプレートの解釈がされない。
その為、ブランクプロジェクトではエラー画面遷移用パスを定義し、専用のControllerで受け付けるようにしている。
  • pom.xmlの定義
  • プロジェクトのルートのpom.xml
<dependencyManagement>
  <dependencies>

    <!-- == Begin Thymeleaf == -->
    <!-- (1) -->
    <dependency>
      <groupId>org.thymeleaf</groupId>
      <artifactId>thymeleaf</artifactId>
      <version>${thymeleaf.version}</version>
    </dependency>
    <!-- (2) -->
    <dependency>
      <groupId>org.thymeleaf</groupId>
      <artifactId>thymeleaf-spring4</artifactId>
      <version>${thymeleaf.version}</version>
    </dependency>
    <!-- (3) -->
    <dependency>
      <groupId>org.thymeleaf.extras</groupId>
      <artifactId>thymeleaf-extras-springsecurity4</artifactId>
      <version>${thymeleaf-extras-springsecurity4.version}</version>
    </dependency>
    <!-- == End Thymeleaf == -->

  </dependencies>
</dependencyManagement>


<properties>

  <!-- (4) -->
  <thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
  <thymeleaf-extras-springsecurity4.version>3.0.2.RELEASE</thymeleaf-extras-springsecurity4.version>

</properties>
項番 説明
(1)
thymeleafのdependencyを定義する。
(2)
thymeleaf-spring4のdependencyを定義する。
(3)
thymeleaf-extras-springsecurity4のdependencyを定義する。
(4)
Thymeleaf及び推奨するライブラリのバージョンを定義する。
本ガイドラインで採用するThymeleafのバージョンは、採用するSpring IO platform(Brussels-SR5)で管理されているバージョンと異なる為、明示的に上書き指定している。

Note

Thymeleaf 3.xを採用する理由

Spring IO platform(Brussels-SR5)で管理されているThymeleafのバージョンは、2.1.4.RELEASLEであるが、本ガイドラインは、3.0.9.RELEALSEを前提に記述している。 敢えてSpring IO platformと異なるバージョンを採用する理由は、Thymeleaf 2.xから3.xへのバージョンアップの際に大幅に性能向上が図られたためである。 3.xへのバージョンアップ情報の詳細については、Thymeleaf 3.0 announcement and more info を参照されたい。

  • [artifactID]-webプロジェクトのpom.xml
<dependencies>

  <!-- == Begin Thymeleaf == -->
  <!-- (1) -->
  <dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
  </dependency>
  <!-- (2) -->
  <dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring4</artifactId>
  </dependency>
  <!-- (3) -->
  <dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
  </dependency>
  <!-- == End Thymeleaf == -->

</dependencies>
項番 説明
(1)
thymeleafのdependencyを追加することで、Thymeleafが利用可能となる。
(2)
thymeleaf-spring4のdependencyを追加することで、Spring MVCとの連携機能が有効になる。
(3)
thymeleaf-extras-springsecurity4のdependencyを追加することで、Spring Securityとの連携機能が有効になる。
  • spring-mvc.xmlの定義

    <!-- (1) -->
    <mvc:view-resolvers>
        <mvc:bean-name />
        <bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
            <property name="templateEngine" ref="templateEngine" />
            <property name="characterEncoding" value="UTF-8" /> <!-- (2) -->
            <property name="forceContentType" value="true" /> <!-- (3) -->
            <property name="contentType" value="text/html;charset=UTF-8" /> <!-- (3) -->
        </bean>
    </mvc:view-resolvers>
    
    <!-- (4) -->
    <bean id="templateResolver"
        class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
        <property name="prefix" value="/WEB-INF/views/" /> <!-- (5) -->
        <property name="suffix" value=".html" /> <!-- (6) -->
        <property name="templateMode" value="HTML" /> <!-- (7) -->
        <property name="characterEncoding" value="UTF-8" /> <!-- (8) -->
    </bean>
    
    <!-- (9) -->
    <bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
        <property name="templateResolver" ref="templateResolver" />
        <property name="enableSpringELCompiler" value="true" /> <!-- (10) -->
        <property name="additionalDialects">
            <set>
                <bean
                    class="org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect" />  <!-- (11) -->
            </set>
        </property>
    </bean>
    
    項番 説明
    (1)
    ThymeleafViewResolverをBean定義する。
    Spring MVCのViewにThymeleafを採用する場合には、ThymeleafViewResolverを用いる。
    <mvc:view-resolvers>内に記述し、BeanNameViewResolverの次に処理をする設定としている。
    (2)
    レスポンスのエンコーディングを設定する。UTF-8を設定している。
    (3)
    forcedContentTypeプロパティにtrueを指定し、レスポンスのContent-Typeヘッダを明示的に設定するようにしている。
    contentTypeプロパティにtext/html;charset=UTF-8を指定している。
    (4)
    SpringResourceTemplateResolverをBean定義する。
    SpringResourceTemplateResolverは、SpringのResourceLoader経由で、Thymeleafのテンプレートファイルを検出する。
    (5)
    Thymeleafテンプレートが格納されているベースディレクトリ(ファイルパスのプレフィックス)を指定する。
    (6)
    Thymeleafテンプレートの拡張子(ファイルパスのサフィックス)を設定する。HTMLファイルをテンプレートとする為、.htmlを設定している。
    (7)
    解釈するテンプレートモードを設定する。デフォルト値は”HTML”モードであるが、明示的に設定している。
    (8)
    テンプレートファイルのエンコーディングを設定する。UTF-8を設定している
    (9)
    SpringTemplateEngineをBean定義する。
    SpringTemplateEngineにより、Thymeleaf + Springが提供する各種機能を利用可能となる。
    (10)
    SpEL(Spring Expression Language)のコンパイル実施可否を設定する。
    SpELのコンパイルを実施する事で性能向上が見込める為、trueを設定している。
    (11)
    additionalDialectsに、SpringSecurityDialectを定義することで、テンプレートHTML内で、Spring Securityの認証・認可制御が可能となる。

    Note

    レスポンスのContent-Typeの解決方法について

    ThymeleafViewResolverのデフォルトの動作では、リクエストのAcceptヘッダの値やURLを元にレスポンスのContent-Typeヘッダの値を決めている。 例えば、URLの末尾に.jsonのような拡張子を指定したリクエストの場合、レスポンスのContent-Typeにapplication/jsonが設定される。 レスポンスでHTMLのみを返却する場合は、Content-Typeが自動判定されることで思わぬ不具合が生じる可能性がある。 本ガイドラインでは、Thymeleafを介した場合のレスポンスがHTMLのみである想定の為、ブランクプロジェクトにてContent-Typeを”text/html;charset=UTF-8”に明示的に指定している。 Content-Typeの指定は、ThymeleafViewResolverのBean定義でforcedContentTypeプロパティをtrueとし、contentTypeプロパティに任意のContent-Typeを設定する事で可能である。

  • spring-security.xmlの定義

    <bean id="accessDeniedHandler"
        class="org.springframework.security.web.access.DelegatingAccessDeniedHandler">
        <constructor-arg index="0">
            <map>
                <entry
                    key="org.springframework.security.web.csrf.InvalidCsrfTokenException">
                    <bean
                        class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
                        <property name="errorPage"
                            value="/common/error/invalidCsrfTokenError" /> <!-- (1) -->
                    </bean>
                </entry>
                <entry
                    key="org.springframework.security.web.csrf.MissingCsrfTokenException">
                    <bean
                        class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
                        <property name="errorPage"
                            value="/common/error/missingCsrfTokenError" /> <!-- (1) -->
                    </bean>
                </entry>
            </map>
        </constructor-arg>
        <constructor-arg index="1">
            <bean
                class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
                <property name="errorPage"
                    value="/common/error/accessDeniedError" /> <!-- (1) -->
            </bean>
        </constructor-arg>
    </bean>
    
    項番 説明
    (1)
    spring-security.xmlのAccessDeniedHandlererrorPageのパスを指定する。
    エラー画面をThymeleafに処理させるため、直接HTMLファイルのパスを指定せず、後述するエラー画面に遷移させるためのControllerでハンドリングされるようにしている。
  • web.xmlの定義

    <error-page>
        <error-code>500</error-code>
        <location>/common/error/systemError</location>  <!-- (1) -->
    </error-page>
    
    <error-page>
        <error-code>404</error-code>
        <location>/common/error/resourceNotFoundError</location>  <!-- (1) -->
    </error-page>
    
    <error-page>
        <exception-type>java.lang.Exception</exception-type>
        <location>/WEB-INF/views/common/error/unhandledSystemError.html</location>  <!-- (2) -->
    </error-page>
    
    項番 説明
    (1)
    遷移するパスを指定する。エラー画面をThymeleafに処理させるため、直接HTMLファイルのパスを指定せず、後述するエラー画面に遷移させるためのControllerでハンドリングされるようにしている。
    (2)
    unhandledSystemError.htmlは、Thymeleafのテンプレートではない為、直接HTMLファイルのパスを指定している。
  • エラーページ遷移用Controllerクラス

    @Controller
    @RequestMapping("common/error") // (1)
    public class CommonErrorController {
    
        @RequestMapping("accessDeniedError") // (1)
        public String accessDeniedError() {
            return "common/error/accessDeniedError"; // (2)
        }
    
        @RequestMapping("businessError")
        public String businessError() {
            return "common/error/businessError";
        }
    
        @RequestMapping("dataAccessError")
        public String dataAccessError() {
            return "common/error/dataAccessError";
        }
    
        @RequestMapping("/invalidCsrfTokenError")
        public String invalidCsrfTokenError() {
            return "common/error/invalidCsrfTokenError";
        }
    
        // omitted
    
    }
    
    項番 説明
    (1)
    クラスレベルの@RequestMappingアノテーションにエラー画面の共通パスを指定し、メソッドレベルの@RequestMappingアノテーションに各種例外に応じたエラー画面遷移用パスを指定する。
    (2)
    ハンドラメソッドからは、Thymeleafのテンプレートを指定する文字列を返却する。

4.1.2.2. Viewの実装

ThymeleafのテンプレートHTMLの実装については、アプリケーション層の実装Viewの実装を参照されたい。

4.1.3. How to extend

4.1.3.1. カスタムダイアレクトの追加

Thymeleafでは開発者がカスタムダイアレクトを追加することで、独自に開発したタグや属性、式オブジェクトを使用することができる。

カスタムダイアレクトを追加するにはProcessorやExpressionObjectとDialectを実装する必要がある。

  説明
Processor
テンプレート内のイベントに対して実行する処理を定義するオブジェクト。
タグを定義する要素プロセッサとタグの属性を定義する属性プロセッサなどの種類がある。
ExpressionObject
テンプレート内の式から呼び出されるオブジェクト。
テンプレート内で用いるためのメソッドなどを定義する。特に制約がなく、POJOで定義できる。
Dialect
ProcessorやExpressionObjectをまとめたライブラリ。
テンプレートエンジンにDialectを登録することで、ProcessorやExpressionObjectで定義された文法をテンプレート内で用いることができるようになる。

4.1.3.1.1. Processorの実装

Processorはテンプレート内のイベントに対して実行する処理を定義するオブジェクトである。

Processorを実装するためには、Thymeleafから提供されているインタフェースを実装すればよい。

Thymeleafから提供されている代表的なProcessorのインタフェースを以下に示す。

processor 説明
org.thymeleaf.processor.element.IElementTagProcessor
開始タグに対して実行される処理を定義するためのインタフェース。対象のタグの内容は参照可能だが、直接変更することはできない。structureHandlerを介してのみ対象のタグの属性やボディを変更することができる。
通常は、IElementTagProcessorを直接実装するのではなく、org.thymeleaf.processor.AbstractAttributeTagProcessorなどのIElementTagProcessorを実装した抽象クラスを継承する。
org.thymeleaf.processor.element.IElementModelProcessor
開始タグから閉じタグまでの要素全体に対して実行される処理を定義するためのインタフェース。対象の要素全体をモデルとして処理するため、任意の要素を参照、直接変更することができる。また、閉じタグの後など、任意の箇所に要素を追加することもできる。
通常は、IElementModelProcessorを直接実装するのではなく、org.thymeleaf.processor.AbstractAttributeModelProcessorなどのIElementModelProcessorを実装した抽象クラスを継承する。

Note

上記のインタフェース以外にもイベントごとに対応するインタフェースが提供されている。詳しくはTutorial: Extending Thymeleaf(Processors)を参照されたい。

Processorでの処理に用いる代表的なインタフェースを以下に示す。

インタフェース 説明
org.thymeleaf.model.IModel
HTMLタグなどを抽象化したインタフェース。開始タグ、ボディ、終了タグなどのHTMLを構成する要素をリストのように保持する。
org.thymeleaf.model.IModelFactory
IModelの生成や組み立てをするインタフェース。
org.thymeleaf.context.ITemplateContext
コンテキストの情報を保持するインタフェース。IModelFactoryなどを取得することができる。
org.thymeleaf.model.IProcessableElementTag
属性を適用したタグ自体の情報を保持するインタフェース。タグの名前や付与された属性を取得することができる。
org.thymeleaf.processor.element.IElementTagStructureHandler
属性を適用したタグや、そのボディ部を編集するためのインタフェース。

ラベル、入力フィールド、エラーメッセージをまとめて出力する独自属性の実装例を以下に示す。

Note

独自タグと独自属性どちらでも同じ機能を実装できる場合があるが、独自属性での実装を推奨する。

理由は、静的表示する際、独自タグは<th:block>と同様に解釈不能となってしまうが、独自属性はその属性のみが無視され、正しく表示できるためである。

テンプレート記述例

<form th:object="${userForm}">
    <div input:form-input="*{userName}"></div>
</form>

独自属性の処理結果

<form th:object="${userForm}">
    <div class="form-input">
        <label for="userName">userName</label>
        <input th:field="*{userName}" />
        <span th:errors="*{userName}"></span>
    </div>
</form>

Note

上記の処理結果は実装する独自属性のみをテンプレートエンジンで評価した結果である。 実際に出力されるHTMLはth:field属性などもテンプレートエンジンで評価した形となるため上記の処理結果とは異なる。 実際のHTML出力については カスタムダイアレクトの使用方法 を参照されたい。

実装例

// (1)
public class FormInputAttributeTagProcessor extends AbstractAttributeTagProcessor {

    public FormInputAttributeTagProcessor(final String dialectPrefix) {
        super(TemplateMode.HTML, // (2)
                dialectPrefix, // (3)
                null, false, // (4)
                "form-input", true, // (5)
                1000, // (6)
                true // (7)
        );
    }

    @Override
    protected void doProcess(ITemplateContext context,
            IProcessableElementTag tag, AttributeName attributeName,
            String attributeValue, //(8)
            IElementTagStructureHandler structureHandler) {

        // (9)
        String classValue = tag.getAttributeValue("class");

        // (10)
        if (StringUtils.isEmpty(classValue)) {
            structureHandler.setAttribute("class", "form-input");
        } else {
            structureHandler.setAttribute("class", classValue + " form-input");
        }

        // (11)
        IModelFactory modelFactory = context.getModelFactory();
        IModel model = modelFactory.createModel();

        // (12)
        model.add(modelFactory.createOpenElementTag("label", "for", "userName"));
        model.add(modelFactory.createText(createLabel(attributeValue)));
        model.add(modelFactory.createCloseElementTag("label"));

        model.add(modelFactory.createStandaloneElementTag("input", "th:field",
                attributeValue));

        model.add(modelFactory.createOpenElementTag("span", "th:errors",
                attributeValue));
        model.add(modelFactory.createCloseElementTag("span"));

        // (13)
        structureHandler.setBody(model, true);

    }

    private String createLabel(String attributeValue){

        // omitted

    }

}
項番 説明
(1)
AbstractAttributeTagProcessorIElementTagProcessorを実装した抽象クラス)を継承する。
(2)
HTMLテンプレートに適用する場合は、TemplateMode.HTMLを指定する。
(3)
属性の名前に適用するプレフィックスを指定する。通常は、Dialectから引数で受け取った値を指定する。
(4)
独自タグを作成する場合、タグ名を設定する。この例では独自属性を作成するのでnullを設定している。booleanはタグ名にプレフィックスを適用するかを指定する。
(5)
独自属性を作成する場合、属性名を設定する。booleanは属性名にプレフィックスを適用するかを指定する。
(6)
Dialect内におけるProcessorの優先順位を指定する。値が低いほど優先度が高くなる。
(7)
Processor適用後に適用対象の属性の記述を削除するか指定する。基本的に適用対象の属性は出力するHTMLには不要となるのでtrueを指定する。
(8)
適用対象の属性が持つ値が渡される。渡される値は式の処理をしていない状態で、上記のテンプレート記述例の場合は*{userName}が渡される。
(9)
適用対象の属性を持つタグからclass属性の値を取得する。class属性が存在しない場合はnullになる。
(10)
適用対象の属性を持つタグのclass属性の値にform-inputを追加する。
(11)
IModelFactoryを取得し、IModelを生成する。
(12)
IModelにラベル、入力フィールド、エラーメッセージを出力させるための要素を追加する。
(13)
渡したIModel適用対象の属性を持つタグのボディを置き換える。booleanは置き換えたボディをテンプレートエンジンで再評価するかを指定する。
上記の例ではth:field属性とth:errors属性を再評価する必要があるためtrueを指定している。

Note

AbstractAttributeTagProcessorを継承した抽象クラスがいくつか提供されており、より簡単にProcessorを実装することができる場合がある。詳しくはAbstractAttributeTagProcessorを参照されたい。

4.1.3.1.2. ExpressionObjectの実装

ExpressionObjectはテンプレート内の式から呼び出すメソッドなどを定義するオブジェクトである。

ExpressionObjectはインタフェース等を実装する必要がなく、POJOで定義できる。

日付(java.util.Date)をyyyy/MM/dd形式でフォーマットして出力するメソッドを持つ式オブジェクトの実装例を以下に示す。

Note

日付を引数で渡した形式でフォーマットして出力する機能はthymeleafから提供されている。

実装例

// (1)
public class CustomDateFormat {

    // (2)
    public String formatYYYYMMDD(Date date) {
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
        return dateFormat.format(date);
    }

}
項番 説明
(1)
POJOとして作成する。
(2)
引数に指定された日付をyyyy/MM/dd形式でフォーマットした文字列を返す。

4.1.3.1.3. Dialectの実装

ProcessorやExpressionObjectで実装した処理をテンプレートに適用するためにはDialectを実装してテンプレートエンジンに追加する必要がある。

Dialectを実装するためにThymeleafから提供されている代表的なインタフェースを以下に示す。

インタフェース名 説明
org.thymeleaf.dialect.IProcessorDialect
Processorを登録するDialectを実装するためのインタフェース
通常は、IProcessorDialectを直接実装するのではなく、IProcessorDialectを実装した抽象クラスorg.thymeleaf.dialect.AbstractProcessorDialectを継承する。
org.thymeleaf.dialect.IExpressionObjectDialect
ExpressionObjectを登録するDialectを実装するためのインタフェース

Note

上記のインタフェース以外にも登録内容ごとに対応するインタフェースが提供されている。詳しくはTutorial: Extending Thymeleaf(Dialects)を参照されたい。

ProcessorとExpressionObjectを登録するDialectの実装例を以下に示す。

実装例(Processorの登録)

// (1)
public class InputFormDialect extends AbstractProcessorDialect {

    // (2)
    public InputFormDialect() {
        super("Input Form Dialect", "input", 1000);
    }

    @Override
    public Set<IProcessor> getProcessors(String dialectPrefix) {

        final Set<IProcessor> processors = new HashSet<IProcessor>();

        // (3)
        processors.add(new FormInputAttributeTagProcessor(dialectPrefix));

        // (4)
        processors.add(
                new StandardXmlNsTagProcessor(TemplateMode.HTML, dialectPrefix));

        return processors;

    }

}
項番 説明
(1)
Processorを登録する場合は、AbstractProcessorDialectIProcessorDialectを実装した抽象クラス)を継承する。
(2)
引数はDialect名、登録するProcessorのプレフィックス、Dialectの優先順位である。
Processorの適用順序はDialectの優先順位、Processorの優先順位の順番で比較して決められる。
(3)
実装したProcessorを登録する。
(4)
HTMLの最初につけるxmlns:th="http://www.thymeleaf.org"のようなネームスペース表記を削除するためにorg.thymeleaf.standard.processor.StandardXmlNsTagProcessorを登録する。

実装例(ExpressionObjectの登録)

// (1)
public class CustomFormatDialect implements IExpressionObjectDialect {

    private Set<String> names = new HashSet<String>() {
        {
            add("customdateformat");
        }
    };

    @Override
    public IExpressionObjectFactory getExpressionObjectFactory() {
        return new IExpressionObjectFactory() {

            // (2)
            @Override
            public Set<String> getAllExpressionObjectNames() {
                return names;
            }

            // (3)
            @Override
            public Object buildObject(IExpressionContext context,
                    String expressionObjectName) {
                if ("customdateformat".equals(expressionObjectName)) {
                    return new CustomDateFormat();
                }
                return null;
            }

            // (4)
            @Override
            public boolean isCacheable(String expressionObjectName) {
                return true;
            }

        };
    }

    @Override
    public String getName() {
        return "Date Format(yyyy/MM/dd) Dialect";
    }

}
項番 説明
(1)
ExpressionObjectを登録する場合は、IExpressionObjectDialectを実装する。
(2)
ExpressionObjectの名前を登録する。
(3)
実装したExpressionObjectを登録する。引数のexpressionObjectNameに入る値が(2)で登録した名前に存在する場合、このメソッドが呼ばれる。
(4)
ExpressionObjectをキャッシュするか指定する。ExpressionObjectが状態によって異なる値を返す場合はfalse、状態にかかわらず返す値が一定である場合はtrueを指定する。

Note

上記の例ではProcessorとExpressionObjectを別のDialectで登録する例を示しているが、意味的にまとめられる機能であれば一つのDialectで登録することも可能である。

4.1.3.1.4. カスタムダイアレクトの使用方法

作成したカスタムダイアレクトを使用するために必要なアプリケーション設定と出力画面の実装を以下に示す。

spring-mvc.xml

<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">

    <!-- omitted -->

    <!-- (1) -->
    <property name="additionalDialects">
        <set>
            <bean class="org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect" />
            <bean class="com.example.sample.dialect.InputFormDialect" />
            <bean class="com.example.sample.dialect.CustomFormatDialect" />
        </set>
    </property>
</bean>
項番 説明
(1)
テンプレートエンジンに作成したカスタムダイアレクトをjava.util.Set<IDialect>で追加する。

view.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:input="http://inputform.sample.example.com"> <!-- (1) -->
<head>

    <!-- omitted -->

</head>
<body>

    <!-- omitted -->

    <!-- (2) -->
    <form th:object="${userForm}">
        <div input:form-input="*{userName}"></div>
    </form>

    <!-- omitted -->

    <span th:text="${#customdateformat.formatYYYYMMDD(date)}">yyyy/MM/dd</span> <!-- (3) -->

    <!-- omitted -->

</body>
</html>
項番 説明
(1)
作成したDialectの名前空間を定義する。
(2)
作成したinput:form-input属性を指定する。
(3)
作成した式オブジェクトcustomdateformatを呼び出す。

出力結果

<!DOCTYPE html>
<html>
<head>

    <!-- omitted -->

</head>
<body>

    <!-- omitted -->

    <form>
        <!-- (1) -->
        <div class="form-input">
            <label for="userName">userName</label>
            <input id="userName" name="userName" value=""/>
        </div>
    </form>


    <!-- omitted -->

    <span>2017/10/30</span>

    <!-- omitted -->

</body>
</html>
項番 説明
(1)
見やすくするために改行とインデントを入れてあるが、実際には開始タグから閉じタグまで1行で出力される。

4.1.4. Appendix

4.1.4.1. テンプレートキャッシュの適用

テンプレートキャッシュの機能および設定方法について説明する。

4.1.4.1.1. テンプレートキャッシュ機能の説明

テンプレートキャッシュとは、パースしたテンプレート(HTML)をキャッシュする機能である。 テンプレートが呼び出される度にパースを行わないため、処理時間を削減することができる。

Controllerから渡されたView名をキーとしてキャッシュの判定が行われる。キャッシュにヒットした場合は、キャッシュからパースされたテンプレートが読み込まれる。 キャッシュにヒットしない場合は、テンプレートをパースしてキャッシュに追加する。

Note

org.thymeleaf.spring4.view.ThymeleafViewResolverにはViewオブジェクトをキャッシュする機能が備わっているが、本機能と直接関係はないので、ここでの説明は省略する。

4.1.4.1.2. アプリケーションの設定

キャッシュの有効化、対象および期間の設定は、org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolverで実施する。 主要な設定項目の一覧を以下に示す。

項番 設定項目 内容 デフォルト値
(1)
cacheable
全テンプレートに対するキャッシュの有効化をtruefalseで指定する。
true
(2)
cacheTTLMs
キャッシュの生存時間をミリ秒単位で指定する。
null(時間経過でのキャッシュ削除を行わない)
(3)
cacheablePatterns
キャッシュ対象のテンプレートをView名で指定する。cacheablefalseを指定した場合に用いる。”*”などのワイルドカードを使用することができる。
-
(4)
nonCacheablePatterns
キャッシュ対象から除外するテンプレートをView名で指定する。cacheabletrueを指定した場合に用いる。”*”などのワイルドカードを使用することができる。
-

Note

頻繁にアクセスするテンプレートをキャッシュ対象とし、アクセス頻度が低いテンプレートはキャッシュ対象から除外することで、メモリ負荷を抑えて効率的にキャッシュ機能を働かせることを推奨する。

以下に特定のテンプレートのみキャッシュ対象から除外する場合の設定例を示す。

  • spring-mvc.xml
<bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
    <!-- ... -->
    <property name="nonCacheablePatterns">
        <set>
            <value>welcome/home</value>
            <value>sample/*</value>
        </set>
    </property>
    <property name="cacheTTLMs" value="300000" />
</bean>

さらにキャッシュサイズ等の詳細な設定については、org.thymeleaf.cache.StandardCacheManagerで実施する。 主要な設定項目の一覧を以下に示す。

項番 設定項目 内容 デフォルト値
(1)
templateCacheInitialSize
キャッシュの初期サイズをテンプレート数単位で指定する。
20
(2)
templateCacheMaxSize
キャッシュの最大サイズをテンプレート数単位で指定する。-1を指定した場合は制限なしになる。”0”を指定した場合はキャッシュが無効になる。
200
(3)
templateCacheLoggerName
ログを出力するロガー名を指定する。
org.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE
(4)
templateCacheName
ログに出力するキャッシュ名を指定する。
TEMPLATE_CACHE

以下にキャッシュの初期サイズ、最大サイズをデフォルト値から変更する場合の設定例を示す。

  • spring-mvc.xml
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
    <!-- ... -->
    <property name="cacheManager" ref="cacheManager" />
</bean>

<bean id="cacheManager" class="org.thymeleaf.cache.StandardCacheManager">
    <property name="templateCacheInitialSize" value="90" />
    <property name="templateCacheMaxSize" value="100" />
</bean>

StandardCacheManagerを利用する場合、キャッシュの初期サイズに指定した値を初期容量としてキャッシュの初期化が行われる。 キャッシュされたテンプレート数がキャッシュ容量の9割を超えるごとにキャッシュ容量が自動で段階的に拡張されるため、一時的に性能が劣化する場合がある。 そのため、全テンプレートに満遍なくアクセスがあるような場合、全テンプレートをキャッシュできる十分なサイズになるように初期サイズを指定することを推奨する。 また、少数のテンプレートのみにアクセスが集中するような場合には、アクセス集中が考えられるテンプレート数の初期サイズを指定することを推奨する。 どちらの場合においてもSpringResourceTemplateResolverで生存時間を適切に指定することで、レスポンス性能の向上を期待できる。

キャッシュの最大サイズについても、テンプレート数単位で指定するため、1テンプレートの容量が大きいアプリではメモリ負荷が大きくなる場合がある。 テンプレートキャッシュに割り当てることができるメモリ容量に応じて、適切な値で指定する必要がある。

Note

キャッシュされたテンプレート数がキャッシュの最大サイズを超過する場合は、キャッシュ上でテンプレートの入れ替えが行われる。

15.1 Template Resolverには、 「キャッシュの生存時間を指定しない場合にはLRU(Least Recently Used)方式でのみキャッシュの削除が行われる」と記述されているが、実際にはFIFO(First-In First-Out)方式で実装されている。

Note

ログを出力する

ThymeleafのStandardCacheManagerはデフォルトでorg.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHEというロガー名でトレースログを出力する。 このトレースログには、テンプレートがキャッシュに追加、および削除されたこと、また一定時間ごとにキャッシュのヒット回数やヒット率などをまとめたキャッシュレポートが出力される。 キャッシュサイズの計算などに参考にされたい。

以下にログの出力例を示す。

  • キャッシュに追加される場合
date:2017-10-30 11:07:11    thread:http-nio-8080-exec-2    X-Track:f5e0a41eecf844259d94d7dcd9f292f5    level:TRACE    logger:o.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE    message:[THYMELEAF][CACHE_INITIALIZE] Initializing cache TEMPLATE_CACHE. Max size: 200. Soft references are used.
date:2017-10-30 11:07:11    thread:http-nio-8080-exec-2    X-Track:f5e0a41eecf844259d94d7dcd9f292f5    level:TRACE    logger:o.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE    message:[THYMELEAF][http-nio-8080-exec-2][TEMPLATE_CACHE][CACHE_MISS] Cache miss in cache "TEMPLATE_CACHE" for key "welcome/home".
date:2017-10-30 11:07:12    thread:http-nio-8080-exec-2    X-Track:f5e0a41eecf844259d94d7dcd9f292f5    level:TRACE    logger:o.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE    message:[THYMELEAF][http-nio-8080-exec-2][TEMPLATE_CACHE][CACHE_ADD][1] Adding cache entry in cache "TEMPLATE_CACHE" for key "welcome/home".
  • キャッシュにヒットした場合
date:2017-10-30 11:07:15    thread:http-nio-8080-exec-5    X-Track:aa6b912177ed483e87270f38d479b9a9    level:TRACE    logger:o.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE    message:[THYMELEAF][http-nio-8080-exec-5][TEMPLATE_CACHE][CACHE_HIT] Cache hit in cache "TEMPLATE_CACHE" for key "welcome/home".
  • キャッシュレポート
date:2017-10-30 11:13:15    thread:http-nio-8080-exec-3    X-Track:7377e5e09aff4d35ab391efe6f6b9958    level:TRACE    logger:o.thymeleaf.TemplateEngine.cache.TEMPLATE_CACHE    message:[THYMELEAF][*][*][*][CACHE_REPORT]        4 elements |            4 puts |           21 gets |           13 hits |            8 misses | 0.62 hit ratio | 0.38 miss ratio - [TEMPLATE_CACHE]

logback.xmlで以下のように設定することで、トレースログを出力することができる。 org.thymeleaf.TemplateEngine.cache以下のロガーを出力するように設定することで、org.thymeleaf.TemplateEngine.cache.EXPRESSION_CACHEなどのトレースログも出力することができる。 詳細については、「ロギング」を参照されたい。

  • logback.xml

    <configuration>
        <!-- ・・・ -->
        <logger name="org.thymeleaf.TemplateEngine.cache">
            <level value="trace" />
        </logger>
        <!-- ・・・ -->
    </configuration>