Spring MVC 異常處理
1. 前言
本章節(jié)將和大家一起聊聊 Spring MVC 是如何處理異常的。
Spring MVC 框架中,將異常處理從業(yè)務邏輯中解耦出來,既保證了業(yè)務邏輯高度內(nèi)聚性,又能實現(xiàn)了異常信息的統(tǒng)一處理和維護。很優(yōu)雅的解決了異常的問題。通過本章節(jié)內(nèi)容的學習,你將了解到 Spring MVC 內(nèi)置的異常處理機制。
- 將異常映射成 HTTP 狀態(tài)碼;
- 全局異常處理器的使用。這個將是本章節(jié)的重點;
- 與異常有關(guān)的注解。
2. 異常處理原則
異常是程序運行過程中不可避免的問題。異常出現(xiàn)的原因很多,但不管怎樣,都需要提前預知或者當異常發(fā)生后采取相應的處理措施。
異常的處理原則是:
- 能預知的盡可能在邏輯層面提前制止。如用戶注冊時,要求登錄名是唯一的,可先檢查數(shù)據(jù)庫是否存在同名用戶名后,再進行添加操作;
- 以一種友好的方式告知使用者出錯的原因;
- 采用多層體系結(jié)構(gòu)的項目中,建議異常由下逐層向上拋出,一直到達應用層面;
- 使用日志記錄功能把異常信息記錄在日志文件中,便于開發(fā)者分析。
如下面的控制器方法:
@Controller
public class ExceptionAction {
@RequestMapping("/exception01")
public String exception01(@RequestParam("userName") String userName) {
return "exception";
}
}
在瀏覽器中輸入:http://localhost:8888/sm-demo/exception01 ,頁面中會出現(xiàn)錯誤提示。
這個原因是 @RequestParam(“userName”) 注解在默認情況下,要求請求包中一定要有 userName 這個參數(shù)。
顯然,頁面中顯示出來的錯誤信息是不友好的。所謂的異常處理,并不能完全阻止異常的發(fā)生。而是把異常信息對外、對內(nèi)做一個封裝,換一個淺白的、直接的、非專業(yè)的方式告訴使用者。
對于前面的異常解決方案,可以在 @RequestParam(value = “userName”,required = false) 中添加一個 required = false 的設(shè)置。這是一種最理想的異常解決方案。
3. Spring MVC 的異常處理方案
3.1 將異常映射成狀態(tài)碼
Spring MVC 內(nèi)部提供了很多優(yōu)雅的異常處理機制。其中之一就是把不同的異常映射成 HTTP 狀態(tài)碼。
如下面實例:
- 首先定義一個異常類:
@ResponseStatus(value = HttpStatus.FORBIDDEN, reason = "數(shù)字范圍不符合要求!(不能是 20)")
public class NumberException extends RuntimeException {
}
- 如果在方法中拋出的異常已經(jīng)被映射成了狀態(tài)嗎。則在瀏覽器中顯示出來的異常信息會明確很多。
@RequestMapping("/exception")
public String test(@RequestParam("num") int num) {
if (num == 20) {
throw new NumberException();
}
return "success";
}
在瀏覽器中輸入 http://localhost:8888/sm-demo/exception?num=13 ??梢钥吹剑?/p>
定制化的信息已經(jīng)有所改善,但是,還是不夠友好。
3.2 全局異常處理器
Spring MVC 提供了名為 SimpleMappingExceptionResolver 的異常處理組件,該組件實現(xiàn)了 HandlerExceptionResolver 接口,或者說實現(xiàn)了這個接口的對象都可稱其為全局異常處理器。
何謂全局異常處理器?
通俗講,有點類似于前端控制器的設(shè)計思路。Spring MVC 把所有異常分離出來后通通交給全局異常處理器做集中處理。
使用流程:
- 打開項目中的 WebConfig 配置類,添加組件;
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver simResolver=new SimpleMappingExceptionResolver();
//異常處理頁面
simResolver.setDefaultErrorView("error");
//封裝異常信息的屬性名,默認是 exception
simResolver.setExceptionAttribute("exception");
//添加自定義異常信息
Properties mappings=new Properties();
mappings.put("com.mk.web.exception.MyException", "/WEB-INF/jsp/exception.jsp");
simResolver.setExceptionMappings(mappings);
return simResolver;
}
代碼中有注解,不再多言。
- 自定義異常類。自定義異常類并不是必須的,項目中自定義異常的目的可以讓異常的語義更具體;
public class MyException extends Exception {
public MyException() {
}
public MyException(String msg) {
super(msg);
}
}
- 編寫控制器??刂破髦械姆椒〞鶕?jù) userName 的值決定是否拋出異常。
@RequestMapping("/exception03")
public String exception03(String userName) throws MyException {
if (StringUtils.isEmpty(userName)) {
throw new MyException("用戶名不能為空");
}
return "index";
}
- 測試。打開瀏覽器,輸入 http://localhost:8888/sm-demo/exception03 ;
- 瀏覽器會顯示把錯誤導向到 “WEB-INF/exception.jsp” 頁面。此頁面,可添加下面的代碼。
<body>
出錯啦!${exception.message}
</body>
解析出錯誤的具體信息,最后可以在瀏覽器中看到:
開發(fā)者可以根據(jù)需要編寫自己的全局異常處理器組件。
3.3 與異常有關(guān)的注解
Spring MVC 還可以使用注解的方式集中處理異常。
@ExceptionHandler 注解:
此注解所標注的方法能夠處理同一個控制器中所有方法所拋出的異常。
如下面實例:
@ExceptionHandler(Exception.class)
public String exception() {
return "exception";
}
@RequestMapping("/exception04")
public String exception04(String userName) throws Exception {
if (StringUtils.isEmpty(userName)) {
throw new Exception("用戶名不能為空");
}
return "index";
}
當控制器中的方法拋出異常后,會由 exception() 方法統(tǒng)一捕獲,然后跳到指定頁面。
@ControllerAdvice 注解:
此注解放在類的前面,且類中可以包含一個或多個如下類型的方法。
Tips: 由 @ControllerAdvice 注解標注的類本質(zhì)上就是一個基于 AOP 思想的攔截器。
- @ExceptionHandler 注解標注的方法;
- @InitBinder 注解標注的方法;
- @ModelAttribute 注解標注的方法。
@ControllerAdvice 實現(xiàn)案例如下:
@ControllerAdvice
public class ExceptionAdviceAction {
@ExceptionHandler(Exception.class)
public String exception() {
return "exception";
}
}
無論哪一個控制器中拋出異常,都會由 ExceptionAdviceAction 類中的 exception() 方法統(tǒng)一響應處理。
4. 小結(jié)
本章節(jié)和大家講解了 Spring MVC 中如何優(yōu)雅的處理異常。主要有 3 種方案:
- 將異常映射成為 HTTP 狀態(tài)碼;
- 使用全局異常處理組件。建議大家使用這種方式,具有很多的隔離性、統(tǒng)一性;
- 使用注解的方式處理異常。