詳解Spring boot/Spring 統(tǒng)一錯誤處理方案的使用
當(dāng)我們開發(fā)spring web應(yīng)用程序時,對于如 IOException , ClassNotFoundException 之類的檢查異常,往往編譯器會提示程序員采用 try-catch 進(jìn)行顯式捕獲,而對于像 ClassCastException , NullPointerException 這類非檢查異常,編譯器是不會提示你了,這往往也是能體現(xiàn)程序員代碼編寫能力的一個方面。
在spring web特別是spring-boot應(yīng)用中,當(dāng)一個請求調(diào)用成功時,一般情況下會返回 json 格式的對象,就像下面圖所示:
但如果請求拋出了一個 RuntimeException 呢?如果我們不做處理,再次調(diào)用時將出現(xiàn)下面的頁面:

也就是說當(dāng)調(diào)用出現(xiàn)錯誤時,spring-boot默認(rèn)會將請求映射到 /error 路徑中去,如果沒有相應(yīng)的路徑請求處理器,那么就會返回上面的 Whitelabel 錯誤頁面。
1、自定義錯誤處理頁面
當(dāng)然對運行時異常不做處理是不可能的啦!通常的做法是自定義統(tǒng)一錯誤頁面,然后返回。按照上面的思路,我們實現(xiàn)一個請求路徑為 /error 的控制器,控制器返回一個資源路徑地址,定義請求映射路徑為 /error 的控制器并實現(xiàn) ErrorController 接口,代碼如下:
MyErrorPageController
package com.example.demo.controller.handler.errorpage;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
*
* The class MyErrorPageController.
*
* Description:自定義錯誤頁面
*
* @author: huangjiawei
* @since: 2018年6月13日
* @version: $Revision$ $Date$ $LastChangedBy$
*
*/
@Controller
public class MyErrorPageController implements ErrorController {
@RequestMapping("/error")
public String handleError() {
return "error.html"; // 該資源位于resources/static目錄下
}
@Override
public String getErrorPath() {
return null;
}
}
然后在 reosurces/static 目錄下建立 error.html 文件:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <h1>這是個錯誤頁面!存放在resources/static目錄下,spring-boot發(fā)生錯誤時默認(rèn)調(diào)用</h1> </body> </html>
再次請求 http://localhost:7000/demo/getUserInfoWithNoHandler.json ,如下:
2、使用 @ControllerAdvice 、 @ResponseBody 、 @ExceptionHandler 統(tǒng)一處理異常
在spring中可以使用上面3個注解進(jìn)行統(tǒng)一異常處理,默認(rèn)情況下我們可以針對系統(tǒng)中出現(xiàn)的某種類型的異常定義一個統(tǒng)一的處理器handler,比如說系統(tǒng)拋出了一個 NullPointerException ,那么我們可以定義一個專門針對 NullPointerException 的處理器,代碼如下:
getUserInfoWithNullPointerException 接口
/**
* 測試空指針錯誤的處理
* @return
* @throws NullPointerException
*/
@RequestMapping(value = "getUserInfoWithNullPointerException.json", method = RequestMethod.GET)
public Student getUserInfoWithNullPointerException() throws NullPointerException {
throw new NullPointerException();
}
NullPointerExceptionHandler.java
package com.example.demo.controller.handler;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.demo.pojo.ErrorReturn;
/**
*
* The class NullPointerExceptionHandler.
*
* Description:處理空指針
*
* @author: huangjiawei
* @since: 2018年6月13日
* @version: $Revision$ $Date$ $LastChangedBy$
*
*/
@ControllerAdvice
public class NullPointerExceptionHandler {
@ExceptionHandler(NullPointerException.class)
@ResponseBody
public ErrorReturn dealNullPointerException() {
e.printStackTrace();
ErrorReturn error = new ErrorReturn();
error.setReturnCode("-1");
error.setDesc("出現(xiàn)空指針異常啦!");
return error;
}
}
瀏覽器執(zhí)行: http://localhost:7000/demo/getUserInfoWithNullPointerException.json

同樣的道理,如果我們還需要為其他的運行時異常提供統(tǒng)一的處理器,那么也可以像上面一樣為每一個異常類型定義一個處理器,比如我們又想為 ArithmeticException 定義處理器,那么我們只需要建立一個類或者方法,然后在方法上的 @ExceptionHanler 注解內(nèi)加上 ArithmeticException.class 指定異常類型即可。
不過你有沒有發(fā)現(xiàn),這樣為每種異常類型定義一個異常處理類或者方法,因為運行時異常類型特別多,不可能為每種類型都指定一個處理器類或方法,針對這種情況,spring也是可以解決的。如果我們沒有為某種特定類型異常,如 ArithmeticException 定義處理器,那么我們可以定義一個 Exception 或者 Throwable 處理器統(tǒng)一處理。
這樣做的好處是,減少了處理器類的數(shù)量,同時將異常處理轉(zhuǎn)移到父類上面去,這也是繼承的一大優(yōu)勢吧!但是,當(dāng)你既定義了特定類型的異常,同時又定義了 Exception 異常的處理器,那么要小心了,這里不一定有優(yōu)先級的關(guān)系,也就是說不一定會出現(xiàn)只執(zhí)行父異常處理器的情況,可能是只執(zhí)行A處理器,而不執(zhí)行B處理器或者只執(zhí)行B處理器,不執(zhí)行A處理器。如 NullPointerExceptionHandler 異常會向 Exception 異常傳遞(但 ArithmeticException 不會向 Exception 傳遞)
現(xiàn)在假設(shè)我們既定義上面的 NullPointerExceptionHandler ,又定義了下面的 ExceptionThrowableHandler ,那么當(dāng)發(fā)生 NullPointerException 時,就會默認(rèn)執(zhí)行 ExceptionThrowableHandler 的方法。
ExceptionThrowableHandler.java
package com.example.demo.controller.handler;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.demo.pojo.ErrorReturn;
/**
*
* The class ExceptionThrowableHandler.
*
* Description:有些異常會向高級別異常傳遞(但ArithmeticException不會向Exception傳送)
*
* @author: huangjiawei
* @since: 2018年6月13日
* @version: $Revision$ $Date$ $LastChangedBy$
*
*/
@ControllerAdvice
public class ExceptionThrowableHandler {
@ExceptionHandler(Throwable.class)
@ResponseBody
public ErrorReturn dealThrowable() {
ErrorReturn error = new ErrorReturn();
error.setDesc("處理Throwable!");
error.setReturnCode("-1");
return error;
}
@ExceptionHandler(Exception.class)
@ResponseBody
public ErrorReturn dealCommonException() {
ErrorReturn error = new ErrorReturn();
error.setReturnCode("-1");
error.setDesc("公共異常處理!");
return error;
}
}
瀏覽器執(zhí)行 : http://localhost:7000/demo/getUserInfoWithNullPointerException.json

可以發(fā)現(xiàn)只執(zhí)行 Exception 的處理器,沒有執(zhí)行空指針的處理器,也就是異常處理往上傳送了。下面再來看看拋出 ArithmeticException 的情況:
getUserInfoWithArithmeticException.json
/**
* 測試空指針錯誤的處理
* @return
* @throws NullPointerException
*/
@RequestMapping(value = "getUserInfoWithArithmeticException.json", method = RequestMethod.GET)
public Student getUserInfoWithArithmeticException() throws ArithmeticException {
throw new ArithmeticException();
}
ArithmeticExceptionHandler.java
package com.example.demo.controller.handler;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.demo.pojo.ErrorReturn;
@ControllerAdvice
public class ArithmeticExceptionHandler {
/**
* 處理ArithmeticException異常
* @return
*/
@ResponseBody
@ExceptionHandler(ArithmeticException.class)
public ErrorReturn dealArithmeticException() {
ErrorReturn errorObject = new ErrorReturn();
errorObject.setReturnCode("-1");
errorObject.setDesc("算數(shù)處理出現(xiàn)異常!");
return errorObject;
}
}
瀏覽器執(zhí)行 : http://localhost:7000/demo/getUserInfoWithArithmeticException.json

結(jié)果發(fā)現(xiàn)異常處理并沒有往上層的 ExceptionHandler 傳送。
總結(jié):對于既定義特定類型的處理器,又定義 Exception 等父類型的處理器時要特別小心,并不是所有的異常都會往上級處理,如果我們想只減少處理器類的數(shù)量,不想為每種特定類型的處理器添加類或者方法,那么小編建議使用 instanceof 關(guān)鍵字對異常類型進(jìn)行判斷即可。
如下面的代碼,我們只建立一個公共的異常處理器,處理 Exception 異常,同時使用 instanceof 進(jìn)行判斷。
@ExceptionHandler(Exception.class)
@ResponseBody
public ErrorReturn dealCommonException(Exception e) {
ErrorReturn error = new ErrorReturn();
// 此處可以采用 instanceof 判斷異常類型
if (e instanceof ArithmeticException) {
error.setReturnCode("-1");
error.setDesc("算數(shù)異常處理!");
return error;
}
System.err.println("exception");
error.setReturnCode("-1");
error.setDesc("公共異常處理!");
return error;
}
瀏覽器執(zhí)行拋出 ArithmeticException 的接口,如下:

本文代碼地址: https://github.com/SmallerCoder/spring_exceptionHandler
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Windows系統(tǒng)下Java連接SQL Server的方法簡介
這篇文章主要介紹了Windows系統(tǒng)下Java連接SQL Server的方法,分別是JDBC和JTDS的相關(guān)使用,需要的朋友可以參考下2015-09-09
ArrayList?foreach循環(huán)增添刪除導(dǎo)致ConcurrentModificationException解決分
這篇文章主要為大家介紹了ArrayList?foreach循環(huán)增添刪除導(dǎo)致ConcurrentModificationException解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2023-12-12
淺析java中ArrayList與Vector的區(qū)別以及HashMap與Hashtable的區(qū)別
以下是對java中ArrayList與Vector的區(qū)別以及HashMap與Hashtable的區(qū)別進(jìn)行了詳細(xì)的解析。需要的朋友可以過來參考下2013-08-08

