简概
异常处理⽅法
-
处理⽅法
- @ExceptionHandler
-
添加位置
- @Controller / @RestController
- @ControllerAdvice / @RestControllerAdvice
@RestControllerAdvice
或者@ControllerAdvice
类内的解析器的优先级低于@Controller
或@RestController
类的解析器的优先级- 如果一个异常能被多个解析器所处理,则选择继承关系最近的解析器
异常处理方法-@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
变体中,通常使用实际的FileSystemException
或RemoteException
实例作为参数调用该方法,因为它们都是从IOException
扩展而来的。但是,如果任何这样的匹配异常在包装器异常中传播,而包装器异常本身就是一个IOException
,则传入的异常实例就是那个包装器异常。
在handle(Exception)
变体中,这种行为甚至更简单。在包装场景中,这总是与包装器异常一起调用,在这种情况下,通过ex.getCause()
找到实际匹配的异常。传入的异常只有在作为顶级异常抛出时才是实际的FileSystemException
或RemoteException
实例。
通常建议在参数签名中尽可能具体,以减少根源异常类型和原因异常类型之间不匹配的可能性。考虑将多匹配方法分解为单独的@ExceptionHandler方法,每个方法通过其签名匹配单个特定的异常类型。
在多个@ControllerAdvice
中,建议按相应顺序优先级排列的@ControllerAdvice
上声明主根异常映射。虽然根异常匹配优先于原因,但这是在一个给定controller
或@ControllerAdvice
类的方法中定义的。这也就是说高优先级的@ControllerAdvice bean
上的原因匹配优先于低优先级的@ControllerAdvice bean
上的任何匹配(例如,根)。
最后但并非最不重要的是,@ExceptionHandler
方法实现可以通过以原始形式重新抛出给定异常实例来选择退出处理。如果您只对根级匹配感兴趣,或者对无法静态确定的特定上下文中的匹配感兴趣,那么这将非常有用。重新抛出的异常通过剩余的解析链传播,就好像给定的@ExceptionHandler
方法一开始就不匹配一样。
Spring MVC中对@ExceptionHandler
方法的支持是建立在DispatcherServlet
级别、HandlerExceptionResolver
机制上的。
Controller Advice
@ExceptionHandler
、@InitBinder
和@ModelAttribute
三个注解标记的方法只作用于声明它们的@Controller
类或类层次结构。而如果它们在@ControllerAdvice
或@RestControllerAdvice
类中声明,那么它们将应用于任何controller
。从5.3开始,@ControllerAdvice
中的@ExceptionHandler
方法可以用于处理来自任何@Controller或任何其他处理程序的异常。
@ControllerAdvice
使用@Component
进行元注释,因此可以通过组件扫描注册为Spring bean。@RestControllerAdvice
使用@ControllerAdvice
和@ResponseBody
进行元注释,这意味着@ExceptionHandler
方法的返回值将通过响应体消息转换呈现,而不是通过HTML视图。
在启动时,RequestMappingHandlerMapping
和ExceptionHandlerExceptionResolver
检测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 | 选择任何特定的请求或响应类型(例如,ServletRequest 或HttpServletRequest 或Spring的MultipartRequest 或MultipartHttpServletRequest )。 |
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。 |
String | 用ViewResolver 实现解析并与隐式模型一起使用的视图名称——通过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 返回类型(或空返回值)的方法如果还具有ServletResponse 、OutputStream 参数或@ResponseStatus 注解,则被认为已经完全处理了响应。如果controller 已经做了一个positive的ETag 或lastModified 时间戳检查,也会发生同样的情况(详见Controllers )。 如果以上都不是true,void 返回类型也可以为REST controllers 指示“无响应体”,或为HTML controllers 指示默认视图名称选择。 |
Any other return value | 如果返回值与上述任何一个都不匹配,并且不是简单类型(由 BeanUtils#isSimpleProperty 确定),默认情况下,它被视为要添加到模型中的模型属性。如果是简单类型,则仍然无法解决。 |