SpringMVC异常处理(Java学习笔记)

在实际的应用开发中,经常会不可避免地遇到各种可预知的、不可预知的异常,此时我们就需要对这些异常处理,以保证程序正常运行。

Spring MVC 提供了一个名为 HandlerExceptionResolver 的异常处理器接口,它可以对控制器方法执行过程中出现的各种异常进行处理。

Srping MVC 为 HandlerExceptionResolver 接口提供了多个不同的实现类,其中最常用的实现类如下。
DefaultHandlerExceptionResolver
ResponseStatusExceptionResolver
ExceptionHandlerExceptionResolver
SimpleMappingExceptionResolver

其中,ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver 和 DefaultHandlerExceptionResolver 是 Spring MVC 的默认异常处理器。

如果程序发生异常,Spring MVC 会按照 ExceptionHandlerExceptionResolver → ResponseStatusExceptionResolver → DefaultHandlerExceptionResolver 的顺序,依次使用这三个异常处理器对异常进行解析,直到完成对异常的解析工作为止。

DefaultHandlerExceptionResolver

DefaultHandlerExceptionResolver 是 HandlerExceptionResolver 接口的常用实现类之一,更是 Spring MVC 提供的默认异常处理器之一,Spring MVC 默认通过它对控制器处理请求时出现的异常进行处理。

DefaultHandlerExceptionResolver 提供了一个 doResolveException() 方法,其返回类型为 ModelAndView。该方法会在控制器方法出现指定异常时,生成一个新的包含了异常信息的 ModelAndView 对象替换控制器方法的 ModelAndView 对象,以达到跳转到指定的错误页面,展示异常信息的目的,其部分源码如下。

@Nullable
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
    try {
        if (ex instanceof HttpRequestMethodNotSupportedException) {
            return this.handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException)ex, request, response, handler);
        }

         if (ex instanceof HttpMediaTypeNotSupportedException) {
            return this.handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException)ex, request, response, handler);
        }

        if (ex instanceof HttpMediaTypeNotAcceptableException) {
            return this.handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException)ex, request, response, handler);
        }
        ……
    } catch (Exception var6) {
        if (this.logger.isWarnEnabled()) {
            this.logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", var6);
        }
    }
    return null;
}

从上面的代码可以看出,DefaultHandlerExceptionResolver 的 doResolveException() 方法可以将 Spring MVC 产生的各种异常转换为合适的状态码(code)。通过这些状态码,我们就可以进一步的确定发生异常的原因,以便于找到对应的问题。

异常状态码说明
HttpRequestMethodNotSupportedException405(Method Not Allowed)HTTP 请求方式不支持异常
HttpMediaTypeNotSupportedException415(Unsupported Media Type) HTTP 媒体类型不支持异常
HttpMediaTypeNotAcceptableException406(Not Acceptable)HTTP 媒体类型不可接受异常
BindException400(Bad Request)数据绑定异常
MissingServletRequestParameterException400(Bad Request)缺少参数异常
ConversionNotSupportedException500(Internal Server Error)数据类型转换异常
TypeMismatchException400(Bad Request)类型不匹配异常
HttpMessageNotReadableException400(Bad Request)HTTP 消息不可读异常
HttpMessageNotWritableException500(Internal Server Error)HTTP 消息不可写异常

上表中只列举一些常见的异常状态码,至于更多的异常及其状态码映射,请参考 org.springframework.http.HttpStatus。

ResponseStatusExceptionResolver

ResponseStatusExceptionResolver 也是 HandlerExceptionResolver 的实现类之一。与 DefaultHandlerExceptionResolver 一样,ResponseStatusExceptionResolver 也是 Spring MVC 提供的默认异常处理器之一,它被用来解析 @ResponseStatus 注解标注的自定义异常,并把异常的状态信息返回给客户端展示。
@ResponseStatus 注解
@ResponseStatus 注解主要用来标注在自定义的异常类上,示例代码如下。


import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "自定义异常")
public class UserNotExistException extends RuntimeException {
}

如果程序运行时发生了这个自定义的异常,Spring MVC 就会通过 ResponseStatusExceptionResolver 对该异常进行解析,并将异常信息展示到错误页上。

@ResponseStatus 注解包含了三个属性,如下表。

属性说明
code设置异常的状态码。code 为 @ResponseStatus 注解 value 属性的别名,与 value 属性完全等价。value 设置异常的状态码。
value为 @ResponseStatus 注解 code 属性的别名,与 code 属性完全等价。
reason设置异常的原因或描述。

ExceptionHandlerExceptionResolver

ExceptionHandlerExceptionResolver 是 HandlerExceptionResolver 接口的实现类之一,它也是 Spring MVC 提供的默认异常处理器之一。

ExceptionHandlerExceptionResolver 可以在控制器方法出现异常时,调用相应的 @ExceptionHandler 方法(即使用了 @ExceptionHandler 注解的方法)对异常进行处理。

@ExceptionHandler 注解

Spring MVC 允许我们在控制器类(Controller 类)中通过 @ExceptionHandler 注解来定义一个处理异常的方法,以实现对控制器类内发生异常的处理。

@ExceptionHandler 注解中包含了一个 value 属性,我们可以通过该属性来声明一个指定的异常。如果在程序运行过程中,这个 Controller 类中的方法发生了这个指定的异常,那么 ExceptionHandlerExceptionResolver 就会调用这个 @ExceptionHandler 方法对异常进行处理。

package net.biancheng.c.controller;

import net.biancheng.c.exception.UserNotExistException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class ExceptionController2 {

    //控制器方法
    @RequestMapping(value = "/testExceptionHandler")
    public String testExceptionHandler() {
        //发生 ArithmeticException 异常
        System.out.println(10 / 0);
        return "success";
    }

    //使用 @ExceptionHandler 注解定义一个异常处理方法
    @ExceptionHandler(ArithmeticException.class)
    public String handleException(ArithmeticException exception, Model model) {
        //将异常信息通过 Model 放到 request 域中,以方便在页面中展示异常信息
        model.addAttribute("ex", exception);
        //跳转到错误页
        return "error";
    }

}

@ExceptionHandler 方法的优先级

如果我们在同一个控制器类内使用 @ExceptionHandler 注解定义了多个异常处理的方法,那么我们就需要注意下 @ExceptionHandler 方法的优先级问题。
handleException 声明的异常为 Exception,handleException 声明的异常为 RuntimeException,且 Exception 是 RuntimeException 的父类。若此时控制器方法发生了 ArithmeticException 异常,那么 ExceptionHandlerExceptionResolver 会根据继承关系,调用继承深度最浅的异常处理方法(即 handleException2 方法),对异常进行处理。

注意,定义在某个控制器类中的 @ExceptionHandler 方法只在当前的控制器中有效,它只能处理其所在控制器类中发生的异常。

全局异常处理

我们还可以将 @ExceptionHandler 方法定义在一个使用了 @ControllerAdvice 注解的类中。使用 @ControllerAdvice 注解的类可以包含多个不同的带有 @ExceptionHandler 注解的方法,这些方法可以应用应用程序中所有带有 @RequestMapping 注解的控制器方法中,实现全局异常处理。


package net.biancheng.c.controller;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class ExceptionControllerAdvice {
    @ExceptionHandler
    public String exceptionAdvice(Exception exception, Model model) {
        System.out.println("ExceptionControllerAdvice>>>>>>>>>>>>>>>>>>>");
        model.addAttribute("ex", exception);
        return "error-2";
    }
}

SimpleMappingExceptionResolver

Spring 提供了一个自定义的异常处理器:org.springframework.web.servlet.handler.SimpleMappingExceptionResolver ,也能够实现对所有异常的统一处理,具体步骤如下。

  1. 默认情况下,Spring MVC 的上下文中并没有装配 SimpleMappingExceptionResolver 的实例,因此我们在使用它进行统一处理异常前,需要在 Spring MVC 配置文件中配置异常类和 View 的对应关系,具体代码如下:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <!--properties的键表示处理器方法执行过程中出现的异常
            properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面-->
            <prop key="net.biancheng.c.exception.UserNotExistException">error-3</prop>
        </props>
    </property>
    <!-- exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享-->
    <property name="exceptionAttribute" value="ex"></property>
</bean>
  1. 在 webapp/WEB-INF/templates 目录下,新建一个名为 error-3 的错误页,代码如下。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
error-3 出错了!
<h4 th:text="${ex}"></h4>
</body>
</html>

学习原文

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值