1 Thymeleaf 简介
Thymeleaf 是一个流行的模板引擎,该模板引擎采用 Java 语言开发模板引擎是一个技术名词,是跨领域跨平台的概念,在 Java 语言体系下有模板引擎,在 C#、PHP 语言体系下也有模板引擎,甚至在 JavaScript 中也会用到模板引擎技术,Java 生态下的模板引擎有 Thymeleaf 、Freemaker、Velocity、Beetl(国产) 等。
Thymeleaf 对网络环境不存在严格的要求,既能用于 Web 环境下,也能用于非 Web 环境下。在非 Web 环境下,他能直接显示模板上的静态数据;在 Web 环境下,它能像 Jsp 一样从后台接收数据并替换掉模板上的静态数据。它是基于 HTML 的,以 HTML 标签为载体,Thymeleaf 要寄托在 HTML 标签下实现。
SpringBoot 集成了 Thymeleaf 模板技术,并且 Spring Boot 官方也推荐使用 Thymeleaf 来替代 JSP 技术,Thymeleaf 是另外的一种模板技术,它本身并不属于 Spring Boot,Spring Boot只是很好地集成这种模板技术,作为前端页面的数据展示,在过去的 Java Web 开发中,我们往往会选择使用 Jsp 去完成页面的动态渲染,但是 jsp 需要翻译编译运行,效率低。
Thymeleaf 的官方网站: http://www.thymeleaf.org
Thymeleaf 官方手册: https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
2 SpringBoot 集成 Thymeleaf
2.1 第一个Thymeleaf示例
(1)添加依赖
添加 starter-web、thymeleaf 和 devtools(可选)。
<!--SpringBoot 集成 Thymeleaf 的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--SpringBoot 开发 web 项目的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
(2)编写控制器,推送model,返回视图名称
@Controller
public class ThymeleafController {
@RequestMapping(value = "/hello")
public String message(Model model){
model.addAttribute("data","SpringBoot集成Thymeleaf模板");
return "hello";
}
}
(3)创建 thymeleaf模板页面
thymeleaf 模板是一个H5页面,放置在“main/resources/templates”目录中。
H5页面中用到 thymeleaf 表达式,需要引入命名空间 “xmlns:th=“http://www.thymeleaf.org””
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
......
<body>
<h1 th:text="${data}">要显示的内容</h1>
</body>
</html>
(4)运行springboot项目,查看效果
2.1 添加Thymeleaf的配置,关闭thymeleaf缓存
当修改 thymeleaf 模板页(html)时,我们发现必须重启springboot才刷新,无法实现热部署刷新。
这是由于thymeleaf模板被缓存了,无法实时更新页面,我们可以通过关闭缓存来实现实时刷新。
#thymeleaf 页面的缓存开关,默认 true 开启缓存
#建议在开发阶段关闭 thymeleaf 页面缓存,目的实时看到页面
spring.thymeleaf.cache=false
#thymeleaf 模版前缀,默认可以不写
spring.thymeleaf.prefix=classpath:/templates/
#thymeleaf 模版后缀,默认可以不写
spring.thymeleaf.suffix=.html
3 Thymeleaf 的绑定表达式
3.1 标准变量表达式
语法: ${}
说明: 标准变量表达式用于访问容器( tomcat)上下文环境中的变量,功能和 EL 中的 ${} 相 同。
Thymeleaf 中的变量表达式使用 {变量名} 的方式获取 Controller 中 model 其中的数据
示例:
(1)创建数据实体类和控制器,推送数据
实体类:
public class User {
private Integer id;
private String username;
private String phone;
private String address;
......
}
控制器:
@RequestMapping("/user-detail")
public String user(Model model){
User user = new User();
user.setId(1001);
user.setUsername("张三");
user.setPhone("13800138001");
user.setAddress("广州总部经济区");
model.addAttribute("user",user);
return "user-detail";
}
(2)编写模板,显示数据
<h2>标准变量表达式:${}</h2>
用户编号:<span th:text="${user.id}" /><br>
用户姓名:<span th:text="${user.username}" /><br>
联系电话:<span th:text="${user.phone}" /><br>
联系地址:<span th:text="${user.address}" /><br>
注意: th:text=“” 是 Thymeleaf 的一个属性,用于文本的显示
3.2 选择变量表达式
*语法: {…}
说明:
选择变量表达式,也叫星号变量表达式,使用 th:object 属性来绑定对象;
选择表达式首先使用 th:object 来绑定后台传来的对象(如:User),然后使用 * 来代表这个对象,后面 {} 中的值是此对象中的属性。
选择变量表达式 *{…} 是另一种类似于标准变量表达式 {…}是在上下文的变量 Model 上求解,这种写法比标准变量表达式繁琐,了解即可
<h2>选择变量表达式:*{},星号表达式</h2>
<div th:object="${user}">
用户编号:<span th:text="*{id}" /><br>
用户姓名:<span th:text="*{username}" /><br>
联系电话:<span th:text="*{phone}" /><br>
联系地址:<span th:text="*{address}" /><br>
</div>
3.3 标准变量表达式和选择表达式混用
<h2>标准变量表达式和选择变量表达式混用</h2>
<h3>=======标准变量表达式=======</h3>
用户编号: <span th:text="${user.id}"></span><br/>
用户姓名: <span th:text="${user.username}"></span><br/>
用户手机号: <span th:text="${user.phone}"></span><br/>
用户地址: <span th:text="${user.address}"></span><br/>
<h3>=======选择变量表达式=======</h3>
用户编号: *{user.id} ==> <span th:text="*{user.id}"></span><br/>
用户姓名: *{ user.username} ==> <span th:text="*{user.username}"></span><br/>
用户手机号: *{user.phone} ==> <span th:text="*{user.phone}"></span><br/>
用户地址: *{user.address} ==> <span th:text="*{user.address}"></span><br/>
3.3 URL路径表达式
语法:@{}
说明:主要用于链接、地址的展示, 可用于 <
script src=“…”>
、 <
link href=“…”>
、 <
a href=“…”>
、 <
form action=“…”>
、 <
img src=“”>
等,可以 在 URL 路径中动态获取数据。
(1)在路径表达式中使用完整路径
<h2>完整路径</h2>
<a th:href="@{http://localhost:8083/user-detail}">完整路径无参数</a><br>
<a th:href="@{'http://localhost:8083/user-detail?id=' +${user.id}}">完整路径有参数</a><br>
(2)使用绝对路径 @{/…}
再实际开发中,完整路径由于服务器不确定而无法使用。
而绝对路径(/…)也会受到部署时的context-path影响:
<h2>绝对路径的问题(修改 context-path 可能导致路径无法访问)</h2>
<a href="/user-detail">完整路径无参数</a><br>
在@{}中,“/”开头代表当前项目的根,可以避免context-path的影响
<h2>绝对路径(@{/}),解决 context-path 变化问题</h2>
<a th:href="@{/user-detail}">绝对路径无参数</a><br>
<a th:href="@{'/user-detail?id=' +${user.id}}">绝对路径有参数</a><br>
这是,随时修改context-path也不会导致路径问题:
server.servlet.context-path=/demo #application.properties中修改配置
(3)更优雅的传递查询参数
不希望通过拼接传递参数时,还可以使用:
@{url(参数1=值1,参数2=值2,...)}
例如:
<h2>更优雅的传递参数</h2>
<a th:href="@{/user-detail(id=${user.id},name=${user.username})}">更优雅的传参</a><br>
<a th:href="@{/info/{id}/{phone}.html(id=${user.id},phone=${user.phone})}">RESTful风格传参</a><br>
4 常见属性绑定
4.1 th:action
th:action 定义后台控制器的路径,类似<
form>
标签的 action 属性,主要结合 URL 表达式,获 取动态变量
<h1>th:action 属性的使用</h1>
<h2>请求路径中需要动态获取变量数据时,必须添加 th 前缀</h2>
<form th:action="@{'/user/login?id='+${user.id}}"></form>
<h2>以下两种方式获取不到用户 id</h2>
<form action="'/user/login?id='+${user.id}"></form>
<form action="/user/login"+${user.id}></form>
为什么后两个中{}语法。
我们请求的流程是,发送请求给服务器,服务器接收请求后,处理请求,跳转到指定的静态 html 页面,在服务器端, Thymeleaf 模板引擎会按照它的语法,对动态数据进行处理,所以如果要是 th 开头,模板引擎能够识别,会在服务器端进行处理,获取数据;如果没有以 th 开头,那么 Thymeleaf 模板引擎不会处理,直接返回给客户端了。
4.2 th:method 设置请求方法
<form id="login" th:action="@{/login}" th:method="post">......</form>
<h1>th:method 属性的使用</h1>
<form th:action="@{/user/login}" th:method="post"></form>
4.3 th:href 定义超链接
主要结合 URL 表达式,获取动态变量
<a href="http://www.baidu.com">超链接百度</a><br/>
<a th:href="'http://www.baidu.com?id=' + ${user.id}">th:href 链接</a>
4.4 th:src 用于外部资源引入
比如<
script>
标签的 src 属性, <
img>
标签的 src 属性,常与@{}表达式结 合使用,在 SpringBoot 项目的静态资源都放到 resources 的 static 目录下。 放到 static 路径下的内容,写路径时不需要写上 static
<h1>th:src 属性的使用</h1>
<!--以下方式无法引入 js-->
<script src="/js/jquery-1.7.2.min.js"></script>
<!--该方法是常用方法-->
<script type="text/javascript"
th:src="@{/jquery-1.7.2.min.js}"></script>
<script>
$(function () {
alert("引入 js 文件");
});
</script>
当设置了网站的相对根路径有区别“server.servlet.context-path=/demo”,这种方式比传统方式的好处是,在 URL 表达式前加/,会自动加上上下文根,避免 404 找不 到资源的情况
4.5 th:id 类似 html 标签中的 id 属性
<span th:id="${hello}">aaa</span>
4.6 th:name 设置名称
<input th:type="text" th:id="userName" th:name="userName">
4.7 th:value
类似 html 标签中的 value 属性,能对某元素的 value 属性进行赋值
<input type="hidden" id="userId" name="userId" th:value="${userId}">
4.8 th:attr
该属性也是用于给 HTML 中某元素的某属性赋值,好处是可以给 html 中没有定义的属性动 态的赋值
<h1>th:attr 属性的使用</h1>
<span zhangsan="${user.username}"></span>
<!--通过 th:attr 对自定义的属性赋值-->
<span th:attr="zhangsan=${user.username}"></span>
4.9 th:text 文本的绑定
该属性显示的文本在标签体中,如果是文本框,数据会在文本框外显示, 要想显示在文本框内,使用th:value
<input type="text" id="realName" name="reaName" th:text="${realName}">
4.10 th:object 数据对象绑定,通常用于选择变量表达式(前面已介绍)
4.11 th:onclick 绑定事件代码
<h1>th:onclick 的使用</h1>
<!--目前 thymeleaf 版本要求只能传递数字和布尔值-->
<a th:onclick="'show('+${user.id}+')'">点击:显示学生编号</a>
<script type="text/javascript">
function show(id) {
alert("用户编号为: " + id);
}
</script>
4.12 th:style 设置样式
<a th:onclick="'show('+${user.id}+')'" th:style="'font-size:40px;color:red;'">点击:显示学生编号</a>
5 流程控制
5.1 th:each 迭代(循环)集合元素
比后台传来一个对象集合那么就可以使用此属性遍历输出,它与 JSTL 中的<c: forEach>类似,此属性既可以循环遍历集合,也可以循环遍历数组及 Map 。
语法:th:each=“迭代变量, 迭代状态 : ${集合}”
例如:
th:each="user, interStat : ${userList}"
(1){userList}集合中的一个数据
(3)interStat:循环体的状态信息,包含以下属性:
index: 当前迭代对象的 index(从 0 开始计算)
count: 当前迭代对象的个数(从 1 开始计算) 这两个用的较多
size: 被迭代集合的大小
current: 当前迭代变量
even/odd: 布尔值,当前循环是否是偶数/奇数(从 0 开始计算)
first: 布尔值,当前循环是否是第一个
last: 布尔值,当前循环是否是最后一个
注意:迭代状态 interStat 也可以不定义变量名,默认变量名为迭代变量加上Stat后缀,即 userStat
5.1.1 迭代 List
控制器
@RequestMapping(value = "/each-list")
public String userList(Model model){
List<User> userList = new ArrayList<>();
for(int i=0; i<=9; i++){
User user = new User(100+i, "zhang"+i, "1380013800"+i,"广州市中山"+i+"路");
userList.add(user);
}
model.addAttribute("userList",userList);
return "each-list";
}
thymeleaf
<div th:each="user,userState:${userList}">
<p style="color:gray">
<span th:text="'index:'+${userState.index}"></span>,
<span th:text="'count:'+${userState.count}"></span>,
<span th:text="'first:'+${userState.first}"></span>,
<span th:text="'last:'+${userState.last}"></span>,
<span th:text="'odd:'+${userState.odd}"></span>,
<span th:text="'odd:'+${userState.even}"></span>
</p>
<p style="color:blue">
<span th:text="${user.id}"></span>,
<span th:text="${user.username}"></span>,
<span th:text="${user.phone}"></span>,
<span th:text="${user.address}"></span>
</p>
</div>
5.1.2 迭代 Array
控制器:
@RequestMapping(value="/each-array")
public String userArray(Model model){
User[] userArray = new User[10];
for(int i=0; i<10; i++){
User user = new User(i, "li"+i, "1380013800"+i, "广州市"+i);
userArray[i] = user;
}
model.addAttribute("userArray", userArray);
return "each-array";
}
thymeleaf:
<div th:each="user,userState:${userArray}">
<p style="color: gray">
<span th:text="'index:'+${userState.index}"></span>,
</p>
<p style="color: blue;">
<span th:text="${user.id}"></span>,
<span th:text="${user.username}"></span>,
<span th:text="${user.phone}"></span>,
<span th:text="${user.address}"></span>
</p>
</div>
5.1.3 迭代Map
控制器:
@RequestMapping(value="/each-map")
public String userMap(Model model){
Map<Integer,Object> userMap = new HashMap<>();
for(int i=0; i<10; i++){
User user = new User(i, "li"+i, "1380013800"+i, "广州市"+i);
userMap.put(i,user);
}
model.addAttribute("userMap", userMap);
return "each-map";
}
thymeleaf:
<div th:each="item:${userMap}">
<p style="color: gray">
index: <span th:text="${itemStat.index}"></span>
</p>
<p style="color: blue">
key:<span th:text="${item.key}"></span>,
id:<span th:text="${item.value.id}"></span>,
nick:<span th:text="${item.value.username}"></span>
phone:<span th:text="${item.value.phone}"></span>
phone:<span th:text="${item.value.address}"></span>
</p>
</div>
5.2 条件判断
5.2.1 th:if / th:unless
控制器
@RequestMapping(value = "/condition-if")
public String conditionIf(Model model){
model.addAttribute("sex",1);
return "condition-if";
}
thymeleaf
<h2>th:if 如果满足条件成立则渲染显示(执行),否则相反</h2>
<div th:if="${sex eq 1}">
男
</div>
<h2>th:unless 与th:if用法相反,除非条件不成立才渲染
<div th:unless="${sex eq 1}">
女
</div>
5.2.2 th:switch / th:case
控制器
@RequestMapping(value = "/condition-switch")
public String conditionIf(Model model){
model.addAttribute("productType",1);
return "condition-switch";
}
thymeleaf
<h2>th:switch/th:case用法</h2>
<div th:switch="${productType}">
<span th:case="1">家电</span>
<span th:case="2">服装</span>
<span th:case="*">其它</span>
</div>
一旦某个 case 判断值为 true,剩余的 case 默认不执行,“*”表示默 认的 case,前面的 case 都不匹配时候,执行默认的 case
5.3 使用空标签<th:block>
前面的表达式都需要绑定在一个html元素的属性中,诸如 if 和 foreach 就需要一个html 元素作为容器,如果我们没有适当的html元素充当容器,则可以使用 thymeleaf 提供的“空标签”来充当容器,<th:block>
渲染后不生成html标签。
<th:block th:if="${now.getHours()<=12}">
早上好
</th:block>
<th:block th:if="${now.getHours()>12}">
下午好
</th:block>
6 字面量
6.1 文本字面量(用单引号’…'包围的字符串 )
<h1>文本字面量:用单引号'...'包围的字符串</h1>
<a th:href="@{'/user/info?id=' + ${user.id}}">查看用户:文本字面的路径使用</a><br/>
<span th:text="您好"></span>
6.2 数字字面量
<h1>数字字面量</h1>
今年是<span th:text="2019">1949</span>年<br/>
20 年后,将是<span th:text="2019 + 20">1969</span>年<br/>
6.3 boolean 字面量
<h1>boolean 字面量</h1>
<div th:if="${success}">执行成功</div>
<div th:unless="${flag}">执行不成功</div>
6.4 null 字面量
<h1>null 字面量</h1>
<span th:if="${user ne null}">用户不为空</span><br/>
<span th:unless="${user eq null}">用户不为空(使用 th:unless 取反) </span><br/>
7 Thymeleaf 运算符
三元运算:表达式?”正确结果”:”错误结果”
算术运算: + , - , * , / , %
关系比较:: > , < , >= , <= ( gt , lt , ge , le )
相等判断: == , != ( eq , ne )
8 Thymeleaf 字符串拼接
语法:th:text=“|字面量{表达式}…|”
<h1>文本字面量使用"+"拼接字符串</h1>
<span th:text="'共'+${totalRows}+'条'+${totalPages}+'页,当前第'+${currentPage}+'页'"></span>
<h1>另一种更优雅的方式:使用"|要拼接的内容|"减少字符串拼接的加号</h1>
<span th:text="|共${totalRows}条${totalPages}页,当前第${currentPage}页|"></span>
9 内联文本
(1)内联文本
语法:th:inline=“text”
<!--文档-->
<div th:inline="text">官方文档指出这样用:[[${message}]]</div>
<!--实际-->
<div>实际上也可以直接用:[[${message}]]</div>
(2)内联脚本
语法:th:inline=“javascript”
<!--文档-->
<script th:inline="javascript">alert([[${message}]])</script>
<!--实际-->
<script>alert('[[${message}]]')</script>
10 Thymaleaf 访问 WEB 上下文对象
模板引擎提供了一组内置的对象,例如我们比较常用的 Servlet API 对象,这些的对象可以直接在模板中使用,使用#号开始。
控制器:
@RequestMapping("/servlet-objects")
public String servletObjects(HttpServletRequest request){
request.getSession().setAttribute("user", "zhang3");
return "servlet-objects";
}
10.1 #request
#request 相当于 HttpServletRequest 对象,这是3.x版 本的写法 , 若是2.x版本则使用#httpServletRequest
<h2>从Requst中获取</h2>
<script th:inline="javascript">
var scheme = [[${#request.getScheme()}]];
var serverName = [[${#request.getServerName()}]];
var serverPort = [[${#request.getServerPort()}]];
var contextPath = [[${#request.getContextPath()}]];
var allPath = scheme+"://"+serverName+":"+serverPort+"/"+contextPath;
alert(allPath);
var requestURL = [[${#httpServletRequest.requestURL}]];
var queryString = [[${#httpServletRequest.queryString}]];
alert(requestURL);
alert(queryString);
</script>
10.2 获取请求参数
thymeleaf 模板中可以通过以下方式简单获取请求参数:
${param.请求参数名}
<input type="text" name="username" th:value="${param.username}" />
<span th:text="${param.id}"></span>
10.3 #session
相当于 HttpSession 对象,这是 3.x 版本,若是 2.x 版本使用#httpSession,在后台方法中向 session 中放数据
<h2>从Session中获取值</h2>
<div th:text="${#session.getAttribute('user')}"></div>
<div th:text="${#httpSession.getAttribute('user')}"></div>
10.4 获取 session 中的对象
如果仅仅想获取session作用域中键对应的对象,可以直接使用:
${session.键}
<h3>
欢迎光临,
<th:block th:if="${session.loginUser==null}">游客</th:block>
<th:block th:if="${session.loginUser!=null}" th:text="${session.loginUser.username}"></th:block>!
</h3>
11 Thymeleaf 表达式功能对象
模板引擎提供的一组功能性内置对象,可以在模板中直接使用这些对象提供的功能方法 工作中常使用的数据类型,如集合,时间,数值,可以使用 Thymeleaf 的提供的功能性对象 来处理它们。
内置功能对象前都需要加#号,内置对象一般都以 s 结尾
官方手册: http://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
#dates: java.util.Date 对象的实用方法
<span th:text="${#dates.format(curDate, 'yyyy-MM-dd HH:mm:ss')}"></span>
#calendars: 和 dates 类似, 但是 java.util.Calendar 对象
#numbers: 格式化数字对象的实用方法
#strings: 字符串对象的实用方法: contains, startsWith, prepending/appending 等
#objects: 对 objects 操作的实用方法
#bools: 对布尔值求值的实用方法
#arrays: 数组的实用方法
#lists: list 的实用方法
<span th:text="${#lists.size(datas)}"></span>
#sets: set 的实用方法
#maps: map 的实用方法
#aggregates: 对数组或集合创建聚合的实用方法
例:
控制器
@RequestMapping("/functions")
public String function(Model model){
model.addAttribute("time",new Date());
model.addAttribute("data", "SpringBoot Data");
return "functions";
}
thymeleaf
<div th:text="${time}"></div>
<div th:text="${#dates.format(time, 'yyyy-MM-dd HH:mm:ss')}"></div>
<div th:text="${#strings.substring(data,6,10)}"></div>