异常处理
基本介绍
- 默认情况下,Spring Boot 提供/error 处理所有错误的映射。
- 对于机器客户端,它将生成 JSON 响应,其中包含错误,HTTP 状态和异常消息的详细信 息。对于浏览器客户端,响应一个"whitelabel"错误视图,以 HTML 格式呈现相同的数据。(当前配置了拦截器 LoginInterceptor,需要先登录才能看到)

- SpringBoot底层默认由 DefaultErrorViewResolver 处理错误。

拦截器过滤器区别
使用范围不同
(1)过滤器实现的是 javax.servlet.Filter 接口,而这个接口是在 Servlet 规范中定义的,也就是说过滤器 Filter 的使用要依赖于 Tomcat 等容器,Filter 只能在web程序中使用。
(2)拦截器(Interceptor),它是一个 Spring 组件,并由 Spring 容器管理,并不依赖 Tomcat 等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于 Application 等程序中。
过滤器和拦截器的触发时机不同
如图

(1)过滤器 Filter 是在请求进入容器后,但在进入servlet之前进行预处理,请求结束时在servlet处理完以后。
(2)拦截器 Interceptor 是在请求进入 servlet 后,在进入 Controller 之前进行预处理的,Controller中渲染了对应的视图之后请求结束。
测试
创建 src/main/java/com/lzw/springboot/filter/LzwFilter.java
package com.lzw.springboot.filter;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import java.io.IOException;
/**
* @author LiAng
*/
@Component
public class LzwFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("LzwFilter 被调用");
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
浏览器地址栏 localhost:8080
开始调试


此时,没有拦截 “/” 请求,不会走到 LoginInterceptor

再次走到 Filter

放行,走到 DispatherServlet


此时 return "redirect:/manage.html";
,重定向,相当于又发了个请求,所以走到了 Filter


此时要请求 'manage.html',是被拦截的,所以走到 LoginInterceptor

放行,走到 AdminController


过滤器不会处理请求转发,拦截器会处理请求转发
自定义异常页面
官方文档
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.developing-web-applications.spring-mvc.error-handling.error-pages
应用实例
需求
自定义 404.html 500.html 4xx.html 5xx.html 当发生相应错误时,显示自定义的页面信息。
创建4个页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>404 Not Found</h1>
<a href="#" th:href="@{/}">返回主页面</a><br/>
状态码:<span th:text="${status}"></span><br/>
错误信息:<span th:text="${error}"></span>
</body>
</html>

创建 src/main/java/com/lzw/springboot/controller/MyErrorController.java
package com.lzw.springboot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
/**
* @author LiAng
* 模拟错误
*/
@Controller
public class MyErrorController {
//模拟服务器内部错误 500
@GetMapping("/err")
public String err(){
int i = 10 / 0;
return "manage";
}
//配置的是Post方式
//使用get方式请求,就会出现错误 405
@PostMapping("/err2")
public String err2(){
return "manage";
}
}


全局异常
说明
- @ControllerAdvice+@ExceptionHandler 处理全局异常
- 底层是 ExceptionHandlerExceptionResolver 支持的
应用实例
需求:演示全局异常使用, 当发生 ArithmeticException、NullPointerException 时,不使用默认异常机制匹配的 xxx.html , 而是显示全局异常机制指定的错误页面。
创建 src/main/java/com/lzw/springboot/exception/GlobalExceptionHandler.java
package com.lzw.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;
/**
* @author LiAng
* @ControllerAdvice: 使用它可以标识一个全局异常处理器/对象,会注入到spring容器
*/
@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());
log.info("异常发生的方法是={}", handlerMethod.getMethod());
return "error/global";
}
}
创建 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="${msg}"></h1><br/>
<a href='#' th:href="@{/}">返回主页面</a>
</div>
<hr/>
<img src="images/logo.png"/>
</body>
</html>
因为全局异常处理优先级>默认异常处理机制,所以会进入 global.html,而不是500.html

自定义异常
说明
- 如果 Spring Boot 提供的异常不能满足开发需求,程序员也可以自定义异常
- @ResponseStatus+自定义异常
- 底层是 ResponseStatusExceptionResolver,底层调用 response.sendError(statusCode, resolvedReason);
- 当抛出自定义异常后,仍然会根据状态码,去匹配使用 xxx.html 显示,当然也可以将自定义异常,放在全局异常处理器去处理
应用实例
创建 src/main/java/com/lzw/springboot/exception/AccessException.java
package com.lzw.springboot.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
/**
* @author LiAng
* AccessException : 我们自定义的一个异常
* 关于自定义异常, java基础中
* value = HttpStatus.FORBIDDEN: 表示发生AccessException异常,我们通过http协议返回的状态码 403
* 这个状态码和自定义异常的对应关系是由程序员来决定[尽量合理来设置]
*/
@ResponseStatus(value = HttpStatus.FORBIDDEN)
public class AccessException extends RuntimeException{
//提供一个构造器,可以指定信息
public AccessException(String message) {
super(message);
}
//显示的定义一下无参构造器
public AccessException() {
}
}
修改 MyErrorController.java
//编写方法,模拟一个AccessException
@GetMapping("/err3")
public String err3(String name) {
//如果用户不是tom,我们就认为,无权访问-模拟
if(!"tom".equals(name)) {
//throw new AccessException();
throw new AccessException("自定义的AccessException..");
}
return "manage";//视图地址, 请求转发,看不到数据
//return "redirect:/manage.html";
}

注意事项和细节说明
如果把自定义异常类型,放在全局异常处理器,那么仍然走全局异常处理机制。

