目录
开始之前
在开始之前,先创建项目spring-boot-web
添加Spring Web和Thymeleaf依赖
勾选那两项是为了在Maven中引入以下两个依赖:
创建好后的项目目录如下(框起来的部分可以删除):
一 静态内容
1.1 静态资源的查找路径
默认情况下,Spring Boot从类路径中名为/static、/public、/resources或/META-INF/resources的目录下查找静态内容。
在resources下创建这三个目录:static、public、resources,并在static目录下创建hello.html文件,文件写入以下内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>静态资源文件</title>
</head>
<body>
<h2>这是static目录下的静态资源文件</h2>
</body>
</html>
启动/重启项目,访问一下http://localhost:8080/hello.html
将hello.html文件移动到public或resources目录后,重启项目,再访问一下:
1.2 修改资源文件映射路径
默认情况下,资源映射到/**(即在浏览器中通过http://localhost:8080/xxx即可访问到xxx资源文件),也可以使用spring.mvc.static-path-pattern属性对其进行调整。例如,将所有资源重新映射到/resources/**可以通过以下方式实现:
spring.mvc.static-path-pattern=/resources/**
重启项目,再访问一下,发现已经访问不到了:
在url上加上/resources/后再试一下‘
1.2 欢迎页面
Spring Boot支持静态和模板欢迎页面。它首先在静态资源文件的查找目录中查找index.html文件,如果未找到,则寻找index模板文件。如果找到任何一个,它将自动作为web应用的欢迎界面(即在浏览器中通过http://localhost:8080直接访问到的页面)。
创建index.html文件,并将其放入任一静态资源文件路径下,写入以下内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>这是首页</h1>
</body>
</html>
重启项目,在浏览器访问一下(注意:如果修改了资源文件映射路径的话,将访问不到)
1.3 自定义图标
Spring Boot支持自定义图标,在这里提供两种方式:
方法一:
Spring Boot将在静态资源文件目录查找favicon.ico文件,如果找到了,将以此作为web应用的图标(需要清除浏览器缓存才能看得到)。
方法二:
1.配置spring.mvc.favicon.enabled属性为false
2.在index.html中添加以下代码
<link rel="icon" type="image/x-icon" href="/images/favicon.ico">
方法二有点投机取巧的意思。在访问首页时,浏览器缓存了web应用的图标,而后只要不清除缓存,浏览器显示的就是首页设定的图标。
二 模板引擎
2.1 Spring Boot支持的模板引擎
Spring MVC支持提供动态HTML内容。Spring MVC支持各种模板技术,包括Thymeleaf,FreeMarker和JSP。
SpringBoot对以下模板引擎做了自动配置的支持:
- FreeMarker
- Groovy
- Thymeleaf
- Mustache
SpringBoot官方不推荐使用JSP。将JSP与嵌入式Servlet一起使用时,将存在一些局限,具体可参考官方文档
在默认情况下,使用这些模板引擎之一时,Spring Boot将从resources/templates目录下提取模板引擎。
2.2 使用Thymeleaf模板引擎
2.2.1 引入Thymeleaf
-
确保pom.xml文件中有添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
-
在使用Thymeleaf的html页面上声明名称空间
<html xmlns:th="http://www.thymeleaf.org">
-
Thymeleaf不支持像访问静态资源那样直接被访问。为了看到效果,编写Controller调用我们的Thymeleaf测试文件。
package com.yky.springboot.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; @Controller public class ThymeleafTestController { @GetMapping(value = "/thymeleaf") public String callMyThymeleaf() { return "ThymeleafTest"; } }
ThymeleafTest放到了templates/ThymeleafTest.html路径下。
2.3 Thymeleaf标准表达式语法
2.3.1 message表达式
格式: #{…}
message表达式用于从资源文件中读取内容,并显示在页面上。message表达式的主要用途是用于显示国际化信息,这部分知识将会在本篇文章后面讲解。
<!DOCTYPE html>
<html lang="en">
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>标题</title>
</head>
<body>
<p th:text="#{home.welcome}"></p>
</body>
</html>
在配置文件中有配置home.welcome的值:
效果:
message表达式还支持传入参数:
<p th:text="#{home.welcome('爱学习的少年')}"></p>
配置文件内容:
home.welcome=你好,{0},欢迎
在message表达式传的第一个参数将替代{0},传递的第二个参数将替代{1},以此类推。。。。
2.3.2 变量表达式
格式:${}
变量表达式主要有以下功能:
- 获取变量值
如:${today} - 获取对象属性,调用对象方法,支持OGNL表达式
如:${employee.name} - 使用内置对象
如:${#locale.country}
[1]获取变量:
@Controller
public class ThymeleafTestController
{
@GetMapping(value = "/thymeleaf")
public String callMyThymeleaf(Map<String,Object> map)
{
map.put("today",new Date());
return "ThymeleafTest";
}
}
<div th:text="${today}"></div>
效果:
[2]获取对象属性,调用对象方法
新创建Employee对象如下:
package com.yky.springboot.entity;
public class Employee
{
private String name;
private Integer id;
private Double salary;
getter,setter方法
toString方法
}
ThymeleafTestController代码改写如下:
@GetMapping(value = "/thymeleaf")
public String callMyThymeleaf(Map<String,Object> map)
{
map.put("today",new Date());
Employee employee = new Employee();
employee.setName("yky");
employee.setId(10);
employee.setSalary(15000D);
map.put("employee",employee);
return "ThymeleafTest";
}
html添加以下代码:
<table border="1px">
<tr>
<td>姓名</td>
<td>工号</td>
<td>薪资</td>
</tr>
<tr>
<td th:text="${employee.name}"></td>
<td th:text="${employee.getId()}"></td>
<td th:text="${employee.salary}"></td>
</tr>
</table>
效果如下:
[3] 使用内置对象或工具类
上面的日期显示的有点让人看不懂,不如用内置dates对象转换一下:
<div th:text="${#dates.format(today,'yyyy-MM-dd HH:mm:ss')}"></div>
效果:
其支持的内部对象有:
- #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).
上述工具类的用法参照官方文档
2.3.3 选择表达式
格式:*{…}
选择表达式的和变量表达式类似,在功能上是一样的。下面先来看看它的用法:
<table border="1px">
<tr>
<td>姓名</td>
<td>工号</td>
<td>薪资</td>
</tr>
<tr>
<td th:text="${employee.name}"></td>
<td th:text="${employee.getId()}"></td>
<td th:text="${employee.salary}"></td>
</tr>
<tr th:object="${employee}">
<td th:text="*{name}"></td>
<td th:text="*{getId()}"></td>
<td th:text="*{salary}"></td>
</tr>
</table>
可以很明显看到选择表达式和变量表达式的区别:当选择表达式配合th:object一起使用时,可以省掉对象名前缀。
2.3.4 链接
格式:@{…}
当有用到uri时推荐使用此表达式。
比如提交表单时的action、a标签的href属性等,常常配合th:action、th:href、th:src等属性使用。
<a th:href="@{/}">回到首页</a>
还支持字符串拼接和传递参数(替代了?xxx=xx的形式):
<a th:href="@{${url}(orderId=${o.id})}">view</a>
<a th:href="@{'/details/'+${user.login}(orderId=${o.id})}">view</a>
2.3.5 片段表达式
2.3.6 字面量
[1] 文本文字
直接使用单引号
‘working web application’
<p>
Now you are looking at a <span th:text="'working web application'">template file</span>.
</p>
[2] 数字
2013,2013+2,3.1415,5.0
<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>
[3] 布尔值
true、false
<div th:if="${user.isAdmin()} == false"> ...
或
<div th:if="${user.isAdmin() == false}"> ...
[4] null值
null
<div th:if="${variable.something} == null"> ...
[5] 文本标记
数字、布尔值和null实际上是文字标记的一种特殊情况。
这些标记允许在标准表达式中进行一些简化。它们的工作原理与文本文字(’…’)完全相同,但它们只允许字母(A-Z和A-Z)、数字(0-9)、括号([和])、点(.)、连字符(-)和下划线(_)。没有空格,没有逗号,等等。
标记文本不需要单引号,这使得代码看上去更加简练:
<p>
Now you are looking at a <span th:text="working_web_application">template file</span>.
</p>
2.3.7 文本操作
[1] 追加文本
直接使用+号拼接
<span th:text="'The name of the user is ' + ${user.name}">
[2] 文本替换
<span th:text="|Welcome to our application, ${user.name}!|">
等同于
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">
文本替换还可以与其他类型表达式组合使用
<span th:text="${onevar} + ' ' + |${twovar}, ${threevar}|">
2.3.8 算数运算
算数运算支持+ , - , * , / 和 % .
<p th:text="20 * 5"></p>
2.3.9 比较运算
支持:gt ( > ), lt ( < ), ge ( >= ), le (<=), not (!).Also eq (==), neq/ne (!=).
<p th:if="20 > 10" th:text="显示该内容"/>
<p th:if="20 == 10" th:text="不显示该内容"/>
2.3.10 布尔运算
支持:and、or、not(!)
<p th:if="not true" th:text="不显示该内容"/>
2.3.11 三目运算
[1] if-then
格式:(if) ? (value)
<p th:text="${employee.id} <= 10 ? '领导'"></p>
[2] if-then-else
格式:(if) ? (value1) : (value2)
<p th:text="${employee.salary} > 10000 ? '高薪资':'低薪资'"></p>
[3] default
格式:(if) ? : (defaultValue)
如果前者不为空,取前者。否则,取后者。
<p th:text="${employee.name} ?: '姓名为空,未知'"></p>
2.4 Thymeleaf常用属性
官方文档列出的Thymeleaf属性如下:
属性 | 说明 |
---|---|
th:insert 、th:replace | 片段包含 |
th:each | 迭代 |
th:if 、 th:unless 、 th:switch 、 th:case | 条件判断 |
th:object、th:with | 声明变量 |
th:attr 、 th:attrprepend 、 th:attrappend | 修改任意属性, prepend前面追加, append后面追加 |
th:value 、 th:href 、 th:src 、 … | 修改HTML原生属性 |
th:text、th:utext | 标签体内容修改 ,th:utext会解析HTML在界面中显示相应的效果,th:text不会 |
th:fragment | 片段声明 |
th:remove | 片段移除 |
在2.2.2表达式语法那一节其实就已经使用了Thymeleaf的某些属性,下面再针对常用属性进行说明一下。
2.4.1 th:text、th:utext显示标签体内容
显示标签体内容用th:text或th:utext属性,下面看一下两者的用法。
在Controller中添加下面一行代码:
map.put("msg","<h1>上午好</h1>");
在HTML获取msg:
<p th:text="${msg}"></p>
<p th:utext="${msg}"></p>
看一下效果:
可以看到两者都显示出来了,两者的区别是th:text只会将文本原样显示,th:utext有解析html的功能。
2.4.2 行内表达式
前面刚说了在标签体上显示内容,Thymeleaf还支持在非标签体上显示内容。
格式:[[…]]
还是上面那个例子:
<p th:text="${msg}"></p>
<p th:utext="${msg}"></p>
[[${msg}]]
<p> Hello,[[${employee.name}]] </p>
显示效果:
2.4.3 th:each迭代
为了演示迭代,在Controller中加入以下代码:
Employee employee1 = new Employee();
employee1.setName("name1");
employee1.setId(1);
employee1.setSalary(14000D);
Employee employee2 = new Employee();
employee2.setName("name2");
employee2.setId(2);
employee2.setSalary(14500D);
Employee employee3 = new Employee();
employee3.setName("name3");
employee3.setId(3);
employee3.setSalary(14800D);
List<Employee> employees = new LinkedList<>();
employees.add(employee1);
employees.add(employee2);
employees.add(employee3);
map.put("employees",employees);
我们接下来要实现的效果就是用迭代器,将这三列数据在HTML页面上显示出来
<table border="1px">
<tr>
<td>姓名</td>
<td>工号</td>
<td>薪资</td>
</tr>
<tr th:each="emp : ${employees}">
<td th:text="${emp.name}"></td>
<td th:text="${emp.id}"></td>
<td th:text="${emp.salary}"></td>
</tr>
</table>
效果:
在本例中,th:each作用于tr标签上,将使整个tr标签进行迭代-----即每一次迭代都会生成一个当前标签。
Thymeleaf还支持获取迭代状态,这就需要给th:each多传入一个参数,来获取当前的迭代状态:
<tr th:each="emp,iterState : ${employees}">
...
</tr>
在这里,多传入了一个参数iterState。迭代器的状态信息将保存在iterState(名字任取)对象中,该对象具有以下属性:
- index,当前下标(从0开始)
- count,当前下标(从1开始)
- size,总记录数
- current,当前迭代出的对象
- even/odd,当前迭代的下标是偶数还是奇数(从1开始算,返回布尔值)
- first,当前是否为第一个元素
- last,当前是否为最后一个元素
示例代码如下:
<table border="1px">
<tr>
<td>序号</td>
<td>奇偶性</td>
<td>姓名</td>
<td>工号</td>
<td>薪资</td>
</tr>
<tr th:each="emp,iterState : ${employees}">
<td th:text="${iterState.count}"></td>
<td th:text="${iterState.even} ? '偶数' : '奇数'"></td>
<td th:text="${emp.name}"></td>
<td th:text="${emp.id}"></td>
<td th:text="${emp.name}"></td>
</tr>
</table>
效果:
2.4.4 条件判断
-
th:if作用在标签上,如果表达式成立,则该标签显示;不成立,则标签不显示。表达式成立的条件:
- 值非空且:
– boolean类型的值,且值为true
– 数字类型,且值不为0
– 字符类型,且不为’0’
– 字符串类型,且不为空,不为"false",“off”,“no”
– 如果不是布尔、数字、字符、字符串,也返回true
- 值非空且:
-
th:unless与th:if完全相反
<p th:if="${employee.id} == 1">工号为1时显示:老板好</p> <p th:unless="${employee.id} == 1">工号为其他时显示:你好</p>
th:unless="${employee.id} == 1"等价于th:if="not (${employee.id} == 1)"[注意优先级问题]
-
th:switch、th:case
<table border="1px">
<tr>
<td>序号</td>
<td>奇偶性</td>
<td>姓名</td>
<td>工号</td>
<td>薪资</td>
<td>职位</td>
</tr>
<tr th:each="emp,iterState : ${employees}">
<td th:text="${iterState.count}"></td>
<td th:text="${iterState.even} ? '偶数' : '奇数'"></td>
<td th:text="${emp.name}"></td>
<td th:text="${emp.id}"></td>
<td th:text="${emp.salary}"></td>
<div th:switch="${emp.id}">
<td th:case="1">老板</td>
<td th:case="2">经理</td>
<td th:case="*">普通职员</td>
</div>
</tr>
</table>
效果:
和th:if一样,满足条件的标签才会被显示,不满足条件会直接被剔除,这一点通过查看源代码不难发现:
2.4.5 片段声明与引入
准备工作:在templates/public目录下新创建public.html用于存放公共代码片段。
-
使用th:fragment声明公共片段
声明:<div th:fragment="public_fragment"> 这是th:fragment声明的公共片段 </div>
引入:
<div th:replace="public/public :: public_fragment"></div>
-
使用id选择器声明片段
<div id="public_id"> 这是用id选择器声明的片段 </div>
引入
<div th:replace="public/public :: #public_id"></div>
-
th:replace和th:insert区别
引入片段有两种方式:th:replace和th:insert。从字面意思不难看出一个是插入,一个是替换。<h1 th:insert="public/public :: #public_id"></h1> <h1 th:replace="public/public :: #public_id"></h1>
效果:
看一下源码:
可以看到,使用th:replace会将整个标签完全替换掉,而使用th:insert将保留使用时的标签。
2.4.6 th:object获取对象
th:object一般配合选择表达式来使用,前面在讲选择表达式的时候也说到了它的用法。