【Web开发】Spring MVC 中的异常处理方法

简概

官方文档地址:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-exceptionhandler

异常处理⽅法

  • 处理⽅法

    • @ExceptionHandler
  • 添加位置

    • @Controller / @RestController
    • @ControllerAdvice / @RestControllerAdvice
  1. @RestControllerAdvice或者@ControllerAdvice类内的解析器的优先级低于@Controller@RestController类的解析器的优先级
  2. 如果一个异常能被多个解析器所处理,则选择继承关系最近的解析器

异常处理方法-@ExceptionHandler

@Controller和@ControllerAdvice类可以有@ExceptionHandler方法来处理来自控制器方法的异常,如下例所示:

@Controller
public class SimpleController {

    // ...

    @ExceptionHandler
    public ResponseEntity<String> handle(IOException ex) {
        // ...
    }
}

异常可以匹配正在传播的顶级异常(例如抛出的直接IOException),也可以匹配包装器异常中的嵌套原因(例如包裹在IllegalStateException中的IOException)。在5.3中,这可以匹配任意的原因级别,而以前只考虑直接原因。

为了匹配异常类型,最好将目标异常声明为方法参数,如上例所示。当多个异常方法匹配时,根异常匹配通常优先于原因异常匹配。更具体地说,ExceptionDepthComparator用于根据抛出异常类型的深度对异常进行排序。

或者,通过注解的声明来缩小异常类型以匹配,如下例所示:

@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(IOException ex) {
    // ...
}

甚至可以使用一个带有非常通用的参数签名的特定异常类型列表,如下例所示:

@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(Exception ex) {
    // ...
}

根源异常和原因异常匹配之间的区别可能令人惊讶。

在前面展示的IOException变体中,通常使用实际的FileSystemExceptionRemoteException实例作为参数调用该方法,因为它们都是从IOException扩展而来的。但是,如果任何这样的匹配异常在包装器异常中传播,而包装器异常本身就是一个IOException,则传入的异常实例就是那个包装器异常。

handle(Exception)变体中,这种行为甚至更简单。在包装场景中,这总是与包装器异常一起调用,在这种情况下,通过ex.getCause()找到实际匹配的异常。传入的异常只有在作为顶级异常抛出时才是实际的FileSystemExceptionRemoteException实例。

通常建议在参数签名中尽可能具体,以减少根源异常类型和原因异常类型之间不匹配的可能性。考虑将多匹配方法分解为单独的@ExceptionHandler方法,每个方法通过其签名匹配单个特定的异常类型。

在多个@ControllerAdvice中,建议按相应顺序优先级排列的@ControllerAdvice上声明主根异常映射。虽然根异常匹配优先于原因,但这是在一个给定controller@ControllerAdvice类的方法中定义的。这也就是说高优先级的@ControllerAdvice bean上的原因匹配优先于低优先级的@ControllerAdvice bean上的任何匹配(例如,根)。

最后但并非最不重要的是,@ExceptionHandler方法实现可以通过以原始形式重新抛出给定异常实例来选择退出处理。如果您只对根级匹配感兴趣,或者对无法静态确定的特定上下文中的匹配感兴趣,那么这将非常有用。重新抛出的异常通过剩余的解析链传播,就好像给定的@ExceptionHandler方法一开始就不匹配一样。

Spring MVC中对@ExceptionHandler方法的支持是建立在DispatcherServlet级别、HandlerExceptionResolver机制上的。

Controller Advice

官网文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-controller-advice

@ExceptionHandler@InitBinder@ModelAttribute三个注解标记的方法只作用于声明它们的@Controller类或类层次结构。而如果它们在@ControllerAdvice@RestControllerAdvice类中声明,那么它们将应用于任何controller。从5.3开始,@ControllerAdvice中的@ExceptionHandler方法可以用于处理来自任何@Controller或任何其他处理程序的异常。

@ControllerAdvice使用@Component进行元注释,因此可以通过组件扫描注册为Spring bean。@RestControllerAdvice使用@ControllerAdvice@ResponseBody进行元注释,这意味着@ExceptionHandler方法的返回值将通过响应体消息转换呈现,而不是通过HTML视图。

在启动时,RequestMappingHandlerMappingExceptionHandlerExceptionResolver检测controller advice beans并在运行时应用它们。来自@ControllerAdvice的全局@ExceptionHandler方法应用在来自@Controller的本地方法之后。相比之下,全局的@ModelAttribute@InitBinder方法在本地方法之前被应用。

@ControllerAdvice注解具有一些属性,可以缩小@ControllerAdvice注解所应用的控制器和处理程序集。

在这里插入图片描述

但其是在运行时计算,如果广泛使用,可能会对性能产生负面影响。

@InitBinder

@InitBinder用于在控制器(Controller)中标注于方法上,表示为当前控制器注册一个属性编辑器,只对当前的Controller有效。@InitBinder标注的方法必须有一个参数WebDataBinder。webDataBinder是用于表单到方法的数据绑定的。所谓的属性编辑器可以理解就是帮助我们完成参数绑定。

可参考:https://www.cnblogs.com/better-farther-world2099/articles/10971897.html

@ModelAttribute

@ModelAttribute,用来将请求参数绑定到 Model 对象。
需要注意的是,因为模型对象要先于 controller 方法之前创建,所以被 @ModelAttribute 注解的方法会在 Controller 每个方法执行之前都执行。因此一个 Controller 映射多个 URL 时,要谨慎使用

可参考:http://c.biancheng.net/spring_mvc/model-attribute.html

@ExceptionHandler支持的方法参数

方法参数描述
Exception type用于访问引发的异常。
HandlerMethod用于访问引发异常的控制器方法。
WebRequest, NativeWebRequest对请求参数、请求和会话属性的通用访问,而不直接使用Servlet API。
javax.servlet.ServletRequest, javax.servlet.ServletResponse选择任何特定的请求或响应类型(例如,ServletRequestHttpServletRequest或Spring的MultipartRequestMultipartHttpServletRequest)。
javax.servlet.http.HttpSession强制会话的存在。因此,这样的参数永远不会为空。 注意,会话访问不是线程安全的。如果允许多个请求并发访问一个会话,可以考虑将RequestMappingHandlerAdapter实例的synchronizeOnSession标志设置为true。
java.security.Principal当前经过身份验证的用户——如果知道,可能是特定的Principal实现类。
HttpMethod请求的HTTP方法。
java.util.Locale当前请求区域设置,由可用的最具体的LocaleResolver确定——实际上,配置的LocaleResolver或LocaleContextResolver。
java.util.TimeZone, java.time.ZoneId与当前请求关联的时区,由LocaleContextResolver确定。
java.io.OutputStream, java.io.Writer用于访问由Servlet API公开的原始响应体
java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap以访问错误响应的模型。总是空的。
RedirectAttributes指定在重定向-情况下使用的属性(将被附加到查询字符串)和flash属性将被临时存储,直到重定向后的请求。参见重定向属性Flash属性
@SessionAttribute对于任何会话属性的访问,与作为类级别@SessionAttributes声明的结果存储在会话中的模型属性相反。更多细节请参见@SessionAttribute
@RequestAttribute用于访问请求属性。详见@RequestAttribute

@ExceptionHandler支持的方法返回值

返回值描述
@ResponseBody返回值通过HttpMessageConverter实例转换并写入响应。参见@ResponseBody
HttpEntity<B>, ResponseEntity<B>返回值指定完整的响应(包括HTTP头和正文)通过HttpMessageConverter实例进行转换并写入响应。参见ResponseEntity
StringViewResolver实现解析并与隐式模型一起使用的视图名称——通过command对象和@ModelAttribute方法确定。handler方法还可以通过声明model参数(前面描述过)以编程方式丰富模型。
View一个视图实例,用来与隐式模型一起渲染——通过command对象和@ModelAttribute方法确定。handler方法还可以通过声明model参数(前面描述过)以编程方式丰富模型。
java.util.Map, org.springframework.ui.Model属性要添加到隐式模型中,视图名称通过RequestToViewNameTranslator隐式确定。
@ModelAttribute要添加到模型中的属性,通过RequestToViewNameTranslator隐式确定视图名称。 注意,@ModelAttribute是可选的。参见该表末尾的“Any other return value”。
ModelAndView object要使用的视图和模型属性,以及可选的响应状态。
void具有void返回类型(或空返回值)的方法如果还具有ServletResponseOutputStream参数或@ResponseStatus注解,则被认为已经完全处理了响应。如果controller已经做了一个positive的ETaglastModified时间戳检查,也会发生同样的情况(详见Controllers)。 如果以上都不是true,void返回类型也可以为REST controllers指示“无响应体”,或为HTML controllers指示默认视图名称选择。
Any other return value如果返回值与上述任何一个都不匹配,并且不是简单类型(由 BeanUtils#isSimpleProperty确定),默认情况下,它被视为要添加到模型中的模型属性。如果是简单类型,则仍然无法解决。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值