第5 章Spring Boot 的Web 开发 
学习目的与要求
本章首先介绍SpringBoot的Web开发支持,然后介绍Thymeleaf视图模板引擎技术,最
后介绍SpringBoot的Web开发技术(JSON 数据交互、文件的上传与下载、异常统一处理以
及对JSP的支持)。通过本章的学习,读者应该掌握SpringBoot的Web开发技术。 
本章主要内容
● Thymeleaf模板引擎
● 使用SpringBoot处理JSON 数据
● SpringBoot中文件的上传与下载
● SpringBoot的异常处理
● SpringBoot对JSP的支持
Web开发是一种基于B/S(即浏览器/服务器)架构的应用软件开发技术,分为前端(用户
接口)和后端(业务逻辑和数据)。前端的可视化及用户交互由浏览器实现,即以浏览器作为客
户端,实现客户与服务器远程的数据交互。SpringBoot的Web开发内容主要包括内嵌Servlet 
容器和SpringMVC。
5.1 SpringBoot的Web开发支持
SpringBoot提供了spring-boot-starter-web依赖模块,该依赖模块包含SpringBoot预定
义的Web开发常用依赖包,为Web开发者提供内嵌的Servlet容器(Tomcat)以及Spring 
MVC的依赖。如果开发者希望开发SpringBoot的Web应用程序,可以在SpringBoot项目
的pom.xml文件中添加如下依赖配置: 
<dependency> 
<groupId>org.springframework.boot</groupId> 
<artifactId>spring-boot-starter-web</artifactId> 
</dependency> 
SpringBoot将自动关联Web开发的相关依赖,如Tomcat、spring-webmvc等,进而对
Web开发提供支持,并实现相关技术的自动配置。
另外,开发者也可以使用IDEA 集成开发工具快速创建SpringInitializr,在NewProject 
对话框中添加SpringBoot的Web依赖。
5.2 Thymeleaf模板引擎
在SpringBoot的Web应用中,建议开发者使用HTML开发动态页面。SpringBoot提
供了许多模板引擎,主要包括FreeMarker、Groovy、Thymeleaf、Velocity和Mustache。因为
Thymeleaf提供了完美的Spring MVC 支持,所以在SpringBoot的Web应用中推荐使用
Thymeleaf作为模板引擎。

第5 章 Spring Boot 的Web 开发 
93 
Thymeleaf是一个Java类库,是一个XML/XHTML/HTML5的模板引擎,能够处理
HTML、XML、JavaScript以及CSS,可以作为MVC Web应用的View层显示数据。
.5.2.1 Spring Boot 的Thymeleaf 支持
在SpringBoot1.X版本中,spring-boot-starter-thymeleaf依赖包含了spring-boot-starter-web 
模块。但是,在Spring5 中,WebFlux 的出现对于Web 应用的解决方案不再唯一,所以
spring-boot-starter-thymeleaf依赖不再包含spring-boot-starter-web模块,需要开发者自己选
择spring-boot-starter-web模块依赖。下面通过一个实例讲解如何创建基于Thymeleaf模板
引擎的SpringBootWeb应用ch5_1。
【例5-1】 创建基于Thymeleaf模板引擎的SpringBootWeb应用ch5_1。
其具体实现步骤如下。
. 创建基于Thymeleaf模板引擎的SpringBootWeb应用ch5_1 
图5.1 基于Thymeleaf模板引擎的
SpringBootWeb应用ch5_1 
在IDEA 中选择File→New→Project命令,打开
NewProject对话框;在NewProject对话框中选择和输
入相关信息,然后单击Next按钮,打开新的界面;在新的
界面中选择Thymeleaf、Lombok和Spring Web依赖,单
击Create按钮,即可创建ch5_1应用。
. 打开项目目录
打开已经创建的基于Thymeleaf模板引擎的Spring 
BootWeb应用ch5_1,如图5.1所示。
Thymeleaf模板默认将JS脚本、CSS样式、图片等静
态文件放置在src/main/resources/static目录下,将视图
页面放在src/main/resources/templates目录下。
. 设置Web应用ch5_1的上下文路径
在ch5_1应用的application.properties文件中配置
如下内容: 
server.servlet.context-path=/ch5_1 
. 创建控制器类
创建一个名为com.ch.ch5_1.controller的包,并在该包中创建控制器类TestThymeleafController, 
具体代码如下: 
package com.ch.ch5_1.controller; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.GetMapping; 
@Controller 
public class TestThymeleafController { 
@GetMapping("/") 
public String test(){ 
//根据Thymeleaf 模板,默认返回src/main/resources/templates/index.html 
return "index"; 
} 
}

94 
. 新建index.html页面
在src/main/resources/templates目录下新建index.html页面,代码如下: 
<!DOCTYPE html> 
<html> 
<head> 
<meta charset="UTF-8"> 
<title>Insert title here</title> 
</head> 
<body> 
测试Spring Boot 的Thymeleaf 支持
</body> 
</html> 
. 运行
首先运行Ch51Application主类,然后访问“http://localhost:8080/ch5_1/”,运行结果如
图5.2所示。
图5.2 例5-1的运行结果
.5.2.2 Thymeleaf 的基础语法
. 引入Thymeleaf 
首先将View层页面文件的HTML标签修改如下: 
<html xmlns:th="http://www.thymeleaf.org"> 
然后在View层页面文件的其他标签中使用th:*动态处理页面,示例代码如下: 
<img th:src="'images/' + ${aBook.picture}"/> 
其中,${aBook.picture}获得数据对象aBook的picture属性。
. 输出内容
使用th:text和th:utext(不对文本转义,正常输出)将文本内容输出到所在标签的body 
中。如果在国际化资源文件messages_en_US.properties中有消息文本“test.myText= 
<strong>TestInternationalMessage</strong>”,那么在页面中可以使用如下两种方式获得
消息文本: 
<p th:text="#{test.myText}"></p> 
<!-- 对文本转义,即输出<strong>Test International Message</strong> --> 
<p th:utext="#{test.myText}"></p> 
<!-- 对文本不转义,即输出加粗的“Test International Message”--> 
. 基本表达式
1)变量表达式:${…} 
变量表达式用于访问容器上下文环境中的变量,示例代码如下: 
<span th:text="${information}"> 
2)选择变量表达式:*{…} 
选择变量表达式计算的是选定的对象(th:object属性绑定的对象),示例代码如下:

第5 章 Spring Boot 的Web 开发 
95 
<div th:object="${session.user}"> 
name: <span th: text="*{firstName}"></span><br> 
<!-- firstName 为user 对象的属性--> 
surname: <span th: text="*{lastName}"></span><br> 
nationality: <span th: text="*{nationality}"></span><br> 
</div> 
3)信息表达式:#{…} 
信息表达式一般用于显示页面静态文本,将可能需要根据需求整体变动的静态文本放在
properties文件中以便维护(如国际化),通常与th:text属性一起使用。示例代码如下: 
<p th:text="#{test.myText}"></p> 
. 引入URL 
Thymeleaf模板通过@{…}表达式引入URL,示例代码如下: 
<!-- 默认访问src/main/resources/static 下的css 文件夹--> 
<link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/> 
<!--访问相对路径--> 
<a th:href="@{/}">去看看</a> 
<!--访问绝对路径--> 
<a th:href="@{http://www.tup.tsinghua.edu.cn/index.html(param1='传参')}"> 去清华
大学出版社</a> 
<!-- 默认访问src/main/resources/static 下的images 文件夹--> 
<img th:src="'images/' + ${aBook.picture}"/> 
. 访问WebContext对象中的属性
Thymeleaf模板通过一些专门的表达式从模板的WebContext获取请求参数、请求、会话
和应用程序中的属性,具体如下。
● ${xxx}:返回存储在Thymeleaf模板上下文中的变量xxx或请求request作用域中
的属性xxx。
● ${param.xxx}:返回一个名为xxx的请求参数(可能是多个值)。
● ${session.xxx}:返回一个名为xxx的HttpSession作用域中的属性。
● ${application.xxx}:返回一个名为xxx的全局ServletContext上下文作用域中的
属性。
与EL表达式一样,使用${xxx}获得变量值,使用${对象变量名.属性名}获取JavaBean 
属性值。需要注意的是,${}表达式只能在th标签内部有效。
. 运算符
在Thymeleaf模板的表达式中可以使用+、-、*、/、%等各种算术运算符,也可以使用>、
<、<=、>=、==、!=等各种逻辑运算符。示例代码如下: 
<tr th:class="(${row}== 'even')? 'even': 'odd'">…</tr> 
. 条件判断
1)if和unless 
只有在th:if条件成立时才显示标签内容;th:unless与th:if相反,只有在条件不成立时
才显示标签内容。示例代码如下: 
<a href="success.html" th:if="${user != null}">成功</a> 
<a href="success.html" th:unless="${user = null}">成功</a>

96 
2)switch语句
Thymeleaf模板也支持多路选择switch语句结构,默认属性default可用“*”表示。示例
代码如下: 
<div th:switch="${user.role}"> 
<p th:case="'admin'">User is an administrator</p> 
<p th:case="'teacher'">User is a teacher</p> 
<p th:case="*">User is a student</p> 
</div> 
. 循环
1)基本循环
Thymeleaf模板使用th:each="obj,iterStat:${objList}"标签进行迭代循环,迭代对象可
以是java.util.List、java.util.Map或数组等。示例代码如下: 
<!-- 循环取出集合数据--> 
<div class="col-md-4 col-sm-6" th:each="book:${books}"> 
<a href=""> 
<img th:src="'images/' + ${book.picture}" alt="图书封面" style="height: 
180px; width: 40%;"/> 
</a> 
<div class="caption"> 
<h4 th:text="${book.bname}"></h4> 
<p th:text="${book.author}"></p> 
<p th:text="${book.isbn}"></p> 
<p th:text="${book.price}"></p> 
<p th:text="${book.publishing}"></p> 
</div> 
</div> 
2)循环状态的使用
在th:each标签中可以使用循环状态变量,该变量有如下属性。
● index:当前迭代对象的index(从0开始计数)。
● count:当前迭代对象的index(从1开始计数)。
● size:迭代对象的大小。
● current:当前迭代变量。
● even/odd:布尔值,当前循环是否为偶数/奇数(从0开始计数)。
● first:布尔值,当前循环是否为第一个。
● last:布尔值,当前循环是否为最后一个。
使用循环状态变量的示例代码如下: 
<!-- 循环取出集合数据--> 
<div class="col-md-4 col-sm-6" th:each="book,bookStat:${books}"> 
<a href=""> 
<img th:src="'images/' + ${book.picture}" alt="图书封面" style="height: 
180px; width: 40%;"/> 
</a> 
<div class="caption"> 
<!--循环状态bookStat--> 
<h3 th:text="${bookStat.count}"></h3> 
<h4 th:text="${book.bname}"></h4> 
<p th:text="${book.author}"></p>

第5 章 Spring Boot 的Web 开发 
97 
<p th:text="${book.isbn}"></p> 
<p th:text="${book.price}"></p> 
<p th:text="${book.publishing}"></p> 
</div> 
</div> 
. 内置对象
在实际Web项目开发中经常传递列表、日期等数据,所以Thymeleaf模板提供了很多内
置对象,可以通过# 直接访问。这些内置对象一般都以s结尾,如dates、lists、numbers、
strings等。Thymeleaf模板通过${#…}表达式访问内置对象,常见的内置对象如下。
● #dates:日期格式化的内置对象,操作的方法是java.util.Date类的方法。
● #calendars:类似于#dates,但操作的方法是java.util.Calendar类的方法。
● #numbers:数字格式化的内置对象。
● #strings:字符串格式化的内置对象,操作的方法参照java.lang.String。
● #objects:参照java.lang.Object。
● #bools:判断boolean类型的内置对象。
● #arrays:数组操作的内置对象。
● #lists:列表操作的内置对象,参照java.util.List。
● #sets:Set操作的内置对象,参照java.util.Set。
● #maps:Map操作的内置对象,参照java.util.Map。
● #aggregates:创建数组或集合的聚合的内置对象。
● #messages:在变量表达式内部获取外部消息的内置对象。
例如,有如下控制器方法: 
@GetMapping("/testObject") 
public String testObject(Model model) { 
//系统时间new Date() 
model.addAttribute("nowTime", new Date()); 
//系统日历对象 
model.addAttribute("nowCalendar", Calendar.getInstance()); 
//创建BigDecimal 对象 
BigDecimal money = new BigDecimal(2019.613); 
model.addAttribute("myMoney", money); 
//字符串 
String tsts = "Test strings"; 
model.addAttribute("str", tsts); 
//boolean 类型 
boolean b = false; 
model.addAttribute("bool", b); 
//数组(这里不能使用int 定义数组) 
Integer aint[]= {1,2,3,4,5}; 
model.addAttribute("mya", aint); 
//List 列表1 
List<String> nameList1 = new ArrayList<String>(); 
nameList1.add("陈恒1"); 
nameList1.add("陈恒3"); 
nameList1.add("陈恒2"); 
model.addAttribute("myList1", nameList1); 
//Set 集合 
Set<String> st = new HashSet<String>(); 
st.add("set1");

98 
st.add("set2"); 
model.addAttribute("mySet", st); 
//Map 集合 
Map<String, Object> map = new HashMap<String, Object>(); 
map.put("key1", "value1"); 
map.put("key2", "value2"); 
model.addAttribute("myMap", map); 
//List 列表2 
List<String> nameList2 = new ArrayList<String>(); 
nameList2.add("陈恒6"); 
nameList2.add("陈恒5"); 
nameList2.add("陈恒4"); 
model.addAttribute("myList2", nameList2); 
return "showObject"; 
}
那么,可以在src/main/resources/templates/showObject.html视图页面文件中使用内置
对象操作数据。showObject.html的代码如下: 
<!DOCTYPE html> 
<html xmlns:th="http://www.thymeleaf.org"> 
<head> 
<meta charset="UTF-8"> 
<title>Insert title here</title> 
</head> 
<body> 
格式化控制器传递过来的系统时间nowTime: 
<span th:text="${#dates.format(nowTime, 'yyyy/MM/dd')}"></span> 
<br> 
创建一个日期对象: 
<span th:text="${#dates.create(2019,6,13)}"></span> 
<br> 
格式化控制器传递过来的系统日历nowCalendar: 
<span th:text="${#calendars.format(nowCalendar, 'yyyy-MM-dd')}"></span> 
<br> 
格式化控制器传递过来的BigDecimal 对象myMoney: 
<span th:text="${#numbers.formatInteger(myMoney,3)}"></span> 
<br> 
计算控制器传递过来的字符串str 的长度: 
<span th:text="${#strings.length(str)}"></span> 
<br> 
返回对象,当控制器传递过来的BigDecimal 对象myMoney 为空时返回默认值9999: 
<span th:text="${#objects.nullSafe(myMoney, 9999)}"></span> 
<br> 
判断boolean 数据是否为false: 
<span th:text="${#bools.isFalse(bool)}"></span> 
<br> 
判断数组mya 中是否包含元素5: 
<span th:text="${#arrays.contains(mya, 5)}"></span> 
<br> 
排序列表myList1 的数据: 
<span th:text="${#lists.sort(myList1)}"></span> 
<br> 
判断集合mySet 中是否包含元素set2: 
<span th:text="${#sets.contains(mySet, 'set2')}"></span> 
<br> 
判断myMap 中是否包含key1 关键字: 
<span th:text="${#maps.containsKey(myMap, 'key1')}"></span>

第5 章 Spring Boot 的Web 开发 
99 
<br> 
将数组mya 中的元素求和: 
<span th:text="${#aggregates.sum(mya)}"></span> 
<br> 
将数组mya 中的元素求平均: 
<span th:text="${#aggregates.avg(mya)}"></span> 
<br> 
如果未找到消息,则返回默认消息(如“??msgKey_zh_CN??”): 
<span th:text="${#messages.msg('msgKey')}"></span> 
</body> 
</html> 
.5.2.3 Thymeleaf 的常用属性
通过5.2.2节的学习,发现Thymeleaf语法都是通过在HTML页面的标签中添加th:xxx 
关键字来实现模板套用,且其属性与HTML 页面标签基本类似。Thymeleaf的常用属性
如下。.
th:action 
th:action用于定义后台控制器路径,类似<form>标签的action属性。示例代码如下: 
<form th:action="@{/login}">…</form> 
.th:each 
th:each用于集合对象的遍历,功能类似JSTL标签<c:forEach>。示例代码如下: 
<div class="col-md-4 col-sm-6" th:each="gtype:${gtypes}"> 
<div class="caption"> 
<p th:text="${gtype.id}"></p> 
<p th:text="${gtype.typename}"></p> 
</div> 
</div> 
.th:field 
th:field用于表单参数的绑定,通常与th:object一起使用。示例代码如下: 
<form th:action="@{/login}" th:object="${user}"> 
<input type="text" value="" th:field="*{username}"></input> 
<input type="text" value="" th:field="*{role}"></input> 
</form> 
.th:href 
th:href用于定义超链接,类似<a>标签的href属性。其value形式为@{/logout}。示例
代码如下: 
<a th:href="@{/gogo}"></a> 
.th:id 
th:id用于div的id声明,类似HTML标签中的id属性。示例代码如下: 
<div th:id ="stu+(${rowStat.index}+1)"></div> 
.th:if 
th:if用于条件判断,如果为否,则标签不显示。示例代码如下: 
<div th:if="${rowStat.index} == 0">…do something…</div> 
扫一扫
视频讲解

100 
.th:fragment 
th:fragment用于声明定义该属性的div为模板片段,常用于头文件、尾文件的引入,通常
与th:include、th:replace一起使用。
例如,在ch5_1应用的src/main/resources/templates目录下声明模板片段文件footer. 
html,代码如下: 
<!DOCTYPE html> 
<html xmlns:th="http://www.thymeleaf.org"> 
<head> 
<meta charset="UTF-8"> 
<title>Insert title here</title> 
</head> 
<body> 
<!-- 声明片段content --> 
<div th:fragment="content" > 
主体内容 
</div> 
<!-- 声明片段copy --> 
<div th:fragment="copy" > 
.清华大学出版社 
</div> 
</body> 
</html> 
那么,可以在ch5_1应用的src/main/resources/templates/index.html文件中引入模板片
段,代码如下: 
<!DOCTYPE html> 
<html xmlns:th="http://www.thymeleaf.org"> 
<head> 
<meta charset="UTF-8"> 
<title>Insert title here</title> 
</head> 
<body> 
测试Spring Boot 的Thymeleaf 支持<br> 
引入主体内容模板片段: 
<div th:include="footer::content"></div> 
引入版权所有模板片段: 
<div th:replace="footer::copy" ></div> 
</body> 
</html> 
.th:object 
th:object用于表单数据对象的绑定,将表单绑定到后台controller的一个JavaBean参
数,通常与th:field一起使用。下面通过一个实例讲解表单提交及数据绑定的实现过程。
【例5-2】 表单提交及数据绑定的实现过程。
其具体实现步骤如下。
1)创建实体类
在Web应用ch5_1的src/main/java目录下创建com.ch.ch5_1.model包,并在该包中创
建实体类LoginBean,代码如下: 
package com.ch.ch5_1.model; 
import lombok.Data; 
@Data

第5 章 Spring Boot 的Web 开发
101 
public class LoginBean { 
String uname; 
String urole; 
} 
2)创建控制器类
在Web应用ch5_1的com.ch.ch5_1.controller包中创建控制器类LoginController,代码
如下: 
package com.ch.ch5_1.controller; 
import org.springframework.stereotype.Controller; 
import org.springframework.ui.Model; 
import org.springframework.web.bind.annotation.GetMapping; 
import org.springframework.web.bind.annotation.ModelAttribute; 
import org.springframework.web.bind.annotation.PostMapping; 
import com.ch.ch5_1.model.LoginBean; 
@Controller 
public class LoginController { 
@GetMapping("/toLogin") 
public String toLogin(Model model) { 
/*loginBean 与login.html 页面中的th:object="${loginBean}"相同*/ 
model.addAttribute("loginBean", new LoginBean()); 
return "login"; 
} 
@PostMapping("/login") 
public String greetingSubmit(@ModelAttribute LoginBean loginBean) { 
/*@ModelAttribute LoginBean loginBean 接收login.html 页面中的表单数据,并将
loginBean 对象保存到model 中返回给result.html 页面显示。*/ 
System.out.println("测试提交的数据: " + loginBean.getUname()); 
return "result"; 
} 
} 
3)创建页面表示层
在Web应用ch5_1 的src/main/resources/templates目录下创建页面login.html和
result.html。
login.html页面的代码如下: 
<!DOCTYPE html> 
<html xmlns:th="http://www.thymeleaf.org"> 
<head> 
<meta charset="UTF-8"> 
<title>Insert title here</title> 
</head> 
<body> 
<h1>Form</h1> 
<form action="#" th:action="@{/login}" th:object="${loginBean}" method="post"> 
<!--th:field="*{uname}"的uname 与实体类的属性相同,即绑定loginBean 对象--> 
<p>Uname: <input type="text" th:field="*{uname}" th:placeholder="请输入
用户名"/></p> 
<p>Urole: <input type="text" th:field="*{urole}" th:placeholder="请输入
角色"/></p> 
<p><input type="submit" value="Submit"/> <input type="reset" value= 
"Reset"/></p> 
</form> 
</body> 
</html>