对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

异常

@Controller@ControllerAdvice 类可以包含 @ExceptionHandler 方法,用于处理控制器方法抛出的异常,如下例所示:spring-doc.cadn.net.cn

@Controller
public class SimpleController {

	// ...

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

	// ...

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

该异常可以匹配正在传播的顶层异常(例如直接抛出的IOException),也可以匹配包装异常内部的嵌套原因(例如被包装在IOException中的IllegalStateException)。从5.3版本开始,此匹配可以应用于任意层级的异常原因,而此前仅考虑直接原因。spring-doc.cadn.net.cn

为了匹配异常类型,建议将目标异常声明为方法参数,如前面的示例所示。当多个异常处理方法匹配时,通常优先选择与根异常(root exception)匹配的方法,而不是与原因异常(cause exception)匹配的方法。更具体地说,系统会使用 ExceptionDepthComparator 根据异常类型与所抛出异常之间的继承深度对异常进行排序。spring-doc.cadn.net.cn

或者,注解声明可以缩小要匹配的异常类型范围,如下例所示:spring-doc.cadn.net.cn

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

你甚至可以使用一个特定异常类型的列表,并配合非常通用的参数签名,如下例所示:spring-doc.cadn.net.cn

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

根异常与原因异常的匹配区别可能会令人感到意外。spring-doc.cadn.net.cn

在前面所示的 IOException 变体中,该方法通常以实际的 FileSystemExceptionRemoteException 实例作为参数调用,因为这两者都继承自 IOException。然而,如果任何此类匹配的异常被封装在一个本身也是 IOException 的包装异常中进行传播,则传入的异常实例就是该包装异常。spring-doc.cadn.net.cn

handle(Exception) 的变体中,行为甚至更为简单。在异常包装的场景下,该方法总是以包装后的异常被调用,此时实际匹配的异常需要通过 ex.getCause() 来获取。 只有当 FileSystemExceptionRemoteException 作为顶层异常被抛出时,传入的异常才是这些异常的实际实例。spring-doc.cadn.net.cn

我们通常建议您在方法参数签名中尽可能具体,以减少根异常类型与原因异常类型之间不匹配的可能性。 考虑将一个匹配多种异常的方法拆分为多个单独的 @ExceptionHandler 方法,每个方法通过其签名仅匹配一种特定的异常类型。spring-doc.cadn.net.cn

在多个 @ControllerAdvice 的配置中,我们建议将主要的根异常映射声明在一个通过相应顺序(order)设置为高优先级的 @ControllerAdvice 上。虽然对于某个给定的控制器或 @ControllerAdvice 类中的方法而言,根异常匹配优于原因(cause)匹配,但这仅限于该类内部的方法之间进行比较。这意味着,高优先级 @ControllerAdvice Bean 中对异常原因的匹配,会优先于低优先级 @ControllerAdvice Bean 中的任何匹配(例如根异常匹配)。spring-doc.cadn.net.cn

最后但同样重要的是,@ExceptionHandler 方法的实现可以选择通过以原始形式重新抛出给定的异常实例,从而放弃处理该异常。 这在你只对根级别匹配或特定上下文中的匹配感兴趣(而这些上下文无法静态确定)的场景中非常有用。被重新抛出的异常会继续沿着剩余的解析链传播,就好像该 @ExceptionHandler 方法从一开始就没有匹配到一样。spring-doc.cadn.net.cn

Spring MVC 中对 @ExceptionHandler 方法的支持是建立在 DispatcherServlet 层级的 HandlerExceptionResolver 机制之上的。spring-doc.cadn.net.cn

方法参数

@ExceptionHandler 方法支持以下参数:spring-doc.cadn.net.cn

方法参数 <description> </description>

异常类型spring-doc.cadn.net.cn

用于访问抛出的异常。spring-doc.cadn.net.cn

HandlerMethodspring-doc.cadn.net.cn

用于访问引发异常的控制器方法。spring-doc.cadn.net.cn

WebRequest, NativeWebRequestspring-doc.cadn.net.cn

无需直接使用 Servlet API,即可通用地访问请求参数、请求属性和会话属性。spring-doc.cadn.net.cn

jakarta.servlet.ServletRequest, jakarta.servlet.ServletResponsespring-doc.cadn.net.cn

选择任意特定的请求或响应类型(例如,ServletRequestHttpServletRequest,或者 Spring 的 MultipartRequestMultipartHttpServletRequest)。spring-doc.cadn.net.cn

jakarta.servlet.http.HttpSessionspring-doc.cadn.net.cn

强制要求会话存在。因此,此类参数永远不会null.
请注意,会话访问不是线程安全的。请考虑设置RequestMappingHandlerAdapter实例的synchronizeOnSession标记为true如果允许多个 请求并发访问同一个会话。spring-doc.cadn.net.cn

java.security.Principalspring-doc.cadn.net.cn

当前已认证的用户——如果已知,可能是某个特定的 Principal 实现类。spring-doc.cadn.net.cn

HttpMethodspring-doc.cadn.net.cn

请求的 HTTP 方法。spring-doc.cadn.net.cn

java.util.Localespring-doc.cadn.net.cn

当前请求的区域设置,由可用的最具体的 LocaleResolver 确定——实际上,即所配置的 LocaleResolverLocaleContextResolverspring-doc.cadn.net.cn

java.util.TimeZone, java.time.ZoneIdspring-doc.cadn.net.cn

LocaleContextResolver 确定的与当前请求关联的时区。spring-doc.cadn.net.cn

java.io.OutputStream, java.io.Writerspring-doc.cadn.net.cn

用于访问由 Servlet API 暴露的原始响应体。spring-doc.cadn.net.cn

java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMapspring-doc.cadn.net.cn

用于访问错误响应的模型。始终为空。spring-doc.cadn.net.cn

RedirectAttributesspring-doc.cadn.net.cn

指定在重定向时使用的属性——(即附加到查询字符串中)以及临时存储的 Flash 属性,这些属性会保留到重定向后的下一次请求为止。 参见 重定向属性Flash 属性spring-doc.cadn.net.cn

@SessionAttributespring-doc.cadn.net.cn

用于访问任何会话属性,这与由于类级别的 @SessionAttributes 声明而存储在会话中的模型属性形成对比。 请参阅 @SessionAttribute 以获取更多详细信息。spring-doc.cadn.net.cn

@RequestAttributespring-doc.cadn.net.cn

用于访问请求属性。请参阅 @RequestAttribute 了解更多详情。spring-doc.cadn.net.cn

返回值

@ExceptionHandler 方法支持以下返回值:spring-doc.cadn.net.cn

返回值 <description> </description>

@ResponseBodyspring-doc.cadn.net.cn

返回值通过 HttpMessageConverter 个实例进行转换并写入响应。请参阅 @ResponseBodyspring-doc.cadn.net.cn

HttpEntity<B>, ResponseEntity<B>spring-doc.cadn.net.cn

返回值指定整个响应(包括 HTTP 头部和响应体)应通过 HttpMessageConverter 实例进行转换并写入响应中。 参见 ResponseEntityspring-doc.cadn.net.cn

ErrorResponsespring-doc.cadn.net.cn

要在响应体中包含详细信息以渲染 RFC 7807 错误响应, 请参阅错误响应spring-doc.cadn.net.cn

ProblemDetailspring-doc.cadn.net.cn

要在响应体中包含详细信息以渲染 RFC 7807 错误响应, 请参阅错误响应spring-doc.cadn.net.cn

Stringspring-doc.cadn.net.cn

一个视图名称,将通过 ViewResolver 实现进行解析,并与隐式模型一起使用——该隐式模型通过命令对象和 @ModelAttribute 方法确定。 处理器方法也可以通过声明一个 Model 参数(如前所述)以编程方式丰富模型。spring-doc.cadn.net.cn

Viewspring-doc.cadn.net.cn

一个用于渲染的 View 实例,连同隐式模型一起使用——该模型通过命令对象和 @ModelAttribute 方法确定。处理方法也可以通过声明一个 Model 参数(如前所述)以编程方式丰富模型。spring-doc.cadn.net.cn

java.util.Map, org.springframework.ui.Modelspring-doc.cadn.net.cn

通过 RequestToViewNameTranslator 隐式确定视图名称时,要添加到隐式模型中的属性。spring-doc.cadn.net.cn

@ModelAttributespring-doc.cadn.net.cn

一个要添加到模型中的属性,其视图名称通过 RequestToViewNameTranslator 隐式确定。spring-doc.cadn.net.cn

请注意,@ModelAttribute 是可选的。参见本表格末尾的“任何其他返回值”。spring-doc.cadn.net.cn

ModelAndView 对象spring-doc.cadn.net.cn

要使用的视图和模型属性,以及可选的响应状态。spring-doc.cadn.net.cn

voidspring-doc.cadn.net.cn

一个返回类型为 void(或返回值为 null)的方法,如果它还包含一个 ServletResponse 和一个 OutputStream 参数,或者带有 @ResponseStatus 注解,则被视为已完全处理了响应。如果控制器进行了正面的 ETaglastModified 时间戳检查,情况也是如此(详见 控制器)。spring-doc.cadn.net.cn

如果以上情况均不成立,void 返回类型也可以表示 REST 控制器“无响应体”,或 HTML 控制器使用默认视图名称进行选择。spring-doc.cadn.net.cn

任何其他返回值spring-doc.cadn.net.cn

如果返回值不匹配上述任何一种情况,并且不是简单类型(由 BeanUtils#isSimpleProperty 判定), 默认情况下,它将被视为模型属性并添加到模型中。如果它是简单类型, 则保持未解析状态。spring-doc.cadn.net.cn