springMVC的异常处理

web异常可以分为2种

一是在控制器处理中产生的异常;二是在请求过滤器中等等。

在控制器处理中产生的异常:

        在这类异常中,我们可以在Controller中定义异常处理器,@ExceptionHandler来处理当类中产生的异常。

static class Controller1 {
        public void foo() {

        }
        @ExceptionHandler
        @ResponseBody
        public Map<String, Object> handle(ArithmeticException e) {
            return Map.of("error", e.getMessage());
        }
    }

最后被转化中异常处理器,来处理本类中的异常。

1.同时也采用了责任链模式,它会组合所有的异常处理器,以此判断是否可以处理。(摘自源码)

@Nullable
    private HandlerMethodArgumentResolverComposite argumentResolvers;

2.同时也采用了缓存技术,加载后的异常处理器会存在于缓存中(摘自源码)

private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache = new ConcurrentHashMap(64);
private final Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> exceptionHandlerAdviceCache = new LinkedHashMap();

3.其也对嵌套异常进行了处理(摘自源码)

for(Object exToExpose = exception; exToExpose != null; exToExpose = cause != exToExpose ? cause : null) {
                    exceptions.add(exToExpose);
                    cause = ((Throwable)exToExpose).getCause();
                }

                Object[] arguments = new Object[exceptions.size() + 1];
                exceptions.toArray(arguments);
                arguments[arguments.length - 1] = handlerMethod;
                exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);

他会取出所有异常,包括当前异常的产生异常,就可以判断嵌套异常。

如果是在@ControllerAdvice中定义,它就是全局异常处理器,可以处理所有控制器异常。

@ControllerAdvice
    static class MyControllerAdvice {
        @ExceptionHandler
        @ResponseBody
        public Map<String, Object> handle(Exception e) {
            return Map.of("error", e.getMessage());
        }
    }

对于全局处理器和单类异常处理器的优先级:

        肯定是优先使用当前异常产生类中的异常处理器咯

tomcat异常处理:

如果是springmvc没有进行异常处理的或者是过滤器或者拦击器中的异常就会交由tomcat来处理

首先tomcat遇到异常默认会产生一个异常页面,我们也可以对异常页面进行配置

    @Bean // 修改了 Tomcat 服务器默认错误地址
    public ErrorPageRegistrar errorPageRegistrar() { // 出现错误,会使用请求转发 forward 跳转到 error 地址
        return webServerFactory -> webServerFactory.addErrorPages(new ErrorPage("/error"));
    }

    @Bean
    public ErrorPageRegistrarBeanPostProcessor errorPageRegistrarBeanPostProcessor() {
        return new ErrorPageRegistrarBeanPostProcessor();
    }

    @Controller
    public static class MyController {
        @RequestMapping("test")
        public ModelAndView test() {
            int i = 1 / 0;
            return null;
        }

        @RequestMapping("/error")
        @ResponseBody
        public Map<String, Object> error(HttpServletRequest request) {
            Throwable e = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
            return Map.of("error", e.getMessage());
        }
    }

产生异常后,springMVC会将异常记录在request域中可以通过RequestDispatcher.ERROR_EXCEPTION来获取。

这里可以用springboot提供的一个BasicErrorController类来代替,其功能更完善,可以根据请求同来返回不同信息,浏览器返回html,postman返回json(附上源码,供大家一赏)

其实也是springboot默认使用的

@Bean
    public BasicErrorController basicErrorController() {
        ErrorProperties errorProperties = new ErrorProperties();
        errorProperties.setIncludeException(true);
        return new BasicErrorController(new DefaultErrorAttributes(), errorProperties);
    }

    @Bean
    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>
                        """);
            }
        };
    }
@RequestMapping(
        produces = {"text/html"}
    )
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = this.getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
        return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }

    @RequestMapping
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        HttpStatus status = this.getStatus(request);
        if (status == HttpStatus.NO_CONTENT) {
            return new ResponseEntity(status);
        } else {
            Map<String, Object> body = this.getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));
            return new ResponseEntity(body, status);
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值