# Spring MVC 中的统一异常处理
# Spring MVC 中跳转自定义 404 页面的两种常见方式
# 在 web.xml 中指定自定义的 404 页面
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/jsp/404.jsp</location>
</error-page>
# 提供一个匹配 * 的 Controller 请求处理方法
// 凡是不能精确匹配的 url,都会由这个请求处理方法处理
@RequestMapping("*")
public String error404() {
return "404";
}
# 使用 @ExceptionHandler 注解
@ExceptionHandler 注解用于标注于某个 Controller 的方法上,例如:
@Controller
public class GlobalController {
/**
* 用于处理异常的
*/
@ExceptionHandler({AException.class, BException.class, ...})
public String exception(MyException e) {
...
}
@RequestMapping("test1")
public void test2() {
...
}
@RequestMapping("test2")
public void test2() {
...
}
}
当 Controller 的请求方法发生指定异常时,@ExceptionHandler 所标注的方法将会被执行。返回给服务器的数据也就由该方法的返回值决定。
@ExceptionHandler 所标注的方法的常见参数有以下几种:
一个异常参数。代表所发生的异常(最常用)
HttpServletRequest / HttpServletResponse 对象
HttpSession 对象
Model 对象
等等。
@ExceptionHandler 所标注的方法的返回值的类型支持:
ModelAndView 对象(最常用)
Model 对象
Map 对象
View 对象
被解析成一个视图名称的 String 值(次常用)
等等
另外,可以将 @ResponseStatus 与 @ExceptionHandler 结合使用,来定义 HTTP 响应的响应状态。
可以看到,这种方式最大的缺陷就是不能全局控制异常。每个类都要写一遍。
# 使用 @ControllerAdvice + @ExceptionHandler 注解
@ExceptionHandler 需要进行异常处理的方法必须与出错的方法在同一个 Controller 里面。那么当代码加入了 @ControllerAdvice,则不需要必须在同一个 controller 中了。
这也是 Spring 3.2 带来的新特性。从名字上可以看出大体意思是控制器增强。
# 使用示例
请确保此 WebExceptionHandle 类能被扫描到并装载进 Spring 容器中。由于标注了 @ControllerAdvice 注解,在开启了包扫描的情况下,Spring 会创建该类的单例对象。
@Slf4j
@ControllerAdvice(assignableTypes = FirstController.class)
public class FirstControllerExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public String demo1(Exception e) {
log.warn("IllegalArgumentException");
return "";
}
@ExceptionHandler(NullPointerException.class)
public String demo(Exception e) {
log.warn("NullPointerException");
return "";
}
}
上述方法中的参数对象就是 Controller 层抛出的异常对象。注意,该方法如果有第二个参数,那么该机制会失效,无法捕获-处理 Controller 抛出的异常!
# 控制生效的 Controller 范围
注意到,我是这样编写注解的:
@ControllerAdvice(assignableTypes = { FirstController.class })
它用来限定这些异常处理函数起作用的 Controller 的范围。如果不写,则默认对所有 Controller 有效。
这也是 ControllerAdvice 进行统一异常处理的优点,它能够细粒度的控制该异常处理器针对哪些 Controller 有效,这样的好处是:
一个系统里就能够存在不同的异常处理器,Controller 也可以有选择的决定使用哪个,更加灵活。
不同的业务模块可能对异常处理的方式不同,通过该机制就能做到。
设想一个一开始并未使用全局异常处理的系统,如果直接引入全局范围内生效的全局异常处理,势必可能会改变已有 Controller 的行为,有侵入性。 也就是说,如果不控制生效范围,即默认对所有 Controller 生效。如果控制生效范围,则默认对所有 Controller 不生效,降低侵入性。
ControllerAdvice 支持的限定范围:
按注解:@ControllerAdvice(annotations = RestController.class)
按包名:@ControllerAdvice("org.example.controllers")
按类型:@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
← Spring MVC 中文件下载 数据校验 →