1. 序章

FreeMarkerは、Javaで記述され、ApacheFoundationによって保守されているテンプレートエンジンです。 FreeMarkerテンプレート言語(FTLとも呼ばれます)を使用して、Webページ、電子メール、XMLファイルなどの多くのテキストベースの形式を生成できます。

このチュートリアルでは、FreeMarkerを使用してすぐに実行できることを確認しますが、FreeMarkerは完全に構成可能であり、もSpringとうまく統合できることに注意してください。

始めましょう!

2. クイック概要

ページに動的コンテンツを挿入するには、FreeMarkerが理解できる構文を使用する必要があります

  • テンプレートの${…}は、生成された出力で中括弧内の式の実際の値に置き換えられます–これを補間と呼びます–いくつかの例は[X202X ] $ {1 +2}および${variableName}
  • FTLタグはHTMLタグに似ています(ただし、 また @ )そしてFreeMarkerはそれらを解釈します、例えば <#if…>#if>
  • FreeMarkerのコメントは <#– そして最後に –>

3. インクルードタグ

FTL include ディレクティブは、アプリケーションでDRYの原則に従うための方法です。 ファイル内の反復コンテンツを定義し、単一のincludeタグを使用してさまざまなFreeMarkerテンプレートで再利用します。

そのようなユースケースの1つは、多くのページ内にメニューセクションを含めたい場合です。 まず、ファイル内にメニューセクションを定義します(これを menu.ftl と呼びます)。次の内容を使用します。

<a href="#dashboard">Dashboard</a>
<a href="#newEndpoint">Add new endpoint</a>

そして、HTMLページに、作成したmenu.ftlを含めましょう。

<!DOCTYPE html>
<html>
<body>
<#include 'fragments/menu.ftl'>
    <h6>Dashboard page</h6>
</body>
</html>

また、フラグメントにFTLを含めることもできます。これはすばらしいことです。

4. 価値の存在を処理する

FTLは、null値を欠落値と見なします。 したがって、テンプレート内でnull を処理するために、特に注意してロジックを追加する必要があります。

?? 演算子を使用して、属性またはネストされたプロパティが存在するかどうかを確認できます。 結果はブール値です:

${attribute??}

そのため、 null、の属性をテストしましたが、それだけでは必ずしも十分ではありません。 次に、この欠落している値のフォールバックとしてデフォルト値を定義しましょう。 これを行うには、変数の名前の後に演算子を配置する必要があります。

${attribute!'default value'}

丸括弧を使用して、多くのネストされた属性をラップできます。

たとえば、属性が存在し、ネストされたプロパティと別のネストされたプロパティがあるかどうかを確認するには、すべてをラップします。

${(attribute.nestedProperty.nestedProperty)??}

最後に、すべてをまとめると、静的コンテンツにこれらを埋め込むことができます。

<p>Testing is student property exists: ${student???c}</p>
<p>Using default value for missing student: ${student!'John Doe'}</p>
<p>Wrapping student nested properties: ${(student.address.street)???c}</p>

そして、の学生 null だった場合、次のようになります。

<p>Testing is student property exists: false</p>
<p>Using default value for missing student: John Doe</p>
<p>Wrapping student nested properties: false</p>

?? の後に使用される追加の?cディレクティブに注意してください。 ブール値を人間が読める形式の文字列に変換するために行いました。

5. If-Elseタグ

制御構造はFreeMarkerに存在し、従来のif-elseはおそらくおなじみです。

<#if condition>
    <!-- block to execute if condition is true -->
<#elseif condition2>
    <!-- block to execute if condition2 is the first true condition -->
<#elseif condition3>
    <!-- block to execute if condition3 is the first true condition -->
<#else>
    <!-- block to execute if no condition is true -->
</#if>

elseifおよびelseブランチはオプションですが、条件はブール値に解決される必要があります。

評価を支援するために、次のいずれかを使用する可能性があります。

  • x == y は、xyと等しいことを確認します
  • x!= y は、xyと異なる場合にのみ、trueを返します。
  • x lt y という意味ですバツ厳密に小さくする必要があります y –使用することもできます < それ以外の lt
  • x gtyxyよりも厳密に大きい場合にのみ、trueと評価されます。代わりに>を使用できます。 gt
  • x lte y 次の場合にテストしますバツ以下である y –の代替 LTE <=
  • x gte y は、xy以上であるかどうかをテストします– gteの代替は>=
  • x ?? は、xの存在を確認します
  • sequence?seqContains(x)は、シーケンス内のxの存在を検証します

それを覚えておくことは非常に重要です FreeMarkerは、>=および>をFTLタグの終了文字と見なします。 解決策は、それらの使用法を括弧で囲むか、 代わりにgteまたはgtを使用してください。

次のテンプレートをまとめると、次のようになります。

<#if status??>
    <p>${status.reason}</p>
<#else>
    <p>Missing status!</p>
</#if>

結果として得られるHTMLコードは次のようになります。

 <!-- When status attribute exists -->
<p>404 Not Found</p>

<!-- When status attribute is missing -->
<p>Missing status!</p>

6. サブ変数のコンテナー

FreeMarkerには、サブ変数用の3つのタイプのコンテナーがあります。

  • ハッシュは、キーと値のペアのシーケンスです。キーはハッシュ内で一意である必要があり、順序はありません。
  • シーケンスは、各値に関連付けられたインデックスがあるリストです。注目すべき事実は、サブ変数がさまざまなタイプである可能性があることです。
  • コレクションは、サイズにアクセスしたり、インデックスで値を取得したりできない特殊なケースです。ただし、 list タグを使用してそれらを繰り返すことはできます!

6.1. アイテムの繰り返し

コンテナを反復処理するには、2つの基本的な方法があります。 1つ目は、各値を反復処理し、それぞれに対してロジックを実行する場所です。

<#list sequence as item>
    <!-- do something with ${item} -->
</#list>

または、 Hash を繰り返し処理する場合は、キーと値の両方にアクセスします。

<#list hash as key, value>
    <!-- do something with ${key} and ${value} -->
</#list>

2番目の形式は、反復のさまざまなステップで発生するロジックを定義できるため、より強力です。

<#list sequence>
    <!-- one-time logic if the sequence is not empty -->
    <#items as item>
        <!-- logic repeated for every item in sequence -->
    </#items>
    <!-- one-time logic if the sequence is not empty -->
<#else>
    <!-- one-time logic if the sequence is empty -->
</#list>

item はループされた変数の名前を表しますが、名前を変更することができます。 elseブランチはオプションです。

実践的な例として、いくつかのステータスをリストするテンプレートを適切に定義します。

<#list statuses>
    <ul>
    <#items as status>
        <li>${status}</li>
    </#items>
    </ul>
<#else>
    <p>No statuses available</p>
</#list>

これにより、コンテナが [“ 200 OK”、“ 404 Not Found”、“ 500 Internal Server Error”] の場合、次のHTMLが返されます。

<ul>
<li>200 OK</li>
<li>404 Not Found</li>
<li>500 Internal Server Error</li>
</ul>

6.2. アイテムの取り扱い

ハッシュを使用すると、2つの単純な関数が可能になります。 keys は含まれているキーのみを取得し、valuesは値のみを取得します。

シーケンスはより複雑です。 最も便利な関数をグループ化できます。

  • チャンク結合を使用して、サブシーケンスを取得するか、2つのシーケンスを結合します
  • 要素の順序を変更するためのreverse sort、、および sortBy
  • firstおよびlastは、それぞれ最初または最後の要素を取得します
  • size は、シーケンス内の要素の数を表します
  • seqContains seqIndexOf 、または seqLastIndexOf を使用して、要素を検索します

7. タイプ処理

FreeMarkerには、オブジェクトの操作に使用できるさまざまな関数(組み込み)が付属しています。 よく使う関数を見てみましょう。

7.1. 文字列処理

  • urlおよびurlPathは、 urlPath がスラッシュ/をエスケープしないことを除いて、文字列をURLエスケープします。
  • jString jsString、 jsonString は、それぞれJava、Javascript、JSONのエスケープルールを適用します
  • capFirst uncapFirst upperCase lowerCase Capitalize は、文字列の大文字と小文字を変更するのに役立ちます。彼らの名前によって暗示される
  • boolean date time datetime number は、文字列から他のタイプに変換するための関数です。

これらの関数のいくつかを使用してみましょう。

<p>${'http://myurl.com/?search=Hello World'?urlPath}</p>
<p>${'Using " in text'?jsString}</p>
<p>${'my value?upperCase}</p>
<p>${'2019-01-12'?date('yyyy-MM-dd')}</p>

上記のテンプレートの出力は次のようになります。

<p>http%3A//myurl.com/%3Fsearch%3DHello%20World</p>
<p>MY VALUE</p>
<p>Using \" in text</p>
<p>12.01.2019</p>

date 関数を使用する場合、Stringオブジェクトの解析に使用するパターンも渡しました。 FreeMarkerは、特に日付オブジェクトで使用可能な string 関数で特に指定されていない限り、ローカル形式を使用します

7.2. 番号の取り扱い

  • ラウンドフロア天井は、数値の丸めに役立ちます
  • absは数値の絶対値を返します
  • stringは数値を文字列に変換します。 コンピューター通貨数値パーセントの4つの事前定義された数値形式を渡すか、次のような独自の形式を定義することもできます。 [“0。###”]

いくつかの数学演算のチェーンを実行してみましょう:

<p>${(7.3?round + 3.4?ceiling + 0.1234)?string('0.##')}</p>
<!-- (7 + 4 + 0.1234) with 2 decimals -->

そして予想通り、結果の値は11.12。です。

7.3. 日付処理

  • .nowは現在の日時を表します
  • date time datetime は、日時オブジェクトの日付と時刻のセクションを返すことができます
  • string は日時を文字列に変換します–希望の形式を渡すか、事前定義された形式を使用することもできます

次に、現在の時刻を取得し、出力を時間と分のみを含む文字列にフォーマットします。

<p>${.now?time?string('HH:mm')}</p>

結果のHTMLは次のようになります。

<p>15:39</p>

8. 例外処理

FreeMarkerテンプレートの例外を処理する2つの方法を見ていきます。

最初の方法は、 tryt-recover タグを使用して、実行しようとする内容と、エラーが発生した場合に実行するコードのブロックを定義することです。

構文は次のとおりです。

<#attempt>
    <!-- block to try -->
<#recover>
    <!-- block to execute in case of exception -->
</#attempt>

attemptタグとrecoverタグの両方が必須です。 エラーが発生した場合、試行されたブロックをロールバックし、リカバリセクションのコードのみを実行します。

この構文を念頭に置いて、テンプレートを次のように定義しましょう。

<p>Preparing to evaluate</p>
<#attempt>
    <p>Attribute is ${attributeWithPossibleValue??}</p>
<#recover>
    <p>Attribute is missing</p>
</#attempt>
<p>Done with the evaluation</p>

attributeWithPossibleValue がない場合、次のように表示されます。

<p>Preparing to evaluate</p>
    <p>Attribute is missing</p>
<p>Done with the evaluation</p>

また、attributeWithPossibleValueが存在する場合の出力は次のとおりです。

<p>Preparing to evaluate</p>
    <p>Attribute is 200 OK</p>
<p>Done with the evaluation</p>

2番目の方法は、例外の場合に何が起こるかをFreeMarkerに設定することです。

Spring Bootを使用すると、プロパティファイルを介してこれを簡単に構成できます。 利用可能な構成は次のとおりです。

  • spring.freemarker.setting.template_exception_handler =rethrowは例外を再スローします
  • spring.freemarker.setting.template_exception_handler = debug は、スタックトレース情報をクライアントに出力してから、例外を再スローします。
  • spring.freemarker.setting.template_exception_handler = html_debug は、スタックトレース情報をクライアントに出力し、通常はブラウザーで読みやすいようにフォーマットしてから、例外を再スローします。
  • spring.freemarker.setting.template_exception_handler = ignore は失敗した命令をスキップし、テンプレートの実行を継続させます。
  • spring.freemarker.setting.template_exception_handler = default

9. メソッドの呼び出し

FreeMarkerテンプレートからJavaメソッドを呼び出したい場合があります。 これを行う方法を見ていきます。

9.1. 静的メンバー

静的メンバーへのアクセスを開始するには、グローバルFreeMarker構成を更新するか、モデルにstaticsという属性名でStaticModelsタイプの属性を追加します。

model.addAttribute("statics", new DefaultObjectWrapperBuilder(new Version("2.3.28"))
    .build().getStaticModels());

静的要素へのアクセスは簡単です。

まず、assignタグを使用してクラスの静的要素をインポートし、次に名前を決定し、最後にJavaクラスパスを決定します。

テンプレートにMathクラスをインポートし、静的 PI フィールドの値を表示し、静的powメソッドを使用する方法は次のとおりです。

<#assign MathUtils=statics['java.lang.Math']>
<p>PI value: ${MathUtils.PI}</p>
<p>2*10 is: ${MathUtils.pow(2, 10)}</p>

結果のHTMLは次のとおりです。

<p>PI value: 3.142</p>
<p>2*10 is: 1,024</p>

9.2. Beanメンバー

Beanメンバーへのアクセスは非常に簡単です。ドット(。)を使用すれば、それだけです。

次の例では、Randomオブジェクトをモデルに追加します。

model.addAttribute("random", new Random());

FreeMarkerテンプレートで、ランダムな数字を生成しましょう。

<p>Random value: ${random.nextInt()}</p>

これにより、次のような出力が発生します。

<p>Random value: 1,329,970,768</p>

9.3. カスタムメソッド

カスタムメソッドを追加するための最初のステップは、FreeMarkerの TemplateMethodModelEx インターフェイスを実装し、execメソッド内にロジックを定義するクラスを用意することです。

public class LastCharMethod implements TemplateMethodModelEx {
    public Object exec(List arguments) throws TemplateModelException {
        if (arguments.size() != 1 || StringUtils.isEmpty(arguments.get(0)))
            throw new TemplateModelException("Wrong arguments!");
        String argument = arguments.get(0).toString();
        return argument.charAt(argument.length() - 1);
    }
}

新しいクラスのインスタンスをモデルの属性として追加します。

model.addAttribute("lastChar", new LastCharMethod());

次のステップは、テンプレート内で新しいメソッドを使用することです。

<p>Last char example: ${lastChar('mystring')}</p>

最後に、結果の出力は次のとおりです。

<p>Last char example: g</p>

10. 結論

この記事では、プロジェクト内でFreeMarkerテンプレートエンジンを使用する方法を見てきました。 一般的な操作、さまざまなオブジェクトの操作方法、およびいくつかのより高度なトピックに焦点を当ててきました。

これらすべてのスニペットの実装は、GitHub利用できます。