Skip to content

Instantly share code, notes, and snippets.

@binary2010
Forked from xaolex/Thymeleaf笔记.md
Created May 14, 2018 01:19
Show Gist options
  • Save binary2010/60d28f4ee8b06198ff711a97b63ac970 to your computer and use it in GitHub Desktop.
Save binary2010/60d28f4ee8b06198ff711a97b63ac970 to your computer and use it in GitHub Desktop.
Thymeleaf笔记

Thymeleaf笔记

前言

目前项目用到Thymeleaf2.1的版本,所以笔记写的都是这个版本的内容,而现在最新版是Thymeleaf3.0,之后有空再看下有何变化。


简介

Thymeleaf是一个可以处理XML / XHTML / HTML5的Java模板引擎,能用于转换模板文件,以显示应用程序产生的数据和文本。

可以处理的模板

  • XML
  • Valid XML
  • XHTML
  • Valid XHTML
  • HTML5
  • Legacy HTML5

标准方言

Thymeleaf是一个极具可扩展性的模板引擎(事实上更应称作模板引擎的框架),允许你自定义模板中处理的DOM节点,及如何处理他们。

总体架构

Thymeleaf的核心是一个DOM处理引擎,它使用自己高性能的DOM实现,而非的标准DOM API。


标准表达式语法

  • 简单表示式:

    • 变量表达式:${…}
    • 选择变量表达式:*{…}
    • 消息表达式:#{…}
    • URL链接表达式:@{…}
  • 字面量:

    • 文本:‘one text’ , ‘Another one!’ ,…
    • 数值:0 , 34 , 3.0 , 12.3 ,…
    • 布尔值:true , false
    • 空值:null
    • 文字标记:one , sometext , main ,…
  • 字符串操作:

    • 字符串连接:+
    • 文字替换:|The name is ${name}|
  • 算术操作:

    • 二元运算符:+ , - , * , / , %
    • 负号:-
  • 布尔操作:

    • 二元运算符:and , or
    • 逻辑非:! , not
  • 比较相等操作:

    • 比较:> , < , >= , <= ( gt , lt , ge , le )
    • 相等算法:== , != ( eq , ne )
  • 条件语句:

    • If-then: (if) ? (then)
    • If-then-else: (if) ? (then) : (else)
    • Default: (value) ?: (defaultvalue)

消息表达式

用法: #{...}

示例: <p th:utext="#{home.welcome}">Welcome to our grocery store!</p>

可配合变量表达式使用:

<p th:utext="#{home.welcome(${session.user.name})}">
  Welcome to our grocery store, Sebastian Pepper!
</p>

或者

<p th:utext="#{${welcomeMsgKey}(${session.user.name})}">
  Welcome to our grocery store, Sebastian Pepper!
</p>

变量表达式

变量表达式可以使用OGNL的语法。

示例:

/*
 * 使用点号(.)访问属性
 */
${person.father.name}

/*
 * 也可以使用中括号([])访问属性
 */
${person['father']['name']}

/*
 * 如果是map类型对象,点号和中括号语法相当于调用get方法
 */
${countriesByCode.ES}
${personsByName['Stephen Zucchini'].age}

/*
 * 通过索引来访问数组或集合
 */
${personsArray[0].name}

/*
 * 调用有参数方法
 */
${person.createCompleteName()}
${person.createCompleteNameWithSeparator('-')}
  • OGNL基本内置对象

    • #ctx:上下文对象
    • #vars:上下文变量
    • #locale :上下文语言环境
    • #httpServletRequest :(只在web context中) HttpServletRequest对象
    • #httpSession :(只在web context中) HttpSession对象
  • 表达式工具对象

    • #dates:为java.util.Date对象提供工具方法,比如:格式化,提取年月日等。
    • #calendars:类似于#dates,但是只针对java.util.Calendar对象。
    • #numbers:为数值型对象提供的工具方法。
    • #strings:为String对象提供的工具方法。包括:contains, startsWith, prepending/appending等。
    • #objects:为object对象提供的工具方法。
    • #bools:为boolean对象提供的工具方法。
    • #arrays:为array对象提供的工具方法。
    • #lists:为list对象提供的工具方法。
    • #sets:为set对象提供的工具方法。
    • #maps:为map对象提供的工具方法。
    • #aggregates:为创建array或者collection聚集函数提供的工具方法。
    • #messages:在表达式中获取外部信息的工具方法,与使用 #{…} 相同。
    • #ids:为处理可能重复的id属性提供的工具方法,例如,作为迭代的结果。

选择表达式用法(*{...})

变量表达式不仅能用在${...}上,还能用在*{...}上。两者的区别在于*{...}基于选定对象而不是上下文的对象进行运算,若无选定的对象,两者使用上是相同的。

用法:

  • 使用 th:object 选择对象,再通过 *{...} 使用对象属性。
  • $* 语法可以混合使用。
  • 选定的对象也可以通过#object进行访问。
  • 若无选择的对象, $* 语法效果是相同的。

示例:

<div th:object="${session.user}">
  <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
  <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

等同于:

<div>
  <p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
  <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div>

选定的对象也可以通过#object进行访问:

<div th:object="${session.user}">
  <p>Name: <span th:text="${#object.firstName}">Sebastian</span>.</p>
  <p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
  <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p>
</div>

若无选择的对象, $* 语法效果是相同的:

<div>
  <p>Name: <span th:text="*{session.user.name}">Sebastian</span>.</p>
  <p>Surname: <span th:text="*{session.user.surname}">Pepper</span>.</p>
  <p>Nationality: <span th:text="*{session.user.nationality}">Saturn</span>.</p>
</div>

URL链接表达式

  • th:href 属性修饰符:计算并替换 <a> 标签的href属性值。
  • 参数可使用表达式。
  • 多个参数使用逗号隔开。
  • 变量模板可用在URL路径中,如 @{/order/{orderId}/details(orderId=${orderId})}
  • URL中以 / 开头的路径(如 /order/details )将会自动在前缀加上应用上下文地址。
  • 如果cookies被禁用需要重写URL,可通过 response.encodeURL(...) 插入重写过滤器。
  • th:href 标签可以使用静态的href属性。
  • 可使用 @{~/path/to/something} 创建服务器相对根路径来链接同一台服务器不同上下文。

字面量

  • 文本:包含在单引号内的字符串,单引号字符需要用斜杠 \ 转义。
  • 数值:0 , 34 , 3.0 , 12.3 ,…
  • 布尔值:true , false
  • 空值:null
  • 文字标记(Literal tokens):数值、布尔值、空值实际上是特殊的文字标记。这些文件标记使表达式更加简洁,其工作方式和文本完全一样,但是只能使用字符(a-z,A-Z),数值(0-9),中括号,点号,连字符和下划线。不能有空格和逗号等。

文字替换

文字替换需要使用 | 来包围替换的内容。注意:仅变量表达式(${...})允许在文字替换( |...| )中使用,而像文本、布尔值、数值都不允许。

示例:

<span th:text="|Welcome to our application, ${user.name}!|">

等价于:

<span th:text="'Welcome to our application, ' + ${user.name} + '!'">

与其他表达式结合使用:

<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">

算术操作

在表达式中可以使用一些算术运算符,如: +, -, *, /, % 。这些运算符实际会被OGNL解析执行。注意这些运算符存在文本别名: div (/), mod (%)

比较相等操作

表达式中的值可以通过 >, <, >=, <= 来进行比较,也可以通过 ==!= 判断是否相等。注意XML元素不允许属性值使用 <> 符号,应该使用 &lt;&gt; 取代。

注意对应的文本别名: gt (>), lt (<), ge (>=), le (<=), not (!), eq (==), neq/ne (!=)

条件表达式

条件表达式所有部分(condition, then, else)都可以使用变量表达式(${...}, *{...}),消息表达式(#{...}),URL链接(@{...})和文本 ('...')。条件表达式中else部分也可以省略,自动返回null值。

缺省表达式(Elvis操作符)

缺省表达式是没有then部分时提供一个缺省值,如果条件判断值不为空就用这个条件的值,否则就用缺省值。

示例:

<div th:object="${session.user}">
  ...
  <p>Age: <span th:text="*{age}?: '(no age specified)'">27</span>.</p>
</div>

等价于:

<p>Age: <span th:text="*{age != null}? *{age} : '(no age specified)'">27</span>.</p>

预处理表达式

预处理是在表达式被执行之前进行处理的操作,处理后的表达式被实际执行。预处理表达式和一般的表达式很像,但被双下划线包含( __${expression}__ )。

示例:

国际化资源信息的文件Message_fr.properties包含一个特定语言的OGNL表达式

[email protected]@translateToFrench({0})

Messages_es.properties中包含的信息如下:

[email protected]@translateToSpanish({0})

使用:

<p th:text="${__#{article.text('textVar')}__}">Some text here...</p>

法语地区会被预处理成:

<p th:text="${@myapp.translator.Translator@translateToFrench(textVar)}">Some text here...</p>

安全导航运算符

Thymeleaf中可以使用安全导航操作符 ?. ,这是来自Spring EL的用法,参考了Groovy。安全导航操作符将简单地返回空代替抛出的异常,用来避免产生空指针异常。

示例:

<span th:text="${error?.summary}">Static summary</span>

设置属性值

  • 为任意属性设置值: th:attr ,多个属性值可用逗号隔开,示例: <input type="submit" value="Subscribe me!" th:attr="value=#{subscribe.submit}"/>
  • 为指定属性设置值: th:value ,包括 th:action th:class th:style 等,示例 <form action="subscribe.html" th:action="@{/subscribe}">
  • 一次设置多个值: th:alt-titleth:lang-xmllang ,前者设置alt和title属性,后者设置lang和xmllang属性。示例: <img src="../../images/gtvglogo.png" th:src="@{/images/gtvglogo.png}" th:alt-title="#{logo}" />
  • 追加和预加: th:attrappendth:attrprepend ,此外还可以使用 th:classappendth:styleappend 对class和style进行追加。示例: <input type="button" value="Do it!" class="btn" th:attrappend="class=${' ' + cssStyle}" />
  • 固定值布尔属性: th:checked th:disabled th:multiple th:readonly th:selected 等,标准表达式可以通过计算条件来设置这些固定值属性。示例: <input type="checkbox" name="active" th:checked="${user.active}" />
  • HTML5友好的属性和元素名称: data-{prefix}-{name} ,“data-前缀-名称”是HTML5中的自定义属性,这种语法是 th:* 的附加方式。

迭代

标准表达式提供一个使用迭代的属性 th:each , 可迭代的对象包括:

  • java.util.List
  • java.util.Iterable
  • java.util.Map
  • 数组
  • 任何包含自身的单值列表

记录迭代状态

Thymeleaf提供了一种跟踪迭代器状态的机制:状态变量,包含了以下数据:

  • 当前的迭代索引,从0开始,属性为index;
  • 当前的迭代索引,从1开始,属性为count;
  • 迭代集合长度,属性为size;
  • 当前的迭代变量,属性为current;
  • 当前的迭代是奇数还是偶数,even/odd布尔属性
  • 当前的迭代是否是第一个元素,属性为first的布尔值
  • 当前的迭代是否为最后一个元素,属性为last的布尔值

状态变量(iterStat)定义在th:each属性中,用逗号与迭代变量分隔开。若无定义,默认可通过迭代变量名加后缀 Stat 来使用。示例:

<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
    <td th:text="${prod.name}">Onions</td>
    <td th:text="${prod.price}">2.41</td>
    <td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>

条件判断

简单判断:“if” 和 “unless”

条件判断的内容只在条件判断成立时才显示。th:if “反向”属性是 th:unless。注意 th:if 可以根据以下规则判断为“true”:

  • 布尔值为true
  • 非零数值
  • 非零字符
  • 字符串(非“false”, “off” 或 “no)
  • 非布尔值、数值、字符、字符串

Switch 语句

Thymeleaf中提供了类似Java中的switch结构语法: th:switch / th:case 。注意,只要有一个case条件为真,其它剩余的都不会继续判断。switch的缺省选型是 th:case="*"

<div th:switch="${user.role}">
    <p th:case="'admin'">User is an administrator</p>
    <p th:case="#{roles.manager}">User is a manager</p>
    <p th:case="*">User is some other thing</p>
</div>

模板布局

包含模版片段

定义与引用片段: th:fragmentth:include th:replace

Thymeleaf表达式将在片段被 th:includeth:replace 引入到模版中时解析,它们可以引用上下文环境中的变量。有三种引用方式:

  • "templatename::domselector" 或 "templatename::[domselector]":包含templatename指定部分
  • "templatename":包含完整模板
  • "::domselector" 或 "this::domselector":当前模版中的片段

示例:

<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">

  <body>
  
    <div th:fragment="copy">
      &copy; 2011 The Good Thymes Virtual Grocery
    </div>
  
  </body>
  
</html>

引用:

<body>

  ...

  <div th:include="footer :: copy"></div>
  
</body>

也可以通过id进行引用:

<div id="copy-section">
  &copy; 2011 The Good Thymes Virtual Grocery
</div>

通过 footer :: #copy-section来引用。

th:includeth:replace 的区别

  • th:include 会将片段的内容包含进宿主标签。
  • th:replace 会用片段的标签取代宿主标签。
  • th:replace 可以用别名 th:substituteby

参数化片段签名

th:fragment 属性可以接收一系列参数:

<div th:fragment="frag (onevar,twovar)">
    <p th:text="${onevar} + ' - ' + ${twovar}">...</p>
</div>

引入:

<div th:include="::frag (${value1},${value2})">...</div>
<div th:include="::frag (onevar=${value1},twovar=${value2})">...</div>

使用无参数的片段本地变量:

<div th:include="::frag (onevar=${value1},twovar=${value2})">

相当于:

<div th:include="::frag" th:with="onevar=${value1},twovar=${value2}">

th:assert 在模版内进行断言:

<div th:assert="${onevar},(${twovar} != 43)">...</div>

可以用于验证片段签名:

<header th:fragment="contentheader(title)" th:assert="${!#strings.isEmpty(title)}">...</header>

移除模板片段

可以使用 th:remove 来移除模板片段,th:remove 可以处理Thymeleaf标准表达式的标签,有五种不同的方式:

  • all:删除标签和标签内的所有子元素
  • body:不删除标签,但删除标签的子元素
  • tag:只删除标签,但不删除子元素
  • all-but-first:删除除了第一个之外的所有子元素
  • none(空值):什么都不删除,一般用于动态解析

本地变量

Thymeleaf称那些定义在模板的特定片段,且只能用于该片段中的变量为本地变量。可以使用 th:with 定义或者在迭代中定义。允许重用在当前 th:with 中已经定义的变量。

有效范围:

  • 在标签范围内,对任何其他th:*标签有效。
  • 标签的所有子元素内都有效。

属性优先级

所有的Thymeleaf属性都定义了优先级,顺序如下:

Order Feature Attributes
1 Fragment inclusion th:include th:replace
2 Fragment iteration th:each
3 Conditional evaluation th:if th:unless th:switch th:case
4 Local variable definition th:object th:with
5 General attribute modification th:attr th:attrprepend th:attrappend
6 Specific attribute modification th:value th:href th:src ...
7 Text (tag body modification) th:text th:utext
8 Fragment specification th:fragment
9 Fragment removal th:remove

注释和块

  • 标准的HTML/XML注释: <!-- ... --> 能在 Thymeleaf 模板中任意地方使用,不会被处理。
  • Thymeleaf解析器层注释块: <!--/* ... */--> 部分当解析模板时会被解析器移除。
  • Thymeleaf原型仅存注释块: <!--/*//*/--> 在静态打开时会被注释,而正常解析时会作为正常标签。
  • 合成的th:block块:Thymeleaf中唯一元素级别的处理器是 th:block ,它是一个纯粹的属性容器,允许开发人员指定他们想要的属性。Thymeleaf将只执行块中的属性,而不会保留块本身的内容。

内联

文本内联

Thymeleaf内联表达式 [[...]] 可以使用其他在 th:text 属性中有效的表达式。要想使用这种语法,首先要用 th:inline 属性激活它,分别有三种不同的属性值: text, javascript, none 。需要注意这种用法在静态打开页面会直接显示表达式的内容。示例如下:

<p th:inline="text">Hello, [[${session.user.name}]]!</p>

脚本内联(JavaScript 和 Dart)

目前包括两种模式:javascript (th:inline="javascript") and dart (th:inline="dart")。

<script th:inline="javascript">
/*<![CDATA[*/
    ...    
    var username = /*[[${session.user.name}]]*/ 'Sebastian';    
    ...
/*]]>*/
</script>

/*[[...]]*/ 这种语法有几个好处:

  • javascript注释(/.../)会忽略表达式中的内容。
  • 内联表达式后的值在静态打开时会被显示出来。
  • Thymeleaf解析时会将计算后的值替换掉静态值。

Thymeleaf可以智能地解析以下几种对象:

  • Strings
  • Numbers
  • Booleans
  • Arrays
  • Collections
  • Maps
  • Beans(包含getter和setter方法的对象)

示例:

<script th:inline="javascript">
/*<![CDATA[*/
    ...

    var user = /*[[${session.user}]]*/ null;

    ...
/*]]>*/
</script>

会被转换为:

<script th:inline="javascript">
/*<![CDATA[*/
    ...

    var user = {'age':null,'firstName':'John','lastName':'Apricot',
                'name':'John Apricot','nationality':'Antarctica'};

    ...
/*]]>*/
</script>

/*[+...+]*/ 注释语法可以在解析时解除注释:

var x = 23;

/*[+

var msg  = 'This is a working application';

+]*/

var f = function() {
    ...

/*[- *//* -]*/ 注释语法可以在解析时动态移除内容:

var x = 23;

/*[- */

var msg  = 'This is a non-working template';

/* -]*/

var f = function() {
...

模版缓存

默认情况下Thymeleaf包括一个用于保存解析后的模版的缓存,它保存了读取模版文件后解析出的一些列事件对象。这对于创建Web应用程序方面有以下好处:

  • 输入输出几乎是任何应用程序中耗时最长的环节,相比而言在内存中的处理速度是最快的。
  • 从内存中克隆一个已经存在的事件序列肯定要比从模版中重新读取、解析后再创建一次要更快。
  • Web应用程序通常只有为数不多的模版。
  • 模版文件的尺寸通常不大,并且在应用程序运行期间不会被修改。

在模版解析器中打开和关闭缓存:

// 默认 true
templateResolver.setCacheable(false);
templateResolver.getCacheablePatternSpec().addPattern("/users/*");

通过设置缓存管理对象来配置相关参数,缓存管理对象的默认实现是StandardCacheManager:

//  默认 50
StandardCacheManager cacheManager = new StandardCacheManager();
cacheManager.setTemplateCacheMaxSize(100);
...
templateEngine.setCacheManager(cacheManager);

更多配置选项可以参考org.thymeleaf.cache.StandardCacheManager的JavaDoc。

缓存也可以被手工删除:

// 完全删除缓存
templateEngine.clearTemplateCache();
// 从缓存中删除一个指定的模版
templateEngine.clearTemplateCacheFor("/users/userList");

DOM 选择器语法

Thymeleaf的DOM选择器借鉴了XPath,CSS和JQuery的语法特性。下面是借鉴XPath的基本语法:

  • /x 选择当前节点的名为x的直接子元素。
  • //x 选择当前节点下所有子节点(任何层次)的子元素。
  • x[@z="v"] 选择节点名称为x 并且包含属性z且z的值为v的元素。
  • x[@z1="v1" and @z2="v2"] 表示选择节点名称为x 标签包含z1和z2属性,且它们的值分别为v1和v2的元素。
  • x[i] 表示选择名为x的元素中的第i个元素。
  • x[@z="v"][i] 表示选择名为x并且其属性z的值为v的第i个元素。

其他语法还包括:

  • x 相当于 //x
  • [@class="oneclass"] 表示选择class属性为oneclass的所有元素。
  • 比较操作符除了=,还有!=,^=(开始于),$=(结束于)。比如 x[@class^='section'] 表示查找x元素,并且其class属性以section开头。
  • 符号@ 也可以被省略,比如 x[z='v']等同于x[@z='v']。
  • 多个属性选择器的叠加可以通过and,也可以通过链式叠加的方式,比如x[@z1="v1" and @z2="v2"] 等同于x[@z1='v1'][@z2='v2'] 或者x[z1='v1'][z2='v2']。

JQuery风格的选择器:

  • x.oneclass 等同 x[class='oneclass']。
  • .oneclass 等同 [class='oneclass']。
  • x#oneid 等同 x[id='oneid']。
  • #oneid 等同 [id='oneid']。
  • x%oneref 表示x元素有一个 th:ref="oneref" 属性或者 th:fragment="oneref" 属性。
  • %oneref 表示任何元素有一个 th:ref="oneref" 属性或者 th:fragment="oneref" 属性,与直接使用oneref效果相同。
  • 直接的选择器和属性选择器可以混合使用,比如: a.external[@href^='https']

更多参考

更多接口和其他信息可以参考官方文档附录Javadocs/API 文档

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment