Skip to content

Thymeleaf

模板引擎有哪些?

  • JSP
  • Velocity
  • Thymeleaf
  • FreeMarker

1.Thymeleaf简介

https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html

http://commons.apache.org/proper/commons-ognl/

https://www.kaifaxueyuan.com/frontend/thymeleaf3/thymeleaf3-setting-attribute-values.html

thymeleaf_logo

Thymeleaf是用来开发 Web 和独立环境项目的现代服务器端 Java 模板引擎。

Thymeleaf 的主要目标是为您的开发工作流程带来优雅的自然模板 - HTML。可以在直接浏览器中正确显示,并且可以作为静态原型,从而在开发团队中实现更强大的协作。

借助 Spring Framework 的模块,可以根据自己的喜好进行自由选择,可插拔功能组件,Thymeleaf 是现代 HTML5 JVM Web 开发的理想选择 - 尽管它可以做的更多。

Spring 官方支持的服务的渲染模板中,并不包含 jsp。而是 Thymeleaf 和 Freemarker 等,而 Thymeleaf 与 SpringMVC 的视图技术,及 SpringBoot 的自动化配置集成非常完美,几乎没有任何成本,你只用关注 Thymeleaf 的语法即可。

2.特点

  • 动静结合:Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。
  • 开箱即用:它提供标准和 spring 标准两种方言,可以直接套用模板实现 JSTL、 OGNL 表达式效果,避免每天套模板、改 jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。
  • 多方言支持:Thymeleaf 提供 spring 标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。
  • 与 SpringBoot 完美整合,SpringBoot 提供了 Thymeleaf 的默认配置,并且为 Thymeleaf 设置了视图解析器,我们可以像以前操作 jsp 一样来操作 Thymeleaf。代码几乎没有任何区别,就是在模板语法上有区别。

快速入门

导入依赖

  • pom.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>practice-thymeleaf</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <!-- javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>

        <!-- spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.7</version>
        </dependency>

        <!-- spring-aspect -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.7</version>
        </dependency>

        <!-- spring-web -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.3.7</version>
        </dependency>

        <!-- spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.7</version>
        </dependency>

        <!-- thymeleaf-spring5 -->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.12.RELEASE</version>
        </dependency>
    </dependencies>

</project>

配置SpringMVC

  • application-context.xml
xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/util
        https://www.springframework.org/schema/util/spring-util.xsd">

    <context:component-scan base-package="com.futureweaver"/>

    <bean id="templateResolver"
          class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"
          p:prefix=""
          p:suffix=""
          p:characterEncoding="UTF-8"
          p:order="1"
          p:templateMode="HTML5"
          p:cacheable="false"/>

    <bean id="templateEngine"
          class="org.thymeleaf.spring5.SpringTemplateEngine"
          p:templateResolver-ref="templateResolver"/>

    <bean id="viewResolver"
          class="org.thymeleaf.spring5.view.ThymeleafViewResolver"
          p:templateEngine-ref="templateEngine"
          p:characterEncoding="UTF-8"/>

</beans>

编写Controller

  • WelcomeController.java
java
@Controller
public class WelcomeController {
    @RequestMapping("/welcome")
    public String index(Model model) {
        model.addAttribute("name", "张三");
        return "/index.html";
    }
}

编写html

  • index.html
html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1 th:text="${name}">h1 title</h1>
</body>
</html>

标准表达式语法

  1. 简单表达式

    • 变量表达式: ${...}
    • 选择表达式: *{...}
    • 消息表达式: #{...}
    • 链接表达式: @{...}
    • 片段表达式: ~{...}
  2. 常量

    • 文本常量: 'one text', 'Another one!', ...
    • 数字常量: 0, 34, 3.0, 12.3, ...
    • 布尔常量: true, false
    • Null 常量: null
    • 常量标记: one, sometext, main, ...
  3. 文本操作

    • 字符串级联: +
    • 常量替换: |The name is ${name}|
  4. 算术运算符

    • +, -, *, /, %
  5. 逻辑运算符

    • and, or, !, not
  6. 比较运算符

    • >, <, >=, <=, ==, !=
    • gt, lt, ge, le, eq, ne
  7. 条件运算符

    • If-then: (if) ? (then)
    • If-then-else: (if) ? (then) : (else)
    • Default: (value) ?: (defaultvalue)
  8. 特殊标记

    • No-Operation: _

#{...}消息表达式

  • home_en.properties

    英语

  • home_es.properties

    西班牙语

  • home_pt_BR.properties

    巴西语

  • home.properties

    默认语言

    properties
    home.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}!
  • home.html

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

${...}变量表达式

${...}表达式实际上是在上下文中包含的变量映射上执行的OGNL表达式。

基本对象

  • #ctx

    the context object.

  • #vars

    the context variables.

  • #locale

    the context locale.

  • #request

    (only in Web Contexts) the HttpServletRequest object.

  • #response

    (only in Web Contexts) the HttpServletResponse object.

  • #session

    (only in Web Contexts) the HttpSession object.

  • #servletContext

    (only in Web Contexts) the ServletContext object.

实用工具对象

  • #execInfo

    information about the template being processed.

  • #messages

    methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax.

  • #uris

    methods for escaping parts of URLs/URIs

  • #conversions

    methods for executing the configured conversion service (if any).

  • #dates

    methods for java.util.Date objects: formatting, component extraction, etc.

  • #calendars

    analogous to #dates, but for java.util.Calendar objects.

  • #numbers

    methods for formatting numeric objects.

  • #strings

    methods for String objects: contains, startsWith, prepending/appending, etc.

  • #objects

    methods for objects in general.

  • #bools

    methods for boolean evaluation.

  • #arrays

    methods for arrays.

  • #lists

    methods for lists.

  • #sets

    methods for sets.

  • #maps

    methods for maps.

  • #aggregates

    methods for creating aggregates on arrays or collections.

  • #ids

    methods for dealing with id attributes that might be repeated (for example, as a result of an iteration).

*{...}选择表达式

html
<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>

@{...}链接表达式

  • 绝对路径

    • @{http://www.thymeleaf.org}
  • 相对路径

    • 相对页面

      @{user/login.html}

    • 相对上下文

      @{/itemdetails?id=3}

    • 相对服务器

      @{~/billing/processInvoice}

html
<!-- Will produce 'http://localhost:8080/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" 
   th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">view</a>

<!-- Will produce '/gtvg/order/details?orderId=3' (plus rewriting) -->
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>

<!-- Will produce '/gtvg/order/3/details' (plus rewriting) -->
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>

~{...}片段表达式

html
<div th:insert="~{commons :: main}">...</div>
html
<div th:with="frag=~{footer :: #main/text()}">
  <p th:insert="${frag}">
</div>

Literals

Text literals

html
<p>
  Now you are looking at a <span th:text="'working web application'">template file</span>.
</p>

Number literals

html
<p>The year is <span th:text="2013">1492</span>.</p>
<p>In two years, it will be <span th:text="2013 + 2">1494</span>.</p>

Boolean literals

html
<div th:if="${user.isAdmin()} == false"> ...
<div th:if="${user.isAdmin() == false}"> ...

The null literal

html
<div th:if="${variable.something} == null"> ...

Literal tokens

html
<div th:class="content">...</div>
<div th:class="'content'">...</div>

Appending texts

html
<span th:text="'The name of the user is ' + ${user.name}">

Literal substitutions

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

Arithmetic operations

html
<div th:with="isEven=(${prodStat.count} % 2 == 0)">
<div th:with="isEven=${prodStat.count % 2 == 0}">

Comparators and Equality

html
<div th:if="${prodStat.count} &gt; 1">
<span th:text="'Execution mode is ' + ( (${execMode} == 'dev')? 'Development' : 'Production')">

Conditional expressions

html
<tr th:class="${row.even}? 'even' : 'odd'">
  ...
</tr>

<tr th:class="${row.even}? (${row.first}? 'first' : 'even') : 'odd'">
  ...
</tr>

<tr th:class="${row.even}? 'alt'">
  ...
</tr>

Default expressions (Elvis operator)

html
<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>

<p>
  Name: 
  <span th:text="*{firstName}?: (*{admin}? 'Admin' : #{default.username})">Sebastian</span>
</p>

The No-Operation token

html
<span th:text="${user.name} ?: 'no user authenticated'">...</span>

<span th:text="${user.name} ?: _">no user authenticated</span>

Data Conversion / Formatting

html
<td th:text="${{user.lastAccessDate}}">...</td>

4.15 Preprocessing

  • Messages_fr.properties
properties
article.text=@myapp.translator.Translator@translateToFrench({0})
  • Messages_es.properties
properties
article.text=@myapp.translator.Translator@translateToSpanish({0})
  • Messages.html
html
<p th:text="${__#{article.text('textVar')}__}">Some text here...</p>

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

设置标签属性

设置任意标签属性

  • th:attr
html
<form action="subscribe.html" th:attr="action=@{/subscribe}">
  <fieldset>
    <input type="text" name="email" />
    <input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
  </fieldset>
</form>
html
<input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
  • th:attr
html
<img src="../../images/gtvglogo.png" 
     th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
  • th:value
html
<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>
  • th:action
html
<form action="subscribe.html" th:action="@{/subscribe}">
  • th:href
html
<li><a href="product/list.html" th:href="@{/product/list}">Product List</a></li>
  • 类似的语法还有很多
th:abbrth:acceptth:accept-charset
th:accesskeyth:actionth:align
th:altth:archiveth:audio
th:autocompleteth:axisth:background
th:bgcolorth:borderth:cellpadding
th:cellspacingth:challengeth:charset
th:citeth:classth:classid
th:codebaseth:codetypeth:cols
th:colspanth:compactth:content
th:contenteditableth:contextmenuth:data
th:datetimeth:dirth:draggable
th:dropzoneth:enctypeth:for
th:formth:formactionth:formenctype
th:formmethodth:formtargetth:fragment
th:frameth:frameborderth:headers
th:heightth:highth:href
th:hreflangth:hspaceth:http-equiv
th:iconth:idth:inline
th:keytypeth:kindth:label
th:langth:listth:longdesc
th:lowth:manifestth:marginheight
th:marginwidthth:maxth:maxlength
th:mediath:methodth:min
th:nameth:onabortth:onafterprint
th:onbeforeprintth:onbeforeunloadth:onblur
th:oncanplayth:oncanplaythroughth:onchange
th:onclickth:oncontextmenuth:ondblclick
th:ondragth:ondragendth:ondragenter
th:ondragleaveth:ondragoverth:ondragstart
th:ondropth:ondurationchangeth:onemptied
th:onendedth:onerrorth:onfocus
th:onformchangeth:onforminputth:onhashchange
th:oninputth:oninvalidth:onkeydown
th:onkeypressth:onkeyupth:onload
th:onloadeddatath:onloadedmetadatath:onloadstart
th:onmessageth:onmousedownth:onmousemove
th:onmouseoutth:onmouseoverth:onmouseup
th:onmousewheelth:onofflineth:ononline
th:onpauseth:onplayth:onplaying
th:onpopstateth:onprogressth:onratechange
th:onreadystatechangeth:onredoth:onreset
th:onresizeth:onscrollth:onseeked
th:onseekingth:onselectth:onshow
th:onstalledth:onstorageth:onsubmit
th:onsuspendth:ontimeupdateth:onundo
th:onunloadth:onvolumechangeth:onwaiting
th:optimumth:patternth:placeholder
th:posterth:preloadth:radiogroup
th:relth:revth:rows
th:rowspanth:rulesth:sandbox
th:schemeth:scopeth:scrolling
th:sizeth:sizesth:span
th:spellcheckth:srcth:srclang
th:standbyth:startth:step
th:styleth:summaryth:tabindex
th:targetth:titleth:type
th:usemapth:valueth:valuetype
th:vspaceth:widthth:wrap
th:xmlbaseth:xmllangth:xmlspace

Appending and prepending

  • th:attrappend
  • th:attrprepend
html
<input type="button" value="Do it!" class="btn" th:attrappend="class=${' ' + cssStyle}" />

<!-- If you process this template with the cssStyle variable set to "warning", you will get: -->
<!-- <input type="button" value="Do it!" class="btn warning" /> -->
  • th:classappend
  • th:styleappend
html
<tr th:each="prod : ${prods}" class="row" th:classappend="${prodStat.odd}? 'odd'">

Fixed-value boolean attributes

  • th:checked
html
<input type="checkbox" name="active" th:checked="${user.active}" />
  • 类似的语法还有很多
th:asyncth:autofocusth:autoplay
th:checkedth:controlsth:declare
th:defaultth:deferth:disabled
th:formnovalidateth:hiddenth:ismap
th:loopth:multipleth:novalidate
th:nowrapth:openth:pubdate
th:readonlyth:requiredth:reversed
th:scopedth:seamlessth:selected

循环

html
<ul th:if="${condition}">
  <li th:each="u : ${users}" th:text="${u.name}">user name</li>
</ul>

<!-- 1, 21, 41, 61, 81, 101, 121, 141, 161, 181 -->
<option th:each="x : ${#numbers.sequence(1, 200, 20)}">
    <p th:text="${ x }"></p>
</option>

<!-- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -->
<option th:each="x : ${#numbers.sequence(1, 200/20)}">
    <p th:text="${ x }"></p>
</option>

条件判断

th:if

html
<a href="comments.html"
   th:href="@{/product/comments(prodId=${prod.id})}" 
   th:if="${not #lists.isEmpty(prod.comments)}">view</a>

请注意,th:if属性不仅计算布尔条件。它的能力远远不止于此,它将按照以下规则将指定的表达式评估为真:

  • If value is not null:
  • If value is a boolean and is true.
  • If value is a number and is non-zero
  • If value is a character and is non-zero
  • If value is a String and is not “false”, “off” or “no”
  • If value is not a boolean, a number, a character or a String.
  • (If value is null, th:if will evaluate to false).

th:unless

th:if的逆属性

html
<a href="comments.html"
   th:href="@{/comments(prodId=${prod.id})}" 
   th:unless="${#lists.isEmpty(prod.comments)}">view</a>

switch

html
<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>

SpringBoot整合Thymeleaf

导入依赖

  • pom.xml
xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

配置

注意,默认情况下,Thymeleaf的模板文件,会到resources/templates

yml
spring:
  thymeleaf:
    cache: false
    encoding: UTF-8
    #prefix: classpath:/static/
    #suffix: .html
    mode: HTML