SpringBoot2学习笔记
四、Web开发
4.9)异常处理
4.9.1)异常处理默认规则
默认情况下,Spring Boot提供/error处理所有错误的映射;
对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据,访问不存在路径内容页面如下:
要对其进行自定义,error/下的4xx,5xx页面会被自动解析,添加view页面4xx.html,代码如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<meta name="description" content="">
<meta name="author" content="ThemeBucket">
<link rel="shortcut icon" href="#" type="image/png">
<title>404 Page</title>
<link href="css/style.css" rel="stylesheet">
<link href="css/style-responsive.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="js/html5shiv.js"></script>
<script src="js/respond.min.js"></script>
<![endif]-->
</head>
<body class="error-page">
<section>
<div class="container ">
<section class="error-wrapper text-center">
<h1><img alt="" src="images/404-error.png"></h1>
<h2 th:text="${status}">page not found</h2>
<h3 th:text="${message}">We Couldn’t Find This Page</h3>
<a class="back-btn" th:href="@{/main.html}"> Back To Home</a>
</section>
</div>
</section>
<!-- Placed js at the end of the document so the pages load faster -->
<script src="js/jquery-1.10.2.min.js"></script>
<script src="js/jquery-migrate-1.2.1.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script src="js/modernizr.min.js"></script>
<!--common scripts for all pages-->
<!--<script src="js/scripts.js"></script>-->
</body>
</html>
页面如下:
4.9.2)定制错误处理逻辑
-
自定义错误页: error/404.html error/5xx.html;有精确的错误状态码页面就匹配精确,没有就找 4xx.html;如果都没有就触发白页;
-
@ControllerAdvice+@ExceptionHandler处理全局异常;底层是 ExceptionHandlerExceptionResolver 支持的,新建GlobalExceptionHandler.java ,代码如下:
/** * 处理整个web controller的异常 */ @Slf4j @ControllerAdvice public class GlobalExceptionHandler { //处理数学运算异常和空指针异常 @ExceptionHandler({ArithmeticException.class, NullPointerException.class}) public String handleArithException(Exception e) { log.error("异常是:{}", e); //当遇到数学运算异常和空指针异常时跳转到login页面[视图地址] return "login"; } }
-
@ResponseStatus+自定义异常 ;底层是 ResponseStatusExceptionResolver :把responsestatus注解的信息底层调用 response.sendError(statusCode, resolvedReason);tomcat发送到/error访问错误页, 新建UserTooManyExceptionr.java ,代码如下:
// 当 http状态码为 HttpStatus.FORBIDDEN时,进入该异常进行处理 @ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "用户数量太多") @Slf4j public class UserTooManyException extends RuntimeException { public UserTooManyException() { log.error("异常是:{用户数量太多}"); } public UserTooManyException(String message) { super(message); } }
修改接口内容进行测试:
@GetMapping("/dynamic_table") public String dynamic_table(Model model) { //表格内容的遍历 List<User> users = Arrays.asList(new User("zhangsan", "123456"), new User("lisi", "123444"), new User("haha", "aaaaa"), new User("hehe ", "aaddd")); // @ResponseStatus+自定义异常 if (users.size() > 3) { throw new UserTooManyException(); } model.addAttribute("users", users); return "table/dynamic_table"; }
测试:浏览器访问:http://localhost:8080/dynamic_table,页面如下:
-
-
Spring底层的异常:如参数类型转换异常;DefaultHandlerExceptionResolver 处理框架底层的异常
response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
-
自定义实现 HandlerExceptionResolver 处理异常;可以作为默认的全局异常处理规则,新建CustomerHandlerExceptionResolver.java ,代码如下:
//优先级,数字越小优先级越高,设为最高优先级 @Order(value = Ordered.HIGHEST_PRECEDENCE) @Component public class CustomerHandlerExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { try { response.sendError(511, "我喜欢的错误"); } catch (IOException e) { e.printStackTrace(); } return new ModelAndView(); } }
-
ErrorViewResolver 实现自定义处理异常; ○ response.sendError 。error请求就会转给controller
○ 你的异常没有任何人能处理。tomcat底层 response.sendError。error请求就会转给controller
○ basicErrorController 要去的页面地址是 ErrorViewResolver ;
4.9.3)异常处理自动配置原理
ErrorMvcAutoConfiguration:自动配置异常处理规则
-
容器中的组件:类型:DefaultErrorAttributes -> id:errorAttributes
■ public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver
■ DefaultErrorAttributes:定义错误页面中可以包含哪些数据
-
-
容器中的组件:类型:BasicErrorController --> id:basicErrorController(json+白页 适配响应)
■ 处理默认 /error 路径的请求;页面响应 new ModelAndView("error", model); ■ 容器中有组件 View->id是error;(响应默认错误页) ■ 容器中放组件 BeanNameViewResolver(视图解析器);按照返回的视图名作为组件的id去容器中找View对象。
-
容器中的组件:类型:DefaultErrorViewResolver -> id:conventionErrorViewResolver
■ 如果发生错误,会以HTTP的状态码 作为视图页地址(viewName),找到真正的页面 ■ error/404、5xx.html
4.9.4)异常处理步骤流程
-
执行目标方法,目标方法运行期间有任何异常都会被catch、而且标志当前请求结束;并且用 dispatchException 进行封装,源码如下图:
-
进入视图解析流程(页面渲染)
源码如下图:
processDispatchResult方法源码如下:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
this.logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException)exception).getModelAndView();
} else {
Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
mv = this.processHandlerException(request, response, handler, exception);
errorView = mv != null;
}
}
if (mv != null && !mv.wasCleared()) {
this.render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else if (this.logger.isTraceEnabled()) {
this.logger.trace("No view rendering, null ModelAndView returned.");
}
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
}
}
}
-
处理handler发生的异常
mv = processHandlerException:处理handler发生的异常,处理完成返回ModelAndView;
-
遍历所有的 handlerExceptionResolvers,看谁能处理当前异常【HandlerExceptionResolver处理器异常解析器】,如下图:
HandlerExceptionResolver源码如下:
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
-
系统默认的异常解析器,如下图:
DefaultErrorAttributes先来处理异常。把异常信息保存到rrequest域,并且返回null;
默认没有任何人能处理异常,所以异常会被抛出:
如果没有任何人能处理最终底层就会发送 /error 请求。会被底层的BasicErrorController处理
解析错误视图;遍历所有的 ErrorViewResolver【错误页面解析器】看谁能解析
默认的 DefaultErrorViewResolver ,作用是把响应状态码作为错误页的地址,error/500.html
模板引擎最终响应这个页面 error/500.html