當我們開發spring web應用程序時,對於如IOException , ClassNotFoundException之類的檢查異常,往往編譯器會提示程序員採用try-catch進行顯式捕獲,而對於像ClassCastException , NullPointerException這類非檢查異常,編譯器是不會提示你了,這往往也是能體現程序員代碼編寫能力的一個方面。
在spring web特別是spring-boot應用中,當一個請求調用成功時,一般情況下會返回json格式的對象,就像下面圖所示:
但如果請求拋出了一個RuntimeException呢?如果我們不做處理,再次調用時將出現下面的頁面:
也就是說當調用出現錯誤時,spring-boot默認會將請求映射到/error路徑中去,如果沒有相應的路徑請求處理器,那麼就會返回上面的Whitelabel錯誤頁面。
1、自定義錯誤處理頁面
當然對運行時異常不做處理是不可能的啦!通常的做法是自定義統一錯誤頁面,然後返回。按照上面的思路,我們實現一個請求路徑為/error的控制器,控制器返回一個資源路徑地址,定義請求映射路徑為/error的控制器並實現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$ * */@Controllerpublic 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發生錯誤時默認調用</h1></body></html>
再次請求http://localhost:7000/demo/getUserInfoWithNoHandler.json ,如下:
@ControllerAdvice 、 @ResponseBody 、 @ExceptionHandler統一處理異常
在spring中可以使用上面3個註解進行統一異常處理,默認情況下我們可以針對系統中出現的某種類型的異常定義一個統一的處理器handler,比如說系統拋出了一個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$ * */@ControllerAdvicepublic class NullPointerExceptionHandler { @ExceptionHandler(NullPointerException.class) @ResponseBody public ErrorReturn dealNullPointerException() { e.printStackTrace(); ErrorReturn error = new ErrorReturn(); error.setReturnCode("-1"); error.setDesc("出現空指針異常啦!"); return error; }}瀏覽器執行: http://localhost:7000/demo/getUserInfoWithNullPointerException.json
同樣的道理,如果我們還需要為其他的運行時異常提供統一的處理器,那麼也可以像上面一樣為每一個異常類型定義一個處理器,比如我們又想為ArithmeticException定義處理器,那麼我們只需要建立一個類或者方法,然後在方法上的@ExceptionHanler註解內加上ArithmeticException.class指定異常類型即可。
不過你有沒有發現,這樣為每種異常類型定義一個異常處理類或者方法,因為運行時異常類型特別多,不可能為每種類型都指定一個處理器類或方法,針對這種情況,spring也是可以解決的。如果我們沒有為某種特定類型異常,如ArithmeticException定義處理器,那麼我們可以定義一個Exception或者Throwable處理器統一處理。
這樣做的好處是,減少了處理器類的數量,同時將異常處理轉移到父類上面去,這也是繼承的一大優勢吧!但是,當你既定義了特定類型的異常,同時又定義了Exception異常的處理器,那麼要小心了,這裡不一定有優先級的關係,也就是說不一定會出現只執行父異常處理器的情況,可能是只執行A處理器,而不執行B處理器或者只執行B處理器,不執行A處理器。如NullPointerExceptionHandler異常會向Exception異常傳遞(但ArithmeticException不會向Exception傳遞)
現在假設我們既定義上面的NullPointerExceptionHandler ,又定義了下面的ExceptionThrowableHandler ,那麼當發生NullPointerException時,就會默認執行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$ * */@ControllerAdvicepublic 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; }}瀏覽器執行: http://localhost:7000/demo/getUserInfoWithNullPointerException.json
可以發現只執行Exception的處理器,沒有執行空指針的處理器,也就是異常處理往上傳送了。下面再來看看拋出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;@ControllerAdvicepublic class ArithmeticExceptionHandler { /** * 處理ArithmeticException異常* @return */ @ResponseBody @ExceptionHandler(ArithmeticException.class) public ErrorReturn dealArithmeticException() { ErrorReturn errorObject = new ErrorReturn(); errorObject.setReturnCode("-1"); errorObject.setDesc("算數處理出現異常!"); return errorObject; }}瀏覽器執行: http://localhost:7000/demo/getUserInfoWithArithmeticException.json
結果發現異常處理並沒有往上層的ExceptionHandler傳送。
總結:對於既定義特定類型的處理器,又定義Exception等父類型的處理器時要特別小心,並不是所有的異常都會往上級處理,如果我們想只減少處理器類的數量,不想為每種特定類型的處理器添加類或者方法,那麼小編建議使用instanceof關鍵字對異常類型進行判斷即可。
如下面的代碼,我們只建立一個公共的異常處理器,處理Exception異常,同時使用instanceof進行判斷。
@ExceptionHandler(Exception.class)@ResponseBodypublic ErrorReturn dealCommonException(Exception e) { ErrorReturn error = new ErrorReturn(); // 此處可以採用instanceof 判斷異常類型if (e instanceof ArithmeticException) { error.setReturnCode("-1"); error.setDesc("算數異常處理!"); return error; } System.err.println("exception"); error.setReturnCode("-1"); error.setDesc("公共異常處理!"); return error;}瀏覽器執行拋出ArithmeticException的接口,如下:
本文代碼地址: https://github.com/SmallerCoder/spring_exceptionHandler
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。