Springboot - Thymeleaf
1. Thymeleaf
概念
Thymeleaf
是一个跟FreeMarker
类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下特点:- 动静结合:
Thymeleaf
在有网络和无网络的环境下皆可运行,无网络显示静态内容,有网络用后得到数据替换静态内容 - 与SpringBoot完美整合,
springboot
默认整合thymeleaf
- 动静结合:
1.1 入门案例
实体类、Mapper文件同博文 - 整合mybatis案例
编写接口
- 编写UserService,调用UserMapper的查询所有方法
/**
* User - Service
*
* @author murphy
*/
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public List<User> findAll() {
return userMapper.selectAll();
}
}
- 编写一个
controller
- 返回一些用户数据,放入模型中,等会在页面渲染
/**
* User - Controller
*
* @author murphy
*/
@Controller
public class UserController {
@Resource
private UserService userService;
@RequestMapping("all")
public String all(Model model) {
List<User> list = userService.findAll();
model.addAttribute("users", list);
// 跳转 - classpath:/templates/users.html
return "users";
}
}
- 引入启动器 - 直接引入启动器:
<!-- thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
SpringBoot会自动为Thymeleaf
注册一个视图解析器:
与解析JSP的InternalViewResolver
类似,Thymeleaf
也会根据前缀和后缀来确定模板文件的位置:
- 默认前缀:
classpath:/templates/
- 默认后缀:
.html
所以如果我们返回视图:users
,会指向到classpath:/templates/users.html
一般我们无需进行修改,默认即可。
静态页面
根据上面的文档介绍,模板默认放在classpath
下的templates
文件夹,我们新建一个html
文件放入其中:
编写html
模板,渲染模型中的数据:
注意:把html的名称空间,改成xmlns:th="http://www.thymeleaf.org"
会有语法提示
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
<style type="text/css">
table {
border-collapse: collapse;
font-size: 14px;
width: 80%;
margin: auto
}
table, th, td {
border: 1px solid darkslategray;
padding: 10px
}
</style>
</head>
<body>
<div style="text-align: center">
<span style="color: darkslategray; font-size: 30px">欢迎光临!</span>
<hr/>
<table class="list">
<tr>
<th>id</th>
<th>姓名</th>
<th>用户名</th>
<th>年龄</th>
<th>性别</th>
<th>生日</th>
<th>备注</th>
<th>操作</th>
</tr>
<tr th:each="user : ${users}">
<td th:text="${user.id}">1</td>
<td th:text="${user.name}">murphy</td>
<td th:text="${user.userName}">张三</td>
<td th:text="${user.age}">20</td>
<td th:text="${user.sex} == 1 ? '男': '女'">男</td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd')}">1980-02-30</td>
<td th:text="${user.note}">1</td>
<td>
<a href="#">删除</a>
<a href="#">修改</a>
<a href="#">审核</a>
</td>
</tr>
</table>
</div>
</body>
</html>
我们看到这里使用了以下语法:
${}
:这个类似与el表达式,但其实是ognl
的语法,比el表达式更加强大th-
指令:th-
是利用了Html5
中的自定义属性来实现的。如果不支持H5,可以用data-th-
来代替th:each
:类似于c:foreach
遍历集合,但是语法更加简洁th:text
:声明标签中的文本- 例如
<td th-text='${user.id}'>1</td>
,如果user.id
有值,会覆盖默认的 1 - 如果没有值,则会显示
td
中默认的1。这正是thymeleaf
能够动静结合的原因,模板解析失败不影响,页面的显示效果,因为会显示默认值!
- 例如
测试
接下来,我们打开页面测试一下:
模板缓存
Thymeleaf
会在第一次对模板解析之后进行缓存,极大的提高了并发处理能力。但是这给我们开发带来了不便,修改页面后并不会立刻看到效果,我们开发阶段可以关掉缓存使用:
# 开发阶段关闭thymeleaf的模版缓存
spring.thymeleaf.cache=false
注意:
- 在IDEA中,我们需要在修改页面后按快捷键:
Ctrl + Shift + F9
对项目进行rebuild
才可以。
1.2 thymeleaf 详解
表达式
它们分为三类:
-
变量表达式
-
选择或星号表达式
-
URL表达式
变量表达式
- 变量表达式即
OGNL
表达式或Spring EL
表达式(在Spring中用来获取model attribute
的数据)。如下所示:
${session.user.name}
它们将以HTML标签的一个属性来表示:
<h5>表达式</h5>
<span>${text}</span>
<span th:text="${text}">你好 thymleaf</span>
选择(星号)表达式
- 选择表达式很像变量表达式,不过它们用一个预先选择的对象来代替上下文变量容器
(map)
来执行,如下:
*{customer.name}
被指定的object
由th:object
属性定义:
<tr th:each="user, status : ${users}" th:object="${user}" th:bgcolor="${status.even} ? 'gray'">
<td th:text="${status.count}">1</td>
<td th:text="*{id}">1</td>
<td th:text="*{name}">murphy</td>
<td th:text="*{userName}">张三</td>
<td th:text="${user.age}">20</td>
<td th:text="${user.sex} == 1 ? '男': '女'">男</td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd')}">1980-02-30</td>
<td th:text="${user.note}">1</td>
<td>
<a th:href="@{/delete(id=*{id},userName=*{userName})}">删除</a>
<a th:href="|/update/${user.id}|">修改</a>
<a th:href="'/approve/' + ${user.id}">审核</a>
</td>
</tr>
URL表达式
URL表达式
指的是把一个有用的上下文或回话信息添加到URL,这个过程经常被叫做URL重写。@{/order/list}
URL还可以设置参数: @{/order/details(id=${orderId}, name=*{name})}
相对路径:@{../documents/report}
让我们看这些表达式:
<form th:action="@{/createOrder}">
<a href="main.html" th:href="@{/main}">
url表达式
<a th:href="@{/delete(id=${user.id}, userName=*{userName})}">删除</a>
文本替换
<a th:href="|/update/${user.id}|">修改</a>
字符串拼接
<a th:href="'/approve/' + ${user.id}">审核</a>
表达式常见用法
字面(Literals)
- 文本文字(Text literals):
'one text', 'Another one!',...
- 数字文本(Number literals):
0, 34, 3.0, 12.3,...
- 布尔文本(Boolean literals):
true, false
- 空(Null literal):
null
- 文字标记(Literal tokens):
one, sometext, main,...
文本操作(Text operations)
- 字符串连接(String concatenation):
+
- 文本替换(Literal substitutions):
|The name is ${name}|
算术运算(Arithmetic operations)
- 二元运算符(Binary operators):
+, -, *, /, %
- 减号(单目运算符)Minus sign (unary operator):
-
布尔操作(Boolean operations)
- 二元运算符(Binary operators):
and, or
- 布尔否定(一元运算符)Boolean negation (unary operator):
!, not
比较和等价(Comparisons and equality)
- 比较(Comparators):
>, <, >=, <= (gt, lt, ge, le)
- 等值运算符(Equality operators):
==, != (eq, ne)
条件运算符(Conditional operators)
If-then
:(if)? (then)
If-then-else
:(if)? (then) : (else)
Default
:(value) ?: (defaultvalue)
常用th标签
基本用法
- 赋值、字符串拼接
字符串拼接还有另外一种简洁的写法
<a th:href="|/update/${user.id}|">修改</a>
<a th:href="'/approve/' + ${user.id}">审核</a>
- 条件判断
If/Unless
Thymeleaf中使用th:if
和th:unless
属性进行条件判断,下面的例子中,<a>
标签只有在th:if
中条件成立时才显示:
<h5>if指令</h5>
<a th:if="${users.size() > 0}">查询结果存在</a>
<a th:if="${users.size() <= 0}">查询结果不存在</a>
<a th:unless="${session.user != null}" href="#">登录</a>
th:unless
于th:if
恰好相反,只有表达式中的条件不成立,才会显示其内容。
也可以使用(if)? (then) : (else)
这种语法来判断显示的内容
- for 循环
<tr th:each="user, status : ${users}" th:object="${user}" th:bgcolor="${status.even} ? 'gray'">
<td th:text="${status.count}">1</td>
<td th:text="*{id}">1</td>
<td th:text="*{name}">murphy</td>
<td th:text="*{userName}">张三</td>
<td th:text="${user.age}">20</td>
<td th:text="${user.sex} == 1 ? '男': '女'">男</td>
<td th:text="${#dates.format(user.birthday, 'yyyy-MM-dd')}">1980-02-30</td>
<td th:text="${user.note}">1</td>
<td>
<a th:href="@{/delete(id=*{id},userName=*{userName})}">删除</a>
<a th:href="|/update/${user.id}|">修改</a>
<a th:href="'/approve/' + ${user.id}">审核</a>
</td>
</tr>
status
称作状态变量,属性有:
index
:当前迭代对象的index
(从 0 开始计算)count
:当前迭代对象的index
(从 1 开始计算)size
:被迭代对象的大小current
:当前迭代变量even/odd
:布尔值,当前循环是否是偶数/奇数(从 0 开始计算)first
:布尔值,当前循环是否是第一个last
:布尔值,当前循环是否是最后一个
- 内联文本
- 内联文本:
[[...]]
内联文本的表示方式,使用时,必须先用th:inline="text/javascript/none"
激活,th:inline
可以在父级标签内使用,甚至作为body
的标签。内联文本尽管比th:text
的代码少,不利于原型显示。
在thymeleaf
指令中显示
<h6 th:text="${text}">静态内容</h6>
使用内联文本显示model attribute
<h5>内联文本</h5>
<span>[[${text}}]]</span><br>
<span th:inline="text">[[${text}}]]</span><br>
<span th:inline="none">[[${text}}]]</span><br>
原则能用指令就用th指令
- 内联JS
- 内联文本:
[[...]]
内联文本的表示方式,使用时,必须先用th:inline="text/javascript/none"
激活,th:inline
可以在父级标签内使用,甚至作为body
的标签。内联文本尽管比th:text
的代码少,不利于原型显示。
<h5>内联js</h5>
<script th:inline="javascript">
/*<![CDATA[*/
var text = '[[${text}]]';
alert(text);
/*]]>*/
</script>
- 内嵌变量
- 为了模板更加易用,Thymeleaf还提供了一系列Utility对象(内置于Context中),可以通过
#
直接访问:dates
:java.util.Date**
的功能方法类。calendars
:类似#dates
,面向java.util.Calendar
numbers
:格式化数字的功能方法类strings
:字符串对象的功能类,contains,startWiths,prepending/appending
等等。objects
:对objects
的功能类操作。bools
:对布尔值求值的功能方法。arrays
:对数组的功能类方法。lists
:对lists
功能类方法sets
maps
下面用一段代码来举例一些常用的方法:
dates
<h5>内置变量</h5>
<h6 th:text="${#dates.createNow()}">获取当前日期</h6>
strings
<h5>内置变量</h5>
<h6 th:text="${#dates.createNow()}">获取当前日期</h6>
<h6 th:text="${#strings.substring(text, 6, 9)}">截取字符串</h6>
<h6 th:text="${#strings.length(text)}">获得长度</h6>
<h6 th:text="${#strings.randomAlphanumeric(6)}">随机字符串</h6>
<h6 th:text="${#strings.equals(text, 'hello text....')}"></h6>
- 使用
thymeleaf
布局
使用thymeleaf布局非常的方便
在/resources/templates/
目录下创建footer.html
,内容如下
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<footer th:fragment="copy(title)">
© murphymurphy<br>
<span th:text="${title}">title footer</span>
</footer>
</body>
</html>
在页面任何地方引入:
th:insert
:保留自己的主标签,保留th:fragment
的主标签。th:replace
:不要自己的主标签,保留th:fragment
的主标签。th:include
:保留自己的主标签,不要th:fragment
的主标签。
返回的HTML如下:
<h5>thymeleaf布局</h5>
<div>
<footer>
© murphymurphy<br>
<span>Yang</span>
</footer>
</div>
<footer>
© murphymurphy<br>
<span>Yang</span>
</footer>
<div>
© murphymurphy<br>
<span>Yang</span>
</div>