springboot系列十三: 异常处理
springboot异常处理
- 基本介绍
- 拦截器VS过滤器
- 默认异常页面
- 应用实例
- debug取出状态码和错误信息
- 全局异常
- 基本说明
- 应用实例
- Debug处理流程
- 自定义异常
- 基本说明
- 应用实例
- Debug处理流程
- 注意事项和细节
基本介绍
1.默认情况下,SpringBoot 提供 /error 处理所有错误的映射,当出现错误时,SpringBoot 底层会请求转发到 /error 这个映射。
2.浏览器访问不存在的接口 http://localhost:8084/abc,响应 whitelabel 的错误试图,以 HTML 格式呈现给用户。
注意: 配置登录拦截器时,先登录, 再测试
3.SpringBoot底层默认由 DefaultErrorViewResolver 处理错误。debug->
默认找类路径下的 /error/404.html页面
不管你请求什么, 都去找类路径下的 /error/4xx.html页面
拦截器VS过滤器
1.使用范围不同.
1)过滤器 实现的是 javax.servlet.Filter
接口, 而这个接口是在Servlet
规范中定义的, 也就是说过滤器 Filter
的使用要依赖于 Tomcat
等容器, Filter
只能在 web
程序中使用.
2)拦截器(Interceptor
) 它是一个 Spring
组件, 并由 Spring
容器管理, 并不依赖 Tomcat
等容器, 是可以单独使用的, 不仅能应用在web
程序中, 也可以用于 Application
等程序中.
2.过滤器 和 拦截器的触发时机也不同, 如下图
1)过滤器Filter
是在请求进入容器后, 但是在进入servlet
之前进行预处理, 请求结束是在servlet
处理完以后.
2)拦截器Interceptor
是在请求进入servlet
后, 在进入Controller
之前进行预处理的, Controller
中渲染了对应的视图之后请求结束.
3.说明:过滤器不会处理请求转发, 拦截器会处理请求转发. 因为请求转发是sevlet内部的事。
4.回顾过滤器和拦截器的原理和机制.
默认异常页面
文档:https://docs.spring.io/spring-boot/docs/current/reference/html/index.html
应用实例
需求: 自定义404.html, 500.html, 4xx.html, 5xx.html. 当发生相应错误时, 显示自定义的页面信息
1.创建4个页面, 拷贝即可
1)创建src/main/resources/templates/error/404.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
<h1>404 Not Found</h1>
<a href='#' th:href="@{/}">返回主页面</a>
状态码: <h1 th:text="${status}"></h1>
错误信息: <h1 th:text="${error}"></h1><br/>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
2)创建src/main/resources/templates/error/4xx.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
<h1>4xx 发生错误了:)</h1><br/>
错误状态码: <h1 th:text="${status}"></h1><br/>
错误信息: <h1 th:text="${error}"></h1><br/>
<a href='#' th:href="@{/}">返回主页面</a>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
3)创建src/main/resources/templates/error/500.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
<h1>500 内部服务器出现了问题</h1><br/>
状态码: <h1 th:text="${status}"></h1><br/>
错误信息: <h1 th:text="${error}"></h1>
<a href='#' th:href="@{/static}">返回主页面</a>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
4)创建src/main/resources/templates/error/5xx.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
<h1>5xx 发生错误了:)</h1><br/>
错误状态码: <h1 th:text="${status}"></h1><br/>
错误信息: <h1 th:text="${error}"></h1><br/>
<a href='#' th:href="@{/static}">返回主页面</a>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
2.测试, 登陆后访问 http://localhost:8080/err/eeee
3.创建src/main/java/com/zzw/springboot/controller/MyErrorController.java
@Controller
public class MyErrorController {
//模拟一个服务器内部错误, 500错误
@GetMapping("/err")
public String err() {
int i = 10 / 0;
return "manage";
}
//配置post方式, 用get请求 /error2, 会出现405错误
@PostMapping("/err2")
public String error2() {
return "manage";
}
}
4.注意: 配置登录拦截器时,先登录, 再测试
5.访问 /err, 生成500错误, 可以看到 500.html.
6.访问 /err2, 生成405错误, 可以看到4xx.html.
7.访问 http://localhost:8084/abc, 生成404错误, 可以看到 404.html
debug取出状态码和错误信息
src/main/resources/templates/error/500.html
<div style="text-align: center">
<h1>500 内部服务器出现了问题</h1><br/>
状态码: <h1 th:text="${status}"></h1><br/>
错误信息: <h1 th:text="${error}"></h1>
<a href='#' th:href="@{/static}">返回主页面</a>
</div>
1.请求 http://localhost:8084/err
全局异常
基本说明
1.@ControllerAdvice + @ExceptionHandler 处理全局异常.
2.底层是 ExceptionHandlerExceptionResolver 支持.
应用实例
需求: 演示springboot全局异常使用, 当发生ArithmeticException时, 不使用默认异常机制.
1.新建src/main/java/com/zzw/springboot/exception/GlobalExceptionHandler.java
@Slf4j
//使用这个注解可以标识一个全局异常处理器/对象, 会注入到ioc容器中
@ControllerAdvice
public class GlobalExceptionHandler {
//编写方法, 处理指定异常, 可以指定多个异常
//Exception ex: 表示传递的异常对象
//Model model: 将异常信息, 放入model, 传递到下一个页面
@ExceptionHandler({ArithmeticException.class})
public String catchGlobalException(Exception ex, Model model) {
log.info("异常信息={}", ex.getMessage());
//将异常信息, 放入model, 传递到下一个页面
model.addAttribute("eror", ex.getMessage());
return "error/global";
}
}
2.新建src/main/resources/templates/error/global.html
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>全局异常-显示页面</title>
</head>
<body bgcolor="#CED3FE">
<img src="images/1.GIF"/>
<hr/>
<div style="text-align: center">
<h1>全局异常/错误 发生了:)</h1><br/>
异常/错误信息: <h1 th:text="${error}"></h1><br/>
<a href='#' th:href="@{/}">返回主页面</a>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
3.注意: 配置登录拦截器时,先登录, 再测试.
访问 /err, 抛出ArithmeticException异常, 被全局异常处理器的catchArithGlobalException方法捕获
注意:
1.全局异常处理器优先级大于默认异常处理机制. 所以会进入global.html, 而不是根据状态码进入500.html
2.默认异常处理机制是按照状态码寻找错误页面的.
Debug处理流程
放行
获取异常发生的方法
自定义异常
基本说明
1.如果Spring Boot提供的异常不能满足开发需求,可以自定义异常。
2.@ResponseStatus + 自定义异常
3.底层是 ResponseStatusExceptionResolver, 底层调用 response.sendError(statusCode, resolvedReason);
4.但抛出自定义异常后, 仍然会根据状态码, 去匹配使用 xxx.html 显示, 当然也可以将自定义异常, 放在全局异常处理器去处理
应用实例
需求说明
1.自定义一个异常 ZzwAccessException, 当用户访问某个无权访问的路径时, 抛出该异常, 显示自定义异常状态码,
2,访问 http://localhost:8080/err3, 效果
代码实现
1.新建src/main/java/com/zzw/springboot/exception/ZzwAccessException.java
/**
* 自定义异常
* value = HttpStatus.FORBIDDEN: 表示发生ZzwAccessException异常时, 通过http协议返回的状态码
* 这个状态码和自定义异常的对应关系由程序员决定
*/
public class ZzwAccessException extends RuntimeException {
//构造器
public ZzwAccessException(String message) {
super(message);
}
//显式定义无参构造器
public ZzwAccessException() {
}
}
2.修改src/main/java/com/zzw/springboot/controller/MyErrorController.java
//编写方法, 模拟ZzwAccessException
@GetMapping("/err3")
public String err3(String name) {
//如果用户不是zzw, 判定-无权访问
if (!"zzw".equals(name)) {
throw new ZzwAccessException();
}
return "manage";//thymeleaf视图解析器
//return "redirect:/manage";//重定向 manage.html映射
}
3.测试 http://localhost:8084/err3
Debug处理流程
抛出自定义异常,会先走全局异常的逻辑,全局异常没有指定这个异常
根据你指定的状态码寻找对应的 403.html
注意事项和细节
1.如果把自定义异常类型,放在全局异常处理器,仍然会走全局异常处理机制。
2.Debug看显示效果,也可自定义异常信息。
指定自定义异常 ZzwAccessException 为全局异常
会走全局异常的逻辑,不会根据自定义异常状态码去寻找对应的错误页面。