Spring Boot****整合视图层技术*
这一节我们主要学习如何整合视图层技术:
Jsp
Freemarker
Thymeleaf
在之前的案例中,我们都是通过 @RestController
来处理请求,所以返回的内容为json对象。那么如果需要渲染html页面的时候,要如何实现呢?
Spring Boot推荐使用模板引擎
模板引擎实现伪html 达到seo优化 使动态页面静态化
在动态html上实现Spring Boot依然可以完美胜任,并且提供了多种模板引擎的默认配置支持,所以在推荐的模板引擎下,我们可以很快的上手开发动态网站。
Spring Boot提供了默认配置的模板引擎主要有以下几种:
- Thymeleaf
- FreeMarker
- Velocity
- Groovy
- Mustache
Spring Boot建议使用这些模板引擎,避免使用jsp。
1.Jsp
创建项目
创建 war 项目,编写pom.xml
UTF-8
1.8
1.8
org.springframework.boot
spring-boot-starter-parent
2.1.6.RELEASE
org.springframework.boot
spring-boot-starter-web
视图解析器
resources/application.properties
#配置视图解析器
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
实体类
User.java
public class User implements Serializable {
private Integer id;
private String username;
private Integer age;
public User() {
}
public User(Integer id, String username, Integer age) {
this.id = id;
this.username = username;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", age=" + age +
'}';
}
控制层
UserController.java
@Controller
public class UserController {
@RequestMapping("/showUser")
public String showUser(Model model) {
List list = new ArrayList<>();
list.add(new User(1, "张三", 18));
list.add(new User(2, "李四", 20));
list.add(new User(3, "王五", 22));
model.addAttribute("list", list);
// 跳转视图
return "userList";
}
}
视图层
userList.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
用户展示
ID
Name
Age
${user.id}
${user.username}
${user.age}
启动类
App.java
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
结果
2.Freemarker
创建项目
创建 war 项目,编写pom.xml
org.springframework.boot
spring-boot-starter-parent
2.1.7.RELEASE
org.springframework.boot
spring-boot-starter-web
视图解析器
该文件内容不一定非得需要,可以不添加,不添加使用默认值。
resources/application.properties
实体类
User.java
同上user实体类
控制层
UserController.java
@Controller
public class UserController {
@RequestMapping("/showUser")
public String showUser(Model model) {
List list = new ArrayList<>();
list.add(new User(1, "张三", 18));
list.add(new User(2, "李四", 20));
list.add(new User(3, "王五", 22));
model.addAttribute("list", list);
// 跳转视图
return "userList";
}
}
视图层
Spring Boot要求模板形式的视图层技术的文件必须要放到 src/main/resources 目录下的 templates 目录。
该目录内的模板文件名称必须是 ftl 的后缀结尾。
userList.ftl
用户展示
用户信息
ID
Name
Age
<#list list as user >
${user.id}
${user.username}
${user.age}
#list>
启动类
App.java
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
结果
3.Thymeleaf (重点讲解)
入门案例
创建项目
创建 war 项目,编写pom.xml
org.springframework.boot
spring-boot-starter-thymeleaf
控制层
ThymeleafController.java
@Controller
public class ThymeleafController {
@RequestMapping("/show")
public String show(Model model) {
model.addAttribute("message", "Thymeleaf 入门");
return "msg";
}
}
视图层
Spring Boot要求模板形式的视图层技术的文件必须要放到 src/main/resources 目录下的 templates 目录。
msg.html
Thymeleaf入门
启动类
App.java
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
运行结果出现异常(如果是 Spring Boot 1.X.X 版本才会出现此异常):
1 org.xml.sax.SAXParseException: 元素类型 "meta" 必须由匹配的结束标记 "" 终止。
异常处理
如果是 Spring Boot 1.X.X 版本需要以下操作,本案例是 Spring Boot 2.1.6 版本,所以不需要更改。
方式一:编写风格严谨的HTML代码
1
**方式二:更换hymeleaf的****jar包版本
thymeleaf.jar:更新为 3.0 以上的版本
3.0.11.RELEASE
运行结果
Thymeleaf 语法详解
Thymeleaf 内置对象语法:
- 调用内置对象一定要用 #
- 大部分的内置对象都以 s 结尾 strings 、 numbers 、 dates
准备数据
实体类
User.java
public class User implements Serializable {
private Integer id;
private String username;
private Integer age;
public User() {
}
public User(Integer id, String username, Integer age) {
this.id = id;
this.username = username;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", age=" + age +
'}';
}
}
控制层
ThymeleafController.java
@Controller
public class ThymeleafController {
@RequestMapping("/show")
public String showMsg(Model model,
HttpServletRequest request,
HttpServletResponse response) {
// 字符串
model.addAttribute("msg", "Thymeleaf 入门案例");
// 日期时间
model.addAttribute("myDate", new Date());
// 条件判断if
model.addAttribute("sex", 1);
// 条件判断switch
model.addAttribute("id", 1);
// 对象
model.addAttribute("user", new User(1, "张三", 20));
// 迭代遍历list
List userList = new ArrayList<>();
userList.add(new User(1, "张三", 20));
userList.add(new User(2, "李四", 22));
userList.add(new User(3, "王五", 24));
model.addAttribute("userList", userList);
// 迭代遍历map
Map userMap = new HashMap<>();
userMap.put("u1", new User(1, "张三", 20));
userMap.put("u2", new User(2, "李四", 22));
userMap.put("u3", new User(3, "王五", 24));
model.addAttribute("userMap", userMap);
// 域对象操作
request.setAttribute("req", "HttpServletRequest");
request.getSession().setAttribute("sess", "HttpSession");
request.getSession().getServletContext().setAttribute("app", "Application");
return "msg";
}
/**
* URL表达式-相对路径
* @return
*/
@RequestMapping("/index")
public String index() {
return "index";
}
/**
* URL表达式-普通传参
* @param id
* @param username
* @return
*/
@RequestMapping("/user")
public String user(Integer id, String username) {
System.out.println("id:" + id + " username:" + username);
return "user";
}
/**
* URL表达式-restful传参
* @param id
* @param username
* @return
*/
@RequestMapping("/person/{id}/{username}")
public String person(@PathVariable Integer id, @PathVariable String username) {
System.out.println("id:" + id + " username:" + username);
return "person";
}
}
视图层
index.html
Thymeleaf
index
user.html
Thymeleaf
user
person.html
Thymeleaf
person
字符串
字符串操作
${#strings.isEmpty(key)} :判断字符串是否为空
${#strings.contains(msg, 'T')} :判断字符串是否包含子串
${#strings.startsWith(msg, 'T')} :判断字符串是否以子串开头
${#strings.endsWith(msg, 'T')} :判断字符串是否以子串结尾
``
${#strings.length(msg)} :返回字符串的长度
``
${#strings.indexOf(msg, 'T')} :查找子串的位置,并返回该子串的下标,如果没找到则返回-1
${#strings.substring(msg, 5)} :截取子串,从指定下标开始截止到末尾结束
${#strings.substring(msg, 5, 12)} :截取子串,从指定下标开始截止到指定下标结束
``
${#strings.toUpperCase(msg)} :将字符串转大写
${#strings.toLowerCase(msg)} :将字符串转小写
日期时间
${#dates.format(key)} :格式化日期,以浏览器默认语言为格式化标准
${#dates.format(key,'yyy/MM/dd')} :自定义格式日期转换
${#dates.year(myDate)} :获取年份,还可以获取月份、日、时、分、秒
条件判断
th:if :单选择
性别:男
女
th:switch :多选择(如果要实现 if else if else 判断表达式,在 Thymeleaf 要使用 th:switch 代替)
编号:
1 张三
2 李四
3 王五
对象
迭代遍历
th:each :迭代遍历
th:each :状态变量属性
- index:当前迭代器的索引 从 0 开始
- count:当前迭代对象的计数 从 1 开始
- size:被迭代对象的长度
- even/odd:布尔值,当前循环是否是偶数/奇数 从 0 开始
- first:布尔值,当前循环的是否是第一条,如果是返回 true 否则返回 false
- last:布尔值,当前循环的是否是最后一条,如果是则返回 true 否则返回 false
th:each :迭代 Map
Id
Name
Age
Id
Name
Age
域对象操作
${#httpServletRequest.getAttribute(key)} :HttpServletRequest
``
${session.key} :HttpSession
``
${application.key} :ServletContext
URL****表达式
基本语法
URL表达式的基本语法: @{}
th:href :绝对路径
绝对路径
th:href :相对路径,相对于当前项目的根路径
相对于当前项目的根路径
th:href :相对路径, 相对于服务器的根路径
相对于服务器的根路径
参数传递
相对路径-普通传参
相对路径-restful传参
相对路径-restful传参
Spring Boot****整合持久层技术
1.MyBatis
创建项目
创建 jar项目,编写pom.xml
org.springframework.boot
spring-boot-starter-parent
2.1.7.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-thymeleaf
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.1.0
mysql
mysql-connector-java
com.alibaba
druid
1.1.19
src/main/resources
src/main/java
**/*.xml
**/*.properties
**/*.tld
false
properties 配置文件
添加 application.properties 全局配置文件
# 配置数据库驱动
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=shsxt
# 配置数据库连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# 配置mybatis数据返回类型别名(默认别名是类名)
mybatis.type-aliases-package=com.springboot.pojo
# 配置mybatis.Maper映射文件
mybatis.mapper-locations=classpath:com/springboot/mapper/*.xml
SQL****文件
创建一个test表,字段id,name ,age;
CREATE TABLE `user` (
`id` INT (11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR (255) DEFAULT NULL,
`age` INT (11) DEFAULT NULL,
5PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
1.1添加用户
实体类
User.java
public class User implements Serializable {
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
Mapper 接口
UserMapper.java
public interface UserMapper {
// 添加用户
int insertUser(User user);
// 查询用户
List selectUserList();
// 根据主键查询用户
User selectUserById(Integer id);
// 修改用户
int updateUser(User user);
// 删除用户
int deleteUser(Integer id);
}
映射配置文件
UserMapper.xml
insert into user (name, age) values (#{name}, #{age})
update user set name = #{name}, age = #{age} where id = #{id};
delete from user where id = #{id}
业务层
UserServiceI.java
public interface UserServiceI {
int insertUser(User user);
// 查询用户
List selectUserList();
// 根据主键查询用户
User selectUserById(Integer id);
// 修改用户
int updateUser(User user);
// 删除用户
int deleteUser(Integer id);
}
UserServiceImpl.java
@Service
@Transactional
public class UserServiceImpl implements UserServiceI {
@Autowired
private UserMapper userMapper;
@Override
public int insertUser(User user) {
return userMapper.insertUser(user);
}
@Override
public List selectUserList() {
return userMapper.selectUserList();
}
@Override
public User selectUserById(Integer id) {
return userMapper.selectUserById(id);
}
@Override
public int updateUser(User user) {
return userMapper.updateUser(user);
}
@Override
public int deleteUser(Integer id) {
return userMapper.deleteUser(id);
}
}
控制层
UserController.java
/**
* 操作持久层
*/
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserServiceI userService;
/**
* 页面跳转
*/
@RequestMapping("/{page}")
public String page(@PathVariable String page){
return page;
}
/**
* 添加用户
*/
@PostMapping("/insertUser")
public String insertUser(User user){
int result =userService.insertUser(user);
return "success";
}
/**
* 查询用户列表
*/
@GetMapping("/selectUserList")
public String selectUserList(Model model) {
model.addAttribute("userList", userService.selectUserList());
return "user-list";
}
/**
* 根据主键查询
*/
@GetMapping("/edit/{id}")
public String edit(@PathVariable Integer id, Model model) {
model.addAttribute("user", userService.selectUserById(id));
return "updateUser";
}
/**
* 修改用户保存
* 修改之前需要先根据ID查询
*/
@PostMapping("/updateUser")
public String updateUser(User user) {
userService.updateUser(user);
return "success";
}
/**
* 删除用户
*/
@GetMapping("/deleteUser/{id}")
public String deleteUser(@PathVariable Integer id) {
userService.deleteUser(id);
return "success";
}
}
视图层
templates/register.html
注册用户
templates/success.html
提示
成功
启动类
App.java
@SpringBootApplication
@MapperScan("com.springboot.mapper")
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
结果
页面请求
数据库
1.2查询用户
视图层
templates/user-list.html
用户列表
ID
NAME
AGE
操作
修改
删除
结果
查询所有用户
根据主键查询用户
1.3修改用户
视图层
templates/updateUser.html
修改用户
结果
查询用户,并填写修改信息
数据库
1.4删除用户
结果
查询用户列表
数据库
Spring Boot****服务端数据校验
项目中使用较多的是前端的校验,比如页面中js校验。对于安全较高的建议在服务端进行校验。
校验框架
Spring 使用 Hibernate 的校验框架 validator,页面提交的请求参数,请求到 controller 方法中,使用 validator 进行校验,如果校验不通过,将错误信息展示到页面。
主要使用到以下相关 jar 包(蓝色部分)
1.数据校验
创建项目
使用 IntelliJ IDEA 2019.1 直接创建Spring Boot项目
目前最新的稳定版 Spring Boot 是2.1.7,我们课程中使用的2.1.6,所以需要修改一下pom.xml的版本信息。在创建项目时勾选 Web 和 Themeleaf 组件。
pom.xml
org.springframework.boot
spring-boot-starter-parent
2.1.7.RELEASE
com.springboot
springboot-validator
0.0.1-SNAPSHOT
springboot-validator
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
编写添加用户功能
实体类
User.java
package com.springboot.pojo;
import java.io.Serializable;
public class User implements Serializable {
private Integer id;
private String username;
private String password;
private Integer age;
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
控制层
UserController.java
@Controller
@RequestMapping("/user")
public class UserController {
/**
* 页面跳转
* *
@param page
* @return
*/
@RequestMapping("/{page}")
public String page(@PathVariable String page) {
return page;
} /
**
* 添加用户
* *
@param user
* @param model
* @return
*/
@PostMapping("/insertUser")
public String insertUser(User user, Model model) {
model.addAttribute("user", user);
return "success";
}
}
视图层
templates/register.html
用戶注冊頁面
添加用户
templates/success.html
注册成功页面,返回user
提示
启动类
App.java
准备好项目后,启动项目测试无误,准备开始数据校验.
2.数据校验步骤
实体类添加校验规则
User.java
public class User implements Serializable {
private Integer id;
@NotEmpty(message = "用户名不可以为空", groups = {ValidateGroupForName.class, ValidateGroupForAll.class})// 非空校验不会去除前后空格
private String username;
@NotBlank(message = "密码不可以为空", groups = {ValidateGroupForAll.class})// 非空校验会去除前后空格
@Size(message = "且长度在6 ~ 10之间", min = 6, max = 10, groups = {ValidateGroupForAll.class})// 长度必须在6~10之间
private String password;
// 校验是否为数字,并且在规定的大小值内
@Max(message = "年龄必须在1 ~ 150岁之间", value = 150, groups = {ValidateGroupForAll.class})// 必须小于等于指定的值
@Min(message = "年龄必须在1 ~ 150岁之间", value = 1, groups = {ValidateGroupForAll.class})// 必须大于等于指定的值
private Integer age;
// 校验邮箱格式,默认正则匹配规则是 ".*"
@Email(message = "请输入正确的邮箱格式:[email protected]", regexp = "[a-za-z0-9._%+-]+@[a-za-z0-9.-]+\\.[a-za-z]{2,4}",
groups = {ValidateGroupForAll.class})
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
控制层开启校验
UserController.java
@Controller
@RequestMapping("/user")
public class UserController {
/**
* 页面跳转
*
* @param page
* @return
*/
@RequestMapping("/{page}")
public String page(@PathVariable String page, User user) {
return page;
}
/**
* 添加用户
*
* @param user @Validated:表示对哪个参数进行校验
* BindingResult:接收校验提示信息
* @Validated 和 BindingResult 是配对出现,并且形参顺序是固定的
* @param model
* @return
*/
@PostMapping("/insertUser")
public String insertUser(@Validated(value = {ValidateGroupForAll.class}) User user, BindingResult bindingResult, Model model) {
model.addAttribute("user", user);
if (bindingResult.hasErrors()) {
List errors = bindingResult.getAllErrors();
for (ObjectError error: errors) {
System.out.println(error.getDefaultMessage());// 错误信息
}
}
return bindingResult.hasErrors() ? "register" : "success";
}
/**
* 用户登录。只校验用户名
* @param user
* @param bindingResult
* @return
*/
@PostMapping("/login")
public String login(@Validated(value = {ValidateGroupForName.class}) User user, BindingResult bindingResult) {
return bindingResult.hasErrors() ? "login" : "success";
}
}
视图层获取提示信息
templates/register.html
添加用户
*运行发生异常
org.thymeleaf.exceptions.TemplateProcessingException: Error during
execution of processor
'org.thymeleaf.spring5.processor.SpringErrorsTagProcessor' (template:
"register" - line 10, col 27)
at
org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(Ab
stractAttributeTagProcessor.java:117) ~[thymeleaf-
3.0.11.RELEASE.jar:3.0.11.RELEASE]
解决数据校验异常
方法一
在跳转页面的方法中注入一个对象,要求对象参数的变量名必须是类名的全称且首字母小写(小驼峰)。
UserController.java
@Controller
@RequestMapping("/user")
public class UserController {
/**
* 页面跳转
* *
@param page
* @return
*/
@RequestMapping("/{page}")
public String page(@PathVariable String page, User user) {
return page;
} /
**
* 添加用户
* *
@param user @Validated:表示对哪个参数进行校验
* BindingResult:接收校验提示信息
* @Validated 和 BindingResult 是配对出现,并且形参顺序是固定的
* @param model
* @return
*/
@PostMapping("/insertUser")
public String insertUser(@Validated User user, BindingResult
bindingResult, Model model) {
return bindingResult.hasErrors() ? "register" : "success";
}
}
然后页面使用该变量名获取校验提示信息
templates/register.html
添加用户
方法二
使用 @ModelAttribute 注解,并且可以改变页面获取校验信息参数名称。
UserController.java
@Controller
@RequestMapping("/user")
public class UserController {
/**
* 页面跳转
* *
@param page
* @return
*/
@RequestMapping("/{page}")
public String page(@PathVariable String page, @ModelAttribute("u") User
user) {
return page;
} /
**
* 添加用户
* *
@param user @Validated:表示对哪个参数进行校验
* BindingResult:接收校验提示信息
* @Validated 和 BindingResult 是配对出现,并且形参顺序是固定的
* @param model
* @return
*/
@PostMapping("/insertUser")
public String insertUser(@ModelAttribute("u") @Validated User user,
BindingResult bindingResult, Model model) {
return bindingResult.hasErrors() ? "register" : "success";
}
}
页面使用 u 获取校验提示信息
templates/register.html
添加用户
运行结果
自定义校验提示信息
我们可以通过 message 属性来自定义校验提示信息。
结果
3.分组校验
当需要不同的方法对同一个对象进行校验,但是每个方法需要不同的校验时,需要将校验规则分组。
定义校验分组
ValidateGroupForName.java
package com.springboot.group;
public interface ValidateGroupForName {
// 接口中不需要定义任何方法,仅是对不同的校验规则进行分组
// 此分组只校验姓名
}
ValidateGroupForAll.java
package com.springboot.group;
public interface ValidateGroupForAll {
// 接口中不需要定义任何方法,仅是对不同的校验规则进行分组
// 此分组校验全部
}
User.java
通过注解配置添加分组
每个方法使用不同的校验分组
此时,添加用户方法根据 ValidateGroupForName 分组校验,只会校验用户名,结果如下。
Spring Boot****异常处理
Spring Boot对于异常处理提供了五种处理方式。
创建Spring Boot项目,选择 Web 和 Thymeleaf 组件。
1.默认
[图片上传失败...(image-d5c852-1566671305409)]
Spring Boot默认的处理异常的机制是:一旦程序中出现了异常Spring Boot会向 /error 的 url 发送请求。在Spring Boot中提供了一个叫 BasicExceptionController 来处理 /error 请求,然后跳转到默认显示异常的页面来展示异常信息。
如果我们需要将所有的异常统一跳转到错误页面显式,需要在 src/main/resources/templates 目录下创建error.html 页面,
Spring Boot会自动找到该页面作为错误页面。注意:名称必须叫 error
Spring Boot错误视图提供了以下错误属性:
- timestamp:错误发生时间;
- status:HTTP状态码;
- error:错误原因;
- exception:异常的类名;
- message:异常消息(如果这个错误是由异常引起的);
- errors:BindingResult异常里的各种错误(如果这个错误是由异常引起的);
- trace:异常跟踪信息(如果这个错误是由异常引起的);
- path:错误发生时请求的URL路径。
Spring Boot使用的前端框架模板不同,页面的名称也有所不同:
实现Spring的View接口的Bean,其ID需要设置为error(由Spring的BeanNameViewResolver所解析);
- 如果配置了Thymeleaf,则需命名为error.html的Thymeleaf模板;
- 如果配置了FreeMarker,则需命名为error.ftl的FreeMarker模板;
- 如果配置了Velocity,则需命名为error.vm的Velocity模板;
- 如果是用JSP视图,则需命名为error.jsp的JSP模板。
Thymeleaf 案例如下:
HelloController.java
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
Integer.parseInt("d");
return "hello";
}
}
编写错误跳转页面error.html
错误提示
error页面
亲,出错了,请稍后再试!
错误发生时间:
HTTP状态码:
错误原因:
异常的类名:
异常消息:
BindingResult异常里的各种错误:
异常跟踪信息:
错误发生时请求的URL路径:
结果
1@ExceptionHandle****注解
@ExceptionHandle 注解可以根据 value 属性声明需要捕获哪些异常,然后声明一个自定义方法, Spring Boot会将产生的异常对象注入到方法中供我们使用,该方法需要返回 ModelAndView 封装异常信息,指定跳转视图。
案例如下:
UserController.java
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/show")
public String show() {
Integer.parseInt("D");
return "index";
}
@RequestMapping("/info")
public String info() {
int i = 2 / 0;
return "index";
} /
**
* @ExceptionHandler(value = {}):捕获哪些异常
* ModelAndView:封装异常信息,指定跳转视图
* Exception ex:将产生的异常对象注入到方法中
* @param ex
* @return
*/
@ExceptionHandler(value = {NumberFormatException.class})
public ModelAndView numberFormatExceptionHandler(Exception ex){
ModelAndView mv = new ModelAndView();
mv.addObject("error", ex.toString());
mv.setViewName("numberFormatException");
return mv;
} /
**
* @ExceptionHandler(value = {}):捕获哪些异常
* ModelAndView:封装异常信息,指定跳转视图
* Exception ex:将产生的异常对象注入到方法中
* @param ex
* @return
*/
@ExceptionHandler(value = {ArithmeticException.class})
public ModelAndView arithmeticExceptionHandler(Exception ex){
ModelAndView mv = new ModelAndView();
mv.addObject("error", ex.toString());
mv.setViewName("arithmeticException");
return mv;
}
}
numberFormatException.html
数字转换异常跳转页面
类型转换异常
arithmeticException.html
运算异常跳转页面
算数运算异常
结果:
2.@ControllerAdvice****注解
@ExceptionHandle 注解单独使用时需要借助自定义方法实现,如果多个类中都会有相同的异常出现,那么就需要声明多次,造成代码冗余,此时我们可以借助 @ControllerAdvice 注解实现代码重复利用
。
通过 @ControllerAdvice 注解声明一个全局异常处理类,然后配合 @ExceptionHandle 注解实现异常处理。
[图片上传失败...(image-4151fd-1566671305409)]
案例如下:
GlobalException.java
/**
* 全局异常处理类
*/
@ControllerAdvice
public class GlobalException {
/**
* @ExceptionHandler(value = {}):捕获哪些异常
* ModelAndView:封装异常信息,指定跳转视图
* Exception ex:将产生的异常对象注入到方法中
* @param ex
* @return
*/
@ExceptionHandler(value = {NumberFormatException.class})
public ModelAndView numberFormatExceptionHandler(Exception ex){
ModelAndView mv = new ModelAndView();
mv.addObject("error", ex.toString());
mv.setViewName("numberFormatException");
return mv;
} /
**
* @ExceptionHandler(value = {}):捕获哪些异常
* ModelAndView:封装异常信息,指定跳转视图
* Exception ex:将产生的异常对象注入到方法中
* @param ex
* @return
*/
@ExceptionHandler(value = {ArithmeticException.class})
public ModelAndView arithmeticExceptionHandler(Exception ex){
ModelAndView mv = new ModelAndView();
mv.addObject("error", ex.toString());
mv.setViewName("arithmeticException");
return mv;
}
}
numberFormatException.html
arithmeticException.html
将UserController中的捕获异常声明删除
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/show")
public String show() {
Integer.parseInt("D");
return "index";
}
@RequestMapping("/info")
public String info() {
int i = 2 / 0;
return "index";
}
}
结果:
3.SimpleMappingExceptionResolver
我们可以在全局异常处理类中使用 @Bean 注解注入 SimpleMappingExceptionResolver 实现异常处理。
这种对异常的处理只是定义了异常与视图映射的信息
,无法给页面返回太多异常信息。
案例如下:
GlobalException.java
/**
* 全局异常处理类
*/
@Configuration
public class GlobalException {
@Bean
public SimpleMappingExceptionResolver
getSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver resolver = new
SimpleMappingExceptionResolver();
Properties mappings = new Properties();
/**
* 参数一:异常的类型,注意必须是异常类型的全名
* 参数二:视图名称
*/
mappings.put(ArithmeticException.class.getSimpleName(),
"arithmeticException");
mappings.put(NumberFormatException.class.getSimpleName(),
"numberFormatException");
// 设置异常与视图映射信息
resolver.setExceptionMappings(mappings);
return resolver;
}
}
numberFormatException.html
arithmeticException.html
结果:
4.HandlerExceptionResolver
我们可以在全局异常处理类中实现 HandlerExceptionResolver 接口实现异常处理。
案例如下:
GlobalException.java
/**
* 全局异常处理类
*/
@Configuration
public class GlobalException implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
ModelAndView mv = new ModelAndView();
// 判断异常类型,做不同视图跳转
if(ex instanceof ArithmeticException){
mv.setViewName("arithmeticException");
}
if(ex instanceof NumberFormatException){
mv.setViewName("numberFormatException");
}
mv.addObject("error", ex.toString());
return mv;
}
numberFormatException.html
arithmeticException.html
结果: