十二,Spring Boot 异常处理(自定义异常页面,全局异常,自定义异常)
文章目录
- 十二,Spring Boot 异常处理(自定义异常页面,全局异常,自定义异常)
- 1. 基本介绍
- 2. 自定义异常页面
- 3. 全局异常
- 4. 自定义异常
- 5. 补充:拦截器 VS 过滤器
- 6. 总结:
- 7. 最后:
1. 基本介绍
默认情况下,Spring Boot 提供 / error
处理所有的错误的映射,也就是说当出现错误时,
Spring Boot 底层会请求转发到 /error 这个映射。
注意是:请求转发,不是重定向的。
Spring Boot 它是有自己的默认的处理机制的,在你创建的一个 Spring Boot 项目去访问一个没有的路径会发现,Spring Boot 它是会弹出来这样的信息。
这是 Spring Boot 给我们内置好的,编写好的,默认配置好的 错误信息页面。
如下是 Spring Boot 检索,显示对应错误页面存放位置的结构视图:官网地址:https://docs.spring.io/spring-boot/reference/web/reactive.html#web.reactive.webflux.error-handling.error-pages
src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
+- <other public assets>
src/
+- main/
+- java/
| + <source code>
+- resources/
+- templates/
+- error/
| +- 5xx.mustache
+- <other templates>
一旦项目发生了错误,Spring Boot 会根据对应错误状态码,上述的结构路径,上找到对应的错误页面。
2. 自定义异常页面
根据上述的说明,我们根据在 Spring Boot 默认错误页面的检索路径结构,创建对应的目录,同时存放对应的错误页面。如下图所示:
自定义 404.html,500.html, 4xx.html ,5xx.html
当发生相应错误时,显示自定义的页面信息.
首先,我们先定义前端错误的 4开头
的错误页面。
在定义两个后端错误的 5 开头
的错误页面。
编写好之后,我们就可以模拟错误异常,试试,Spring Boot是否会走我们的自定义的错误页面,在前端展示出来 。
测试:4开头的,前端错误
测试: 5开头的后端错误的
3. 全局异常
全局异常:就是作用于整个项目的异常的,也可以是整个项目的异常页面。
@ControllerAdvice + @ExceptionHandler
这两个注解定义全局异常。
底层是 ExceptionHandlerExceptionResolver 支持的,不可以省略其它的。
定义一个全局异常。
package com.rainbowsea.springboot.exception;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@Slf4j
@ControllerAdvice // 使用它可以标识一个全局异常处理器/对象
public class GlobalExceptionHandler {
// 1.编写方法,处理指定异常,比如:我们处理算术异常,可以指定多个异常
// 2. 这里要处理的异常,由程序员来指定
// 3. Exception e : 表示异常发生后,传递的异常对象
// 4. Model model : 可以将我们的异常信息,放入 model ,再放入到请求域当中,并传递显示页面
@ExceptionHandler({ArithmeticException.class, NullPointerException.class})
public String handleAritException(Exception e, Model model) {
log.info("异常信息={}",e.getMessage()); // 自定义的异常也是可以获取到的
// 这里老师将发生的异常放入到model,可以再错误页面取出显示
model.addAttribute("msg",e.getMessage());
return "/error/global"; // 视图地址
}
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>全局异常</title>
</head>
<body>
<h1>全局异常处理/错误,发生:)</h1>
异常/错误信息: <h1 th:text="${msg}"></h1>
</body>
</html>
运行测试:
注意:全局异常> 自定义异常>内置异常 的优先级
当一个异常,即在自定义异常当中,也在全局异常当中定义处理了,那么Spring Boot 优先走 全局异常/(全局异常的页面)。
当发生ArithmeticException,NullPointerException,不使用默认异常的机制,匹配的 xxx.html,而是显示全局异常机制指定的错误页面。所以,全局异常可以说是最高优先级了。
4. 自定义异常
如果 Spring Boot 提供的异常不能满足开发需求,程序员也可以自定义异常。
- @ResponseStatus + 自定义异常。
- 底层是 ResponseStatusExceptionResolver。
- 当抛出自定义异常后,仍然会根据状态码,去匹配使用 xxx.html 显示。
关于自定义异常,我们在Java基础中是讲解过 => 融合贯通
- @ResponseStatus(value = HttpStatus.FORBIDDEN) : 表示发生 AccessException异常,我们通过http
- 协议返回的状态码 403
- 这个状态码和自定义异常的对应关系是由程序员来决定【尽量合理来设置】
演示: 自定义一个异常AccessException,当用户访问某个无权访问的路径时,抛出该异常,显示自定义异常状态。
下面我们就自定义一个名为:AccessException
的异常,该自定异常的状态码为:
package com.rainbowsea.springboot.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* 关于自定义异常,我们在Java基础中是讲解过 => 融合贯通
*
* @ResponseStatus(value = HttpStatus.FORBIDDEN) : 表示发生 AccessException异常,我们通过http
* 协议返回的状态码 403
* 这个状态码和自定义异常的对应关系是由程序员来决定【尽量合理来设置】
*/
@ResponseStatus(value = HttpStatus.FORBIDDEN)
public class AccessException extends RuntimeException{
// 老师提供一个构造器,可以指定信息
public AccessException(String message) {
super(message);
System.out.println("AccessException 有参数构造方法被调用");
}
// 显示的定义一个无参构造器
public AccessException() {
System.out.println("AccessException 无参数构造方法被调用");
}
}
运行测试:
package com.rainbowsea.springboot.controller;
import com.rainbowsea.springboot.exception.AccessException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class MyErrorController {
// 编写方法, 模拟一个AccessException
@GetMapping("/err3")
public String err3(String name) {
// 如果用户不是tom,我们就认为:无权访问-模拟
if (!"tom".equals(name)) {
throw new AccessException("我们自己定义的异常");
// 这里编写了放入的信息new AccessException("我们自己定义的异常") ,可以进行在后面放入
// 到前端获取到,并显示出来。
/*
log.info("异常信息={}",e.getMessage()); // 自定义的异常也是可以获取到的
// 这里老师将发生的异常放入到model,可以再错误页面取出显示
model.addAttribute("msg",e.getMessage());
*/
}
return "manage"; // 视图地址
}
}
执行效果如图:浏览器: http://locathos:8080/err3
简单的说:其实就是我们在 java se 当中学习到自定义异常处理
如果把自定义异常类型,放在全局异常回来,那么仍然走全局异常处理机制。
简单的说:我们自定义的异常类型,也是可以放到全局异常当中的。
运行测试:注意:全局异常的优先级比自定义异常更高,所以当一个异常,即定义在了“自定义异常” ,也定义在了“全局异常”当中时,优先执行全局异常的错误页面
运行测试:
5. 补充:拦截器 VS 过滤器
- 使用范围不同:
- 过滤器实现是
javax.servlet.Filter
接口,而这个接口在Servlet
规范中定义的,也就是说,过滤器Filter
的使用依赖于 Tomcat 等容器,Filter 只能在 web 程序中使用。- 拦截器(Interceptor) 它是一个 Spring 组件,并由 Spring 容器管理,并不依赖 Tomcat 等容器,是可以单独使用的,不仅能应用在 Web 程序中,而可以用于
Application
等程序中。
- 过滤器 和 拦截器 的触发时机不同:
- 过滤器 Filter 是在请求进入容器后,但在进入 Servlet 之前进行预处理。请求结束时 Servlet 处理完以后。
- 拦截器 Interceptor 是在请求进入 servlet 后,在进入 Controller 控制器之后进行预处理的,Controller 中渲染了对应的视图之后,请求结束。
- 说明:过滤器不会处理请求转发,拦截器会处理请求转发。
- 说明:过滤器不会处理请求转发,拦截器会处理请求转发。因为过滤器是自己内部进行过滤转发的,会先找自己同一层的内部的映射,如果没有找到才会走拦截器需要的。
- 至于过滤器和拦截器的原理和机制,过滤器在JavaWeb 有所说明,大家可以移步至✏️✏️✏️ JavaWeb 中 Filter过滤器-CSDN博客
6. 总结:
- 默认情况下,Spring Boot 提供
/ error
处理所有的错误的映射,也就是说当出现错误时,
Spring Boot 底层会请求转发到 /error 这个映射。**注意是:请求转发,不是重定向的。 ** - 稍微记忆一下,Spring Boot,显示对应错误页面存放位置的结构视图:官网地址:https://docs.spring.io/spring-boot/reference/web/reactive.html#web.reactive.webflux.error-handling.error-pages
-
自定义异常错误页面,要根据 Spring Boot检索的错误页面的目录,来存放错误该错误页面的文件
-
全局异常:就是作用于整个项目的异常的,也可以是整个项目的异常页面。
@ControllerAdvice + @ExceptionHandler
这两个注解定义全局异常。 -
注意:全局异常(/全局异常错误页面)> 自定义异常(/自定义异常错误页面)>内置异常(/内置异常错误页面)的优先级
-
注意:拦截器和过滤器的区别。
7. 最后:
“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”