SpringBoot2.x 集成 Thymeleaf的详细教程

一、Thymeleaf简介

Thymeleaf是面向Web和独立环境的现代服务器Java模板引擎,能够处理HTML,XML,JavaScript,CSS甚至纯文本。

Thymeleaf旨在提供一个优雅的、高度可维护的创建模板的方式。为了实现这一目标,Thymeleaf建立在自然模板的概念上,将其逻辑注入到模板文件中,不会影响模板设计原型。这改善了设计的沟通,弥合了设计和开发团队之间的差距。

Thymeleaf从设计之初就遵循Web标准——特别是HTML5标准,如果需要,Thymeleaf允许创建完全符合HTML5验证标准的模板。

二、集成Thymeleaf

通过Maven新建一个名为springboot-thymeleaf的项目。

1.引入依赖


    org.springframework.boot
    spring-boot-starter-web



    org.springframework.boot
    spring-boot-starter-thymeleaf



    org.projectlombok
    lombok
    1.18.8

2.编写配置文件

spring:
  thymeleaf:
    # 在构建URL时用于查看名称的前缀,默认为classpath:/templates/
    prefix: classpath:/templates/
    # 在构建URL时附加到视图名称的后缀,默认为.html
    suffix: .html
    # 模板模式,默认为HTML
    mode: HTML
    # 模板文件编码,默认为UTF-8
    encoding: UTF-8
    # 是否启用模板缓存,默认为true,表示启用,false不启用
    cache: false
    # 在呈现模板之前是否检查模板存在与否,默认为true
    check-template: true
    # 是否检查模板位置存在与否,默认为true
    check-template-location: true

更多的配置可以查看ThymeleafProperties类:

SpringBoot2.x 集成 Thymeleaf的详细教程_第1张图片

3.准备模板

首先按照配置文件中配置的模板前缀在resources下创建一个templates目录,用于存放模板。然后创建如下名为hello.html的模板:




    
    Hello Thymeleaf


    
th:text文本替换会转义html标签,不解析html

th:utext文本替换不会转义html标签,会解析html

标签中的xmlns:th="http://www.thymeleaf.org声明使用Thymeleaf标签。th:text属性会计算表达式的值将结果设置为标签的标签体。但它会转义HTML标签,HTML标签会直接显示在浏览器上。th:utext属性不会转义HTML标签,HTML标签会被浏览器解析。${hello}是一个变量表达式,它包含一个OGNL(Object-Graph Navigation Language)的表达式,它会从上下文中获取名为hello的变量,然后在模板上进行渲染。

4.Controller层

创建Controller并将模板中要获取的变量设置到Model对象中,如果Controller类上使用的是@Controller注解,则可以返回跟模板名称相同的字符串(不包括前缀和后缀),视图解析器会解析出视图具体地址并生成视图,然后返回给前端控制器进行渲染:

package com.rtxtitanv.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.controller.ThymeleafController
 * @description ThymeleafController
 * @date 2021/7/3 19:23
 */
@Controller
public class ThymeleafController {

    @GetMapping("/hello")
    public String hello(Model model) {
        model.addAttribute("hello", "

Hello Thymeleaf

"); return "hello"; } }

运行项目,浏览器访问http://localhost:8080/hello,发现数据成功渲染到模板:

2

如果Controller类上使用的是@RestController注解,则需要将视图添加到ModelAndView对象中并返回:

package com.rtxtitanv.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.controller.TestController
 * @description TestController
 * @date 2021/7/3 19:43
 */
@RequestMapping("/test")
@RestController
public class TestController {

    @GetMapping("/hello")
    public ModelAndView hello() {
        ModelAndView modelAndView = new ModelAndView("hello");
        modelAndView.addObject("hello", "

hello thymeleaf

"); return modelAndView; } }

运行项目,浏览器访问http://localhost:8080/test/hello,发现数据成功渲染到模板:

3

三、Thymeleaf常用语法

1.标准表达式

(1)变量表达式

${}表达式实际上是在上下⽂中包含的变量的映射上执行的OGNL(Object-Graph Navigation Language)对象。例如:${session.user.name}

在Spring MVC启用的应用程序中,OGNL将被替换为SpringEL,但其语法与OGNL相似(实际上,在大多数常见情况下完全相同)。

模板variable.html如下:




    
    变量表达式


    

变量表达式:${}

id:

username:

password:

User实体类:

package com.rtxtitanv.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.model.User
 * @description User
 * @date 2021/7/3 19:37
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
    private Long id;
    private String username;
    private String password;
}

ThymeleafController中新增以下方法:

@GetMapping("/variable")
public String variable(Model model) {
    model.addAttribute("user", new User(1L, "赵云", "qwe123"));
    return "variable";
}

效果:

4

${}表达式中还可以使用基本对象和工具类对象。这些对象都以#开头。基本对象:

  • #ctx:上下文对象。
  • #vars:上下文变量。
  • #locale:上下文区域设置。
  • #request:(仅在Web Contexts中)HttpServletRequest对象。
  • #response:(仅在Web上下⽂中)HttpServletResponse对象。
  • #session:(仅在Web上下⽂中)HttpSession对象。
  • #servletContext:(仅在Web上下⽂中)ServletContext对象。

工具类对象:

  • #execInfo:有关正在处理的模板的信息。
  • #messages:用于在变量表达式中获取外部化消息的方法,与使用#{}语法获取的方式相同。
  • #uris:转义URL/URI部分的方法。
  • #conversions:执行配置的转换服务的方法。
  • #datesjava.util.Date对象的方法。
  • #calendars:类似于#dates,但对应java.util.Calendar对象。
  • #numbers:用于格式化数字对象的方法。
  • #strings:字符串对象的方法。
  • #objects:一般对象的方法。
  • #bools:布尔相关方法。
  • #arrays:Array的方法。
  • #lists:List的方法。
  • #sets:Set的方法。
  • #maps:Map的方法。
  • #aggregates:在数组或集合上创建聚合的方法。
  • #ids:处理可能重复的id属性的方法。

这些对象的详细方法可以查看官方文档:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#appendix-a-expression-basic-objects。

(2)选择表达式(星号语法)

星号语法*{}计算所选对象而不是整个上下文的表达式。也就是说,只要没有选定的对象(选定对象为th:object属性的表达式结果),$*语法就会完全相同。

模板asterisk.html如下:




    
    选择表达式(星号语法)


    

*语法

id:

username:

password:

$语法

id:

username:

password:

$和*混合使用

id:

username:

password:

对象选择到位时所选对象将作为#object表达式变量可⽤于$表达式

id:

username:

password:

没有执⾏对象选择时$和*语法等效

id:

username:

password:

ThymeleafController中新增以下方法:

@GetMapping("/asterisk")
public String star(Model model) {
    model.addAttribute("user", new User(1L, "赵云", "qwe123"));
    return "asterisk";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第2张图片

(3)URL表达式

URL表达式的语法为@{}。使用th:href属性可以对链接进行渲染。

模板url.html如下:

注意:

th:href属性会计算要使用的URL并将该值设置为 标签的 href属性。URL中允许使用表达式的URL参数,设置多个参数将以 ,分隔。
URL路径中也可以使用变量模板,即可以设置rest风格的参数。 /开头的相对URL将自动以应用上下文名称为前缀。如果cookie未启用或还未知道,可能会在相对URL中添加 ;jsessionid=...后缀以便保留会话。这被称为URL重写。
th:href属性允许在模板中有一个静态的 href属性,这样在直接打开原型模板时,模板链接可以被导航。

ThymeleafController中新增以下方法:

@GetMapping("/url")
public String url(Model model) {
    model.addAttribute("user", new User(1L, "赵云", "qwe123"));
    model.addAttribute("url", "/user/update");
    return "url";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第3张图片

从上到下依次点击5个链接和提交表单的结果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第4张图片

(4)字面量

字面量(Literals)分为:

  • 文本文字(Text literals):包含在单引号之间的字符串,可以包含任何字符,其中的单引号需要\'转义。
  • 数字字面量(Number literals):数字。
  • 布尔字面量(Boolean literals):包含true和false。
  • null字面量(The null literal):null。
  • 文本符号(Literal tokens):数字,布尔和null字面量实际是文本符号的特殊情况,文本符号不需要引号包围,只允许使用字母(A-Z和a-z)、数字(0-9)、括号、点(.)、连字符(-)和下划线(_)。

模板literal.html如下:




    
    字面量


    
  • 文本文字:⽂本文字只是包含在单引号之间的字符串,可以包含任何字符,其中的单引号需要\'转义

  • 数字字面量:数字字面量就是数字

  • 布尔字面量:布尔字⾯量包含true和false

  • null字面量:Thymeleaf标准表达式语法中也可以使⽤null字⾯量

  • 文本符号:数字,布尔和null字面量实际上是⽂本符号的特殊情况

ThymeleafController中新增以下方法:

@GetMapping("/literal")
public String literal(Model model) {
    model.addAttribute("flag", false);
    return "literal";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第5张图片

(5)文本操作

这里的文本操作包含:

  • 追加文本(Appending texts),即字符串连接:无论是文本常量还是表达式结果都可以使用+运算符进行拼接。
  • 字面替换(Literal substitutions):字面替换可以轻松地对包含变量值的字符串进行格式化,而不需要在字面后加+,这些替换必需用|包围。使用字面替换也可以实现和追加文本相同的效果。

模板text.html如下:




    
    文本操作(字符串连接)


    
  • 方式1(+):

  • 方式2(|):

  • 方式3(+与|混合):

  • 方式4(#strings.concat):

  • 方式5(#strings.append):

  • 方式6(#strings.prepend):

  • 方式7(#strings.arrayJoin):

ThymeleafController中新增以下方法:

@GetMapping("/text")
public String text(Model model) {
    model.addAttribute("user", new User(1L, "赵云", "qwe123"));
    return "text";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第6张图片

(6)运算符

运算符:

  • 算数运算符:+-*/div)、%mod)。
  • 比较运算符:>gt)、<lt)、>=ge)、<=le)。
  • 等值运算符:==eq)、!=ne)。
  • 布尔运算符:andor!not)。

模板operation.html如下:




    
    运算符


    

算术运算符:+、-、*、/(div)、%(mod)

  • (y + x % 3) * (y - x / 2) :

  • (x * 5 + 8 div y) mod (x - y) :

比较和等值运算符:>(gt)、<(lt)、>=(ge)、<=(le)、==(eq)、!=(ne)

  • x > 5:

  • y le 2:

  • (x * y) < 50:

  • y ge x:

  • x == y:

  • x ne y:

布尔运算符:and、or、!(not)

  • y lt x and -x gt -y:

  • -x <= -y or y >= x:

  • !(x != y):

  • not (x eq y):

ThymeleafController中新增以下方法:

@GetMapping("/operation")
public String operator(Model model) {
    model.addAttribute("x", 10);
    model.addAttribute("y", 3);
    return "operation";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第7张图片

(7)条件表达式

条件表达式:

  • If-then:(if) ? (then)if表达式结果为true,则条件表达式结果为then表达式结果,否则为null。
  • If-then-else:(if) ? (then) : (else)if表达式结果为true,则条件表达式结果为then表达式结果,否则为else表达式结果。
  • Default(默认表达式):(value) ?: (defaultvalue)value不为null,则结果为value,否则结果为defaultvalue

模板conditional-expr.html如下:




    
    条件表达式、默认表达式、_符号


    

  • 成绩是否及格:

  • 成绩是否是正常数据:

  • 成绩对应的级别:

  • 年龄:

  • 默认表达式嵌套示例:

  • 年龄:没有年龄数据

ThymeleafController中新增以下方法:

@GetMapping("/conditional/expr")
public String conditionExpr(Model model) {
    model.addAttribute("grade", 85);
    model.addAttribute("age", null);
    return "conditional-expr";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第8张图片

grade设置为-1,age设置为20。效果如下:

SpringBoot2.x 集成 Thymeleaf的详细教程_第9张图片

2.设置属性

使用th:attr属性可以设置标签的任何属性值,th:attr只需要通过一个表达式将值赋给对应的属性,并且还可以通过,分隔的形式设置多个属性值。不过使用th:attr设置属性不太优雅,所以用的不多,一般使用其他th:*属性的形式设置指定属性,例如要设置value属性,可以使用th:value,要设置action属性,可以使用th:action,要设置href属性,可以使用th:href,具体都有哪些属性可以这样设置可以参考官方文档。

th:attrprependth:attrappend可以给属性设置前缀和后缀。Thymeleaf标准方言中还有两个特定的附加属性th:classappendth:styleappend,用于将CSS的class或style样式追加到元素中,而不覆盖现有属性。

HTML中有布尔属性这个概念,布尔属性没有值,并且一旦这个布尔属性存在则意味着属性值为true。但是在XHTML中,这些属性只取它本身作为属性值。例如checked属性。Thymeleaf标准方言允许通过计算条件表达式的结果来设置这些属性的值,如果条件表达式结果为true,则该属性将被设置为其固定值,如果评估为false,则不会设置该属性。

Thymeleaf支持使用HTML5自定义属性语法data-{prefix}-{name}来处理模板,这不需要使用任何命名空间。Thymeleaf使这种语法自动适用于所有的方言(不仅仅是标准的方法)。

模板attr.html如下:




    
    设置属性


    

设置单个任何属性:

设置多个任何属性:

设置单个指定属性:

设置多个指定属性:

设置属性后缀:

设置属性前缀:

追加class样式:

追加style样式:

设置布尔属性checked:

使用HTML5自定义属性语法来处理模版:

ThymeleafController中新增以下方法:

@GetMapping("/attr")
public String attr(Model model) {
    model.addAttribute("success", "成功");
    model.addAttribute("active", true);
    return "attr";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第10张图片

f12查看源码可见属性设置成功:

SpringBoot2.x 集成 Thymeleaf的详细教程_第11张图片

active设置为false。checkbox没有选中,查看源码也没有设置checked属性:

15

3.条件判断

th:if属性可以通过判断一个条件是否满足,来决定是否将模板片段显示在结果中,只有满足条件才将模板片段显示在结果中。而th:unless属性则正好与th:if属性相反。通过th:switchth:case可以在模板中使用一种与Java中的Swicth语句等效的结构有条件地显示模板内容。

模板conditional.html如下:




    
    条件判断


    

年龄大于18并且用户等级等于6,则显示此元素
与if条件判断相反,年龄小于18或用户等级不等于6,则显示此元素
表达式的值不为null,th:if判定此表达式的值为true
表达式的值为null,th:if判定此表达式的值为false
值是数字并且不为0,判定此表达式的值为true
值是数字但为0,判定此表达式的值为false
值是一个字符并且不为0,判定此表达式的值为true
值是一个字符串,不是false,off或no,判定此表达式的值为true
值是字符串false,判定此表达式的值为false
值是字符串off,判定此表达式的值为false
值是字符串no,判定此表达式的值为false

青铜 白银 黄金 铂金 钻石 王者 无段位

注意:

th:if属性不仅仅以布尔值作为判断条件。它将按照以下规则判定指定的表达式值为true:

如果表达式的值不为null。(如果表达式的值为null, th:if判定此表达式的值为false。)
如果值为布尔值,则为true。如果值是数字且不为0。
如果值是一个字符且不为0。如果值是一个字符串,不是"false",“off"或"no”。
如果值不是布尔值,数字,字符或字符串。

同一个Switch语句中只要第一个th:case的值为true,则其他的th:case属性将被视为false。Switch语句的default选项指定为th:case=“*”

ThymeleafController中新增以下方法:

@GetMapping("/conditional")
public String condition(Model model) {
    Map map = new HashMap<>();
    map.put("age", 10);
    map.put("userLevel", 6);
    map.put("rank", 5);
    model.addAllAttributes(map);
    return "conditional";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第12张图片

age设置为20,userLevel设置为6,rank设置为0。效果如下:

SpringBoot2.x 集成 Thymeleaf的详细教程_第13张图片

4.循环迭代

使用th:each属性可以迭代以下对象:

  • 任何实现java.util.Iterable接口的对象。
  • 任何实现java.util.Enumeration接口的对象。
  • 任何实现java.util.Iterator接口的对象。其值将被迭代器返回,不需要在内存中缓存所有值。
  • 任何实现java.util.Map接口的对象。迭代map时,迭代变量将是java.util.Map.Entry类型。
  • 任何数组。
  • 任何其将被视为包含对象本身的单值列表。

(1)迭代List

模板each-list.html如下:




    
    循环迭代List


    
id username password index count size current even odd first last

状态变量是在使用th:each时Thymeleaf提供的一种用于跟踪迭代状态的机制。状态变量在th:each属性中通过在迭代变量之后直接写其名称来定义,用,分隔。与迭代变量一样,状态变量的作用范围也是th:each属性所在标签定义的代码片段中。如果没有显式地设置状态变量,Thymeleaf总是会创建一个名为迭代变量名加上Stat后缀的状态变量。

ThymeleafController中新增以下方法:

@GetMapping("/each/list")
public String eachList(ModelMap model) {
    List users = new ArrayList<>();
    users.add(new User(1L, "刘备", "123132"));
    users.add(new User(2L, "关羽", "321231"));
    users.add(new User(3L, "张飞", "213312"));
    model.addAttribute("users", users);
    return "each-list";
}

效果:

18

(2)迭代Map

模板each-map.html如下:




    
    循环迭代Map


    
id username password key index count size current even odd first last

id username password key index count size current even odd first last

ThymeleafController中新增以下方法:

@GetMapping("/each/map")
public String eachMap(Model model) {
    Map map = new HashMap<>(16);
    map.put("user1", new User(1L, "刘备", "123132"));
    map.put("user2", new User(2L, "关羽", "321231"));
    map.put("user3", new User(3L, "张飞", "213312"));
    model.addAttribute("userMap", map);
    return "each-map";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第14张图片

(3)迭代Array

模板each-array.html如下:




    
    循环迭代Array


    
id username password index count size current even odd first last

ThymeleafController中新增以下方法:

@GetMapping("/each/array")
public String eachArray(Model model) {
    User[] users = {new User(1L, "刘备", "123132"), new User(2L, "关羽", "321231"), new User(3L, "张飞", "213312")};
    model.addAttribute("users", users);
    return "each-array";
}

效果:

20

5.模板布局

(1)引用模板片段

th:fragment属性可以用来定义模板片段,th:insertth:replaceth:include(Thymeleaf3.0不再推荐使用)属性可以引用模板片段。

首先创建一个名为footer.html的模板文件用来包含模板片段,然后定义一个名为copy1的片段:




    
    footer


    
© 2021

然后在名为layout.html的模板中引用该片段:




    
    引用模板片段


    

ThymeleafController中新增以下方法:

@GetMapping("/layout")
public String layout(Model model) {
    return "layout";
}

效果:

21

引用模板片段语法中的~{}是可选的,以下代码与之前的等价:

效果:

22

th:insertth:replaceth:include都能引用模板片段,直接在页面中还看不出三者的区别。为了更明显地看出区别,在footer.html模板中新增以下片段:

© 2020-2023

然后在layout.html模板中分别使用th:insertth:replaceth:include进行引用:

th:insert将指定片段插⼊到指定宿主标签的标签体中
th:replace实际上⽤指定的⽚段替换其宿主标签
th:include只插⼊此⽚段的内容到指定宿主标签的标签体中

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第15张图片

按F12查看源码可以看出区别:

SpringBoot2.x 集成 Thymeleaf的详细教程_第16张图片

所以三者区别为:

  • th:insert将指定片段插入到指定宿主标签的标签体中。
  • th:replace实际上用指定的片段替换其宿主标签。
  • th:include只插入此片段的内容到指定宿主标签的标签体中。

引用模板片段的规范语法有以下三种格式:

  • ~{templatename::selector}:包含在名为templatename的模板上通过指定的selector匹配的片段。selector可以只是一个片段名称。
  • ~{templatename}:包含名为templatename的整个模板。
  • ~{::selector}~{this::selector}:包含在同一模板中与指定selector匹配的片段。如果在表达式出现的模板上没有找到,模板调用(插入)的堆栈会向最初处理的模板(root)遍历,直到selector在某个层次上匹配。

templatename和selector都可以是表达式。模板片段中可以包含任何th:*属性,一旦在目标模板中引用了片段,这些属性将被计算并且它们能够引用目标模板中定义的任何上下文变量。

通过th:replace="~{footer}"可以引用整个footer.html

引用名为footer的整个模板

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第17张图片

layout.html模板中新增以下几个片段:

~{:: selector}或~{this :: selector}包含在同⼀模板中的匹配指定选择器的⽚段

文本输入框:

layout.html模板中引用frag和input片段:

引用当前模板中的片段
引用当前模板中的片段

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第18张图片

selector写成一个条件表达式,可以通过条件来决定引用的片段:

模板名和选择器都可以是表达式

将变量flag设置到Model中:

model.addAttribute("flag", true);

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第19张图片

flag设置为fasle时的效果:

28

由于标签选择器的强大功能,没有使用th:fragment属性的片段可以通过id属性来引用。在footer.html模板中新增以下片段:

没有使用th:fragment属性的片段可以通过id属性来引用

layout.html模板中引用该片段:

可以通过id属性来引⽤没有th:fragment属性的⽚段

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第20张图片

(2)参数化的模板片段

th:fragment定义的片段可以指定一组参数。在footer.html模板中新增以下片段:

th:fragment定义的⽚段可以指定⼀组参数

layout.html模板中分别用以下两种语法引用该片段:


引用定义了参数的模板片段语法1
引用定义了参数的模板片段语法2,可以改变参数顺序

将变量var1var2设置到Model中:

model.addAttribute("var1", "参数1");
model.addAttribute("var2", "参数2");

效果:

30

即使片段没有定义参数,也可以调用片段中的局部变量。在footer.html模板中新增以下片段:

没有定义参数的模板片段

layout.html模板中分别通过以下语法调用片段中的局部变量:


没有定义参数的模板片段中的局部变量的调用语法1
没有定义参数的模板片段中的局部变量的调用语法2

引用定义参数的模板片段的两种语法中,第一种语法不能调用没有定义参数的片段中的局部变量,只有第二种语法能调用。

注意:片段的局部变量规范 - 无论是否具有参数签名 - 都不会导致上下文在执行前被清空。片段仍然能访问调用模板中正在使用的每个上下文变量。

效果:

31

使用th:assert可以进行模板内部断言,它可以指定逗号分隔的表达式列表,如果每个表达式的结果都为true,则正确执行,否则引发异常。在footer.html模板中新增以下片段:

⽤th:assert进⾏模版内部断⾔

layout.html模板中引用该片段:


将变量user设置到Model中:

model.addAttribute("user", new User(1L, "赵云", "qwe123"));

效果:
32
只将id设置为null,访问模板出现以下异常:

33

只将username设置为空,访问模板出现以下异常:

34

只将password设置为空,访问模板出现以下异常:

35

(3)灵活布局

通过片段表达式不仅可以指定文本类型、数字类型、对象类型的参数,还可以指定标记片段作为参数。这种方式可以使模板布局变得非常灵活。

下面进行一个页面布局的简单模拟测试,首先在templates下新建一个layout目录,用于存放一些组件模板。

layout/footer.html




    
    footer


    

模板布局页面页脚

layout/header.html




    
    header


    

模板布局页面头部

layout/left.html




    
    left


    

模板布局页面左侧菜单

templates下新建一个基础模板base.html




    
    common title


    

common header

common left

common content

common footer

然后在名为layout-home.html的模板中引用base.html中的片段:




    模板布局主页


    

ThymeleafController中新增以下方法:

@GetMapping("/layout/home")
public String layoutHome(Model model) {
    return "layout-home";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第21张图片

使用特殊片段表达式~{}可以指定没有标记。在layout-home.html中通过以下代码引用base.html中的片段:

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第22张图片

_符号也可以用作片段参数。在layout-home.html中通过以下代码引用base.html中的片段:

_符号导致common_div片段中的th:replace="${footer}"不被执行,从而该div标签使用原型文本。

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第23张图片

~{}_符号可以通过简单优雅的方式实现执行片段的条件插入。在layout-home.html中通过以下代码引用base.html中的片段:

参数中的每一个condition条件可以根据实际业务需求灵活控制。这里为了测试方便,都使用的相同条件。

将变量condition设置到Model中:

model.addAttribute("condition", false);

效果:

39

layout-home.html中通过以下代码引用base.html中的片段:

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第24张图片

条件也可以不在参数中进行判断,还在common_div片段的th:replace属性中进行判断。在layout-home.html中通过以下代码引用base.html中的片段:

base.html中的common_div片段:

Common header

Common left

Common content

Common footer

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第25张图片

6.局部变量

Thymeleaf的局部变量是指定义在模板片段中的变量,该变量的作用域为所在模板片段。th:each中的迭代变量就是一个局部变量,作用域为th:each所在的标签范围,可用于该标签内优先级低于th:each的任何其他th:*属性,可用于该标签的任何子元素。使用th:with属性可以声明局部变量。

local.html模板中使⽤th:with属性声明局部变量:




    
    局部变量


    
    
user1的姓名:

ThymeleafController中新增以下方法:

@GetMapping("/local")
public String local(Model model) {
    User[] users = {new User(1L, "刘备", "123132"), new User(2L, "关羽", "321231"), new User(3L, "张飞", "213312")};
    model.addAttribute("users", users);
    return "local";
}

效果:

42

局部变量只能在声明的标签内使用。在local.html模板中新增以下内容:


user1的姓名:张三

访问模板会报错:

43

可以同时定义多个局部变量。在local.html模板中新增以下内容:


user1的姓名:
user2的姓名:

效果:

44

th:with属性允许重⽤在同⼀属性中定义的变量。在local.html模板中新增以下内容:


当前时间:

效果:

45

7.属性优先级

多个th:*属性在同一个标签中的执行顺序由优先级决定。所有Thymeleaf属性都定义了一个数字优先级,以确定了它们在标签中执行的顺序,数字越小优先级越高:

Order Feature Attributes
1 Fragment inclusion th:insert 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

优先级意味着属性位置发生变化,也会得出相同的结果。

8.注释和块

(1)标准HTML/XML注释

标准HTML/XML注释可以在Thymeleaf模板中的任何地方使用。这些注释中的任何内容都不会被Thymeleaf处理,并将逐字复制到结果中。

这里直接运行项目访问local.html模板,查看模板源码可见HTML注释没有被处理,保持原样:

SpringBoot2.x 集成 Thymeleaf的详细教程_第26张图片

(2)ThymeLeaf解析器级注释

解析器级注释块在Thymeleaf解析时会将之间的所有内容从模板中删除。当此模板静态打开时,这些注释块可用于显示代码。

修改local.html模板的其中一个注释为如下内容:

查看源码可见注释已从模板中删除:

SpringBoot2.x 集成 Thymeleaf的详细教程_第27张图片

(3)Thymeleaf专有注释

Thymeleaf允许定义特殊注释块,在Thymeleaf解析时会将标记删除,但不删除标记之间的内容。当此模板静态打开时,会显示注释标记。

修改local.html模板的其中一个注释为如下内容:

查看源码发现只删除了标记部分,中间的内容保留:

SpringBoot2.x 集成 Thymeleaf的详细教程_第28张图片

(4)th:block标签

th:block标签是Thymeleaf标准方言中唯一的元素处理器。th:block是一个允许模板开发者指定想要的任何属性的属性容器,Thymeleaf会执行这些属性并让这个块消失,但它的内容保留。

模板block.html如下:




    
    th:block


    
            
1 root
root

使用th:block可以轻松地迭代同级标签,例如在

中为每个迭代元素创建多个时使用th:block就很简单。

ThymeleafController中新增以下方法:

@GetMapping("/block")
public String block(Model model) {
    List users = new ArrayList<>();
    users.add(new User(1L, "刘备", "123132"));
    users.add(new User(2L, "关羽", "321231"));
    users.add(new User(3L, "张飞", "213312"));
    model.addAttribute("users", users);
    return "block";
}

效果:

49

查看源码:

SpringBoot2.x 集成 Thymeleaf的详细教程_第29张图片
在和原型注释块结合时很有用:

1 root
root

效果:

51

9.内联 (1)内联表达式

[[]][()]中的表达式为内联表达式,可以直接将表达式写入HTML文本。任何在th:textth:utext属性中使用的表达式都可以出现在[[]][()]中。[[]]等价于th:text,会转义html标签;[()]等价于th:utext,不会转义html标签。

模板inline.html如下:




    
    内联


    

th:text和th:utext

Hello World !

Hello World !

内联表达式

Hello [[${user.username}]] !

Hello [(${user.username})] !

ThymeleafController中新增以下方法:

@GetMapping("/inline")
public String inline(Model model) {
    model.addAttribute("user", new User(1L, "赵云", "this is \"pass\" word"));
    return "inline";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第30张图片

注意,静态打开模板文件时,内联表达式会显示出来,这样就无法将其作为原型设计了。静态打开inline.html模板的效果如下:

SpringBoot2.x 集成 Thymeleaf的详细教程_第31张图片

在有些情况下可能需要禁用内联,比如需要输出[[]]序列时。通过th:inline="none"可以禁用内联。在模板inline.html中新增以下内容:

这是一个二维数组:[[1, 2, 3], [4, 5]]

访问模板时会报如下错误:

54

在刚才的代码中增加禁用内联:

禁用内联

这是一个二维数组:[[1, 2, 3], [4, 5]]

效果:

55

(2)内联JavaScript

使用th:inline="javascript"启用内联JavaScript。

在模板inline.html中新增以下内容:

内联JavaScript

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第32张图片

查看源码,可见输出的字符串进行了转义,是格式正确的JavaScript字符串,因为使用[[]]在输出${user.password}表达式的时候进行了转义:

57

查看Console中打印的日志:

58

注释掉let password = [[${user.password}]];,新增let password = [(${user.password})];。查看源码,由于[()]不会进行转义,可见输出的字符串没有转义,是格式错误的JavaScript代码:

59

查看Console,发现有语法错误:

60

如果通过附加内联表达式的方式来构建脚本的一部分,可能会需要输出未转义的字符串,所以这个功能也很有用。

内联JavaScript可以通过在注释中包含内联表达式作为JavaScript自然模板。下面注释掉let password = [(${user.password})];,新增如下代码:

// 在JavaScript注释中包含(转义)内联表达式,Thymeleaf将忽略在注释之后和分号之前的所有内容
let password = /*[[${user.password}]]*/ "default password";

查看源码:

SpringBoot2.x 集成 Thymeleaf的详细教程_第33张图片


查看Console中打印的日志:
62

发现"default password"确实被忽略了。以静态方式打开模板时变量password的值就为"default password"了:

SpringBoot2.x 集成 Thymeleaf的详细教程_第34张图片
SpringBoot2.x 集成 Thymeleaf的详细教程_第35张图片
65

JavaScript内联表达式结果不限于字符串,还会自动地将Strings、Numbers、Booleans、Arrays、Collections、Maps、Beans(有getter和setter方法)这些对象序列化为JavaScript对象。下面在Controller中新增以下代码:

Map map = new HashMap<>(16);
map.put("user1", new User(1L, "刘备", "123132"));
map.put("user2", new User(2L, "关羽", "321231"));
map.put("user3", new User(3L, "张飞", "213312"));
model.addAttribute("userMap", map);

script标签内新增以下代码,Thymeleaf会将userMap对象转换为JavaScript对象:

// userMap为一个Map对象,Thymeleaf会将其转换为JavaScript对象
let userMap = /*[[${userMap}]]*/ null;
console.log("userMap:", userMap);
// 遍历userMap
for (let key in userMap) {
    console.log(key + ": " + "{id: " + userMap[key].id
        + ", username: " + userMap[key].username + ", password: " + userMap[key].password + "}");
}

查看源码发现Thymeleaf已经将其转换为JavaScript对象:

SpringBoot2.x 集成 Thymeleaf的详细教程_第36张图片

查看Console中打印的日志:

SpringBoot2.x 集成 Thymeleaf的详细教程_第37张图片

(3)内联CSS

使用th:inline="css"启用内联CSS。

在模板inline.html中新增以下内容:

将变量elementaligncolor设置到Model中:

model.addAttribute("element", "h1");
model.addAttribute("align", "center");
model.addAttribute("color", "#2876A7");

访问模板,对齐生效,颜色未生效:

SpringBoot2.x 集成 Thymeleaf的详细教程_第38张图片

查看源码,发现color的值多了一个\

69

这是由于[[]]会进行转义,所以多了一个\。这里不需要对其转义,所以需要使用[()]。将代码修改为color: [(${color})];。访问模板,发现样式都生效了:

SpringBoot2.x 集成 Thymeleaf的详细教程_第39张图片

查看源码,没有多出\了:

71
内联CSS也允许通过在注释中包含内联表达式作为CSS自然模板,使CSS可以静态和动态的工作。将

模板动态打开时的效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第40张图片

查看源码:

73

以静态方式打开的效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第41张图片
查看源码:
75

10.国际化

这里简单模拟一下国际化功能,通过语言切换应用对应语言的国际化配置文件。

(1)创建国际化配置文件

resources/i18n目录下创建如下配置文件。

index.properties

user.register=注册
user.login=登录
index.language=语言
index.language.chinese=中文
index.language.english=英文

index_en_US.properties

user.register=Register
user.login=Login
index.language=Language
index.language.chinese=Chinese
index.language.english=English

index_zh_CN.properties

user.register=注册
user.login=登录
index.language=语言
index.language.chinese=中文
index.language.english=英文

(2)新增配置

application.yml中新增以下配置:

spring:
  messages:
    # 配置消息源的基本名称,默认为messages,这里配置为国际化配置文件的前缀
    basename: i18n.index

(3)创建模板

模板i18n.html如下:




    
    国际化


    
语言 中文 英文

#{}为消息表达式,用于引用消息字符串。而消息字符串通常保存在外部化文本中,消息字符串形式为key=value,通过#{key}可以引用特定的消息。而根据不同的语言从与其对应的外部化文本中获取同一个key的消息,可以实现国际化。

(4)配置国际化解析器

package com.rtxtitanv.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;

/**
 * @author rtxtitanv
 * @version 1.0.0
 * @name com.rtxtitanv.config.WebConfig
 * @description 配置自定义国际化解析器
 * @date 2021/7/6 16:25
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public LocaleResolver localeResolver() {
        return new MyLocaleResolver();
    }

    /**
     * 自定义LocaleResolver
     */
    public static class MyLocaleResolver implements LocaleResolver {
        @Override
        public Locale resolveLocale(HttpServletRequest request) {
            // 接收语言参数
            String lang = request.getParameter("lang");
            // 使用默认语言
            Locale locale = Locale.getDefault();
            // 语言参数不为空就设置为该语言
            if (!StringUtils.isEmptyOrWhitespace(lang)) {
                String[] s = lang.split("_");
                locale = new Locale(s[0], s[1]);
            }
            return locale;
        }

        @Override
        public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {}

    }
}

(5)Controller

@GetMapping(value = {"/index", "/"})
public String index() {
    return "i18n";
}

(5)测试

访问模板,默认语言为中文:

76

点击英文链接切换语言为英文:

77
点击中文链接切换语言为中文:

78

11.常用工具类对象

之前在文中已经简单地总结了Thymeleaf中的工具类对象和它的作用。这里结合实例来总结一下常用工具类对象具体方法的使用,由于工具类对象较多,这里就总结Dates、Numbers和Strings的具体使用方法。至于其他对象的方法,可以参考官方文档。

(1)Dates

模板date.html如下:




    
    工具类对象Dates


    
格式化日期格式1(format):
格式化日期格式2(format):
获取年(year):
获取月(month):
获取日(day):
获取时(hour):
获取分(minute):
获取秒(second):
获取毫秒(millisecond):
获取月份名称(monthName):
获取星期索引,1为星期日,2为星期1,···,7为星期六(dayOfWeek):
获取星期名称(dayOfWeekName):
创建当前date和time(createNow):
创建当前date,time设置为00:00(createToday):

ThymeleafController中新增以下方法:

@GetMapping("/dates")
public String dates(Model model) {
    model.addAttribute("date", new Date());
    return "dates";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第42张图片

(2)Numbers

模板number.html如下:




    
    工具类对象Numbers


    
整数格式化 - 设置最小整数位数为3(formatInteger):
整数格式化 - 设置最小整数位数为6(formatInteger):
整数格式化 - 设置千位分隔符为.(formatInteger):
整数格式化 - 设置千位分隔符为,(formatInteger):
整数数组格式化(arrayFormatInteger): [[${element}]]
小数格式化 - 设置最小整数位数为5且精确保留3位小数位数(formatDecimal)
小数格式化 - 设置千位分隔符为.且小数点分隔符为,(formatDecimal)
货币格式化(formatCurrency):
百分比格式化 - 设置最小整数位数为2且精确保留3位小数(formatPercent):
创建整数数字序列 - 序列从1到10步长为3(sequence): [[${n}]]

ThymeleafController中新增以下方法:

@GetMapping("/numbers")
public String numbers(Model model) {
    Integer[] numArray = {1000, 666, 88888};
    model.addAttribute("num", 99999);
    model.addAttribute("num2", 66.6658932);
    model.addAttribute("nums", numArray);
    return "numbers";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第43张图片

(3)Strings

模板strings.html如下:




    
    工具类对象Strings


    
字符串转换(toString):
检查字符串是否为空(isEmpty):
字符串为空时使用默认值(defaultString):
检查字符串是否以指定片段开头(startsWith):
检查字符串是否以指定片段结尾(endsWith):
检查字符串是否包含指定片段(contains):
判断两个字符串是否相等(equals):
判断两个字符串是否相等,忽略大小写(equalsIgnoreCase):
获取字符串长度(length):
字符串转换为大写字母(toUpperCase):
字符串转换为小写字母(toLowerCase):
片段在字符串中的索引(indexOf):
字符串去除空格(trim):
字符串省略(abbreviate):
字符串截取,从指定索引开始截取到末尾(substring):
字符串截取指定开始索引到结束索引之间的部分,不包含结束索引字符(substring):
截取指定字符串第一次出现前的字符串(substringBefore):
截取指定字符串第一次出现后的字符串(substringAfter):
字符串替换(replace):
从字符串头部向前追加(prepend):
从字符串尾部向后追加(append):
字符串连接(concat):
字符串连接(concatReplaceNulls):
字符串拆分(arraySplit): [[${element}]]
字符串组合(arrayJoin):
随机字符串(randomAlphanumeric):

ThymeleafController中新增以下方法:

@GetMapping("/strings")
public String strings(Model model) {
    model.addAttribute("user", new User(1L, "SpringBoot-Thymeleaf-Strings-Test", ""));
    model.addAttribute("str", "SpringBoot Thymeleaf Strings Test");
    model.addAttribute("strs", new String[] {"SpringBoot", "Thymeleaf", "Strings", "Test"});
    return "strings";
}

效果:

SpringBoot2.x 集成 Thymeleaf的详细教程_第44张图片

代码示例

Github:https://github.com/RtxTitanV/springboot-learning/tree/master/springboot2.x-learning/springboot-thymeleaf

Gitee:https://gitee.com/RtxTitanV/springboot-learning/tree/master/springboot2.x-learning/springboot-thymeleaf

到此这篇关于SpringBoot2.x 集成 Thymeleaf的文章就介绍到这了,更多相关SpringBoot2.x 集成 Thymeleaf内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

你可能感兴趣的:(SpringBoot2.x 集成 Thymeleaf的详细教程)