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