Spring Boot 2.0 整合 Thymeleaf 模块引擎

本文首发于:https://y0ngb1n.github.io/a/567589f9.html

开发环境

<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.1.0.RELEASE</version>
</parent>

<properties>
  <java.version>1.8</java.version>
</properties>

引入依赖

主要增加 spring-boot-starter-thymeleaf 依赖:

  • spring-boot-starter-thymeleaf:自动装配 Thymeleaf 模板引擎
<dependencies>
  ...

  <!-- Thymeleaf Start -->
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
  </dependency>
  <!-- Thymeleaf End -->
  
  ...
</dependencies>

配置 Thymeleaf

application.yml

spring:
  thymeleaf:
    cache: false                  # 是否开启模板缓存,默认为:true,开发时关闭缓存,不然没法看到实时页面!
    mode: HTML                    # 指定模板的模式,默认为:HTML
    encoding: UTF-8               # 指定模板的编码,默认为:UTF-8
    prefix: classpath:/templates/ # 指定模板的前缀,默认为:classpath:/templates/
    suffix: .html                 # 指定模板的后缀,默认为:.html
    servlet:
      content-type: text/html     # 指定 Content-Type 值,默认为:text/html

org.thymeleaf.templatemode.TemplateMode 中可见 Thymeleaf3.0.0 版本开始使用 HTML 替代 HTML5、LEGACYHTML5、XHTML、VALIDXHTML。如果还在使用 3.0.0 以前的版本,想要使用非严格的 HTML,需要做以下配置:

  • pom.xml 中引入 nekohtml 依赖
  • application.yml 中配置 spring.thymeleaf.mode=LEGACYHTML5

更多属性配置请参考「Appendix A. Common application properties」中 # THYMELEAF (ThymeleafAutoConfiguration) 模块的属性介绍。(TIPS:使用 CTRL + F 进行快速定位)

创建测试 Controller

创建一个 Controller,为 message 属性赋值并设置跳转,代码如下:

IndexController.java

@Controller
public class IndexController {

  @GetMapping(path = {"/", "index"})
  public String indexPage(Model model) {
    model.addAttribute("message", "Hello Thymeleaf!");
    return "index";
  }
}

创建测试 HTML 页面

templates 目录下创建 index.html 文件,并在 html 标签中声明 Thymeleaf 命名空间 xmlns:th="http://www.thymeleaf.org",代码如下:

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="UTF-8"/>
    <title>Thymeleaf</title>
  </head>
  <body>
    <h1 th:text="${message}">Hello World!</h1>
  </body>
</html>

其中关键的代码是:

xmlns:th="http://www.thymeleaf.org"

主要是让 IDE 识别 Thymeleaf 命名空间,这样在标签里输入 th: 后,IDE 会提示相应的语法,方便开发!不加入这句代码也不会影响 Thymeleaf 模板引擎的渲染,以及页面的正常显示。

测试访问

启动成功后,访问 http://127.0.0.1:8080,即可看到效果:

Hello Thymeleaf

访问结果:Hello Thymeleaf!


Thymeleaf 常用语法

获取变量值

<p th:text="'Hello! ' + ${name} + '!'" >name</p>

可以看出获取变量值用 $ 符号,对于 JavaBean 的话使用 变量名.属性名 方式获取,这点和 EL 表达式一样。

另外 $ 表达式只能写在 th 标签内部,不然不会生效,上面例子就是使用 th:text 标签的值替换 <p>...</p> 标签里面的值,至于 p 里面的原有的值只是为了给前端开发时做展示用的。这样的话很好的做到了前后端分离。

内容信息输出:th:textth:utext

  • th:text:以纯文本的方式输出
  • th:utext:以 HTML 标签的方式输出,浏览器能正常渲染
`th:text` 与 `th:utext`

HTML 代码:

<body>
  <h2 th:text="' th:text &nbsp » ' + ${content}">以纯文本的方式输出</h2>
  <h2 th:utext="'th:utext      » ' + ${content}">以 HTML 标签的方式输出,浏览器能正常渲染</h2>
</body>

JAVA 代码:

@GetMapping("/text-utext")
public String textAndutext(Model model) {
  model.addAttribute("content", "<span style='color:red'>thymeleaf text output</span>");
  return "text-utext";
}

引用 URL

对于 URL 的处理是通过语法 @{…} 来处理的:

引用 URL

HTML 代码:

<body>
  <ul>
    <li>
      <a th:href="@{https://github.com/{username}(username=${username})}">绝对路径 1</a>,
      <a th:href="@{https://www.baidu.com}">绝对路径 2</a>
    </li>
    <li>
      <a th:href="@{/}">相对路径</a>
    </li>
    <li>
      <a th:href="@{/css/app.css}">Content 路径,默认访问 static 下的 CSS 文件</a>
    </li>
  </ul>
</body>

JAVA 代码:

@GetMapping("/refer-url")
public String referUrl(Model model) {
  model.addAttribute("username", "y0ngb1n");
  return "refer-url";
}

类似的标签有:th:hrefth:src

字符串替换

很多时候可能我们只需要对一大段文字中的某一处地方进行替换,可以通过字符串拼接操作完成:

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

可以用另一种更简洁的方式:

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

文字替换本身可以和与其他表达式联合使用:

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

当然这种形式限制比较多,|…| 中只能包含变量表达式 ${…},不能包含其他常量、条件表达式等。

字符串替换

HTML 代码:

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

JAVA 代码:

@GetMapping("replace-text")
public String replaceText(Model model) {
  model.addAttribute("user", user);
  model.addAttribute("onevar", "one");
  model.addAttribute("twovar", "two");
  model.addAttribute("threevar", "three");
  return "replace-text";
}

运算符

在表达式中可以使用各类算术运算符,例如 +, -, *, /, %

th:with="isEven=(${user.age} % 2 == 0)"

逻辑运算符 >, <, <=, >=, ==, != 都可以使用,唯一需要注意的是使用 <, > 时需要用它的 HTML 转义符:

th:if="${user.age} &gt; 1"
th:text="'Environment is ' + ((${env} == 'dev') ? 'Development' : 'Production')"
运算符

HTML 代码:

<body>
  <h2 th:text="|name: ${user.name}, age: ${user.age}, env: ${env}|"></h2>

  <p th:with="isEven=(${user.age} % 2 == 0)">年龄为偶数</p>
  <p th:with="isEven=(${user.age == 18})">哟,才 18 呐!</p>

  <p th:if="${user.age}  &gt; 18">当前年龄大于 18</p>

  <div th:class="${env} == 'dev' ? 'dev' : 'prod'"></div>

  <p th:text="'当前环境:' + ((${env} == 'dev') ? 'Development' : 'Production')"></p>
</body>

JAVA 代码:

@GetMapping("/operator")
public String operator(Model model) {
  model.addAttribute("user", user);
  model.addAttribute("env", "dev");
  return "operator";
}

条件判断

th:if, th:unless

使用 th:ifth:unless 属性进行条件判断,下面的例子中,标签只有在 th:if 中条件成立时才显示:

<a th:href="@{/login}" th:if=${user == null}>Login</a>
<a th:href="@{/login}" th:unless=${user != null}>Login</a>

th:unlessth:if 恰好相反,只有表达式中的条件不成立,才会显示其内容。

th:switch, th:case

支持多路选择 Switch 结构:

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

默认属性 default 可以用 * 表示:

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

消息表达式:#{...},也称为文本外部化、国际化或 i18n

条件判断

HTML 代码:

<body>
  <a th:href="@{/login}" th:unless="${user == null}">登录</a>
  <p th:if="${user != null}">欢迎,<span th:text="|${user.name}(role: ${user.role})|">tony</span></p>

  <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>
</body>

JAVA 代码:

@GetMapping("/condition")
public String condition(Model model) {
  model.addAttribute("user", user);
  return "condition";
}

循环

渲染列表数据是一种非常常见的场景,例如现在有 n 条记录需要渲染成一个表格,该数据集合必须是可以遍历的,使用 th:each 标签:

HTML 代码:

<body>
  <table>
    <tr>
      <th>NAME</th>
      <th>AGE</th>
      <th>ADMIN</th>
    </tr>
    <tr th:each="user : ${users}">
      <td th:text="${user.name}">Onions</td>
      <td th:text="${user.age}">22</td>
      <td th:text="${user.role} == 'admin' ? #{true} : #{false}">yes</td>
    </tr>
  </table>
</body>

可以看到,需要在被循环渲染的元素(这里是)中加入 th:each 标签,其中 th:each="prod : ${prods}" 意味着对集合变量 prods 进行遍历,循环变量是 prod 在循环体中可以通过表达式访问。

JAVA 代码:

@GetMapping("/loop")
public String loop(Model model) {
  List<User> users = new ArrayList<>(3);
  users.add(user);
  users.add(User.builder().name("tony").age(23).role("user").build());
  users.add(User.builder().name("tom").age(21).role("user").build());

  model.addAttribute("users", users);
  return "loop";
}
循环

更多标签用法请参考「Thymeleaf 常用语法」、「Thymeleaf 参考手册」解锁更多技巧 🤪


参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容