spring 31 Tomcat 异常处理

  • 我们知道 @ExceptionHandler 只能处理发生在 mvc 流程中的异常,例如控制器内、拦截器内,那么如果是 Filter 出现了异常,如何进行处理呢?

  • 在 Spring Boot 中,是这么实现的:

    1. 因为内嵌了 Tomcat 容器,因此可以配置 Tomcat 的错误页面,Filter 与 错误页面之间是通过请求转发跳转的,可以在这里做手脚
    2. 先通过 ErrorPageRegistrarBeanPostProcessor 这个后处理器配置错误页面地址,默认为 /error 也可以通过 ${server.error.path} 进行配置
    3. 当 Filter 发生异常时,不会走 Spring 流程,但会走 Tomcat 的错误处理,于是就希望转发至 /error 这个地址
      • 当然,如果没有 @ExceptionHandler,那么最终也会走到 Tomcat 的错误处理
    4. Spring Boot 又提供了一个 BasicErrorController,它就是一个标准 @Controller,@RequestMapping 配置为 /error,所以处理异常的职责就又回到了 Spring
    5. 异常信息由于会被 Tomcat 放入 request 作用域,因此 BasicErrorController 里也能获取到
    6. 具体异常信息会由 DefaultErrorAttributes 封装好
    7. BasicErrorController 通过 Accept 头判断需要生成哪种 MediaType 的响应
      • 如果要的不是 text/html,走 MessageConverter 流程
      • 如果需要 text/html,走 mvc 流程,此时又分两种情况
        • 配置了 ErrorViewResolver,根据状态码去找 View
        • 没配置或没找到,用 BeanNameViewResolver 根据一个固定为 error 的名字找到 View,即所谓的 WhitelabelErrorView

演示1 - 错误页处理

关键代码
@Bean // ⬅️修改了 Tomcat 服务器默认错误地址, 出错时使用请求转发方式跳转
public ErrorPageRegistrar errorPageRegistrar() {
    return webServerFactory -> webServerFactory.addErrorPages(new ErrorPage("/error"));
}

@Bean // ⬅️TomcatServletWebServerFactory 初始化前用它增强, 注册所有 ErrorPageRegistrar
public ErrorPageRegistrarBeanPostProcessor errorPageRegistrarBeanPostProcessor() {
    return new ErrorPageRegistrarBeanPostProcessor();
}

收获💡

  1. Tomcat 的错误页处理手段

演示2 - BasicErrorController

关键代码
@Bean // ⬅️ErrorProperties 封装环境键值, ErrorAttributes 控制有哪些错误信息
public BasicErrorController basicErrorController() {
    ErrorProperties errorProperties = new ErrorProperties();
    errorProperties.setIncludeException(true);
    return new BasicErrorController(new DefaultErrorAttributes(), errorProperties);
}

@Bean // ⬅️名称为 error 的视图, 作为 BasicErrorController 的 text/html 响应结果
public View error() {
    return new View() {
        @Override
        public void render(
            Map<String, ?> model, 
            HttpServletRequest request, 
            HttpServletResponse response
        ) throws Exception {
            System.out.println(model);
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().print("""
                    <h3>服务器内部错误</h3>
                    """);
        }
    };
}

@Bean // ⬅️收集容器中所有 View 对象, bean 的名字作为视图名
public ViewResolver viewResolver() {
    return new BeanNameViewResolver();
}

收获💡

  1. Spring Boot 中 BasicErrorController 如何工作
posted @ 2022-07-09 22:48  xy7112  阅读(293)  评论(0)    收藏  举报