Spring boot/Spring 統一錯誤處理方案的使用

當咱們開發spring web應用程序時,對於如IOException,ClassNotFoundException之類的檢查異常,每每編譯器會提示程序員採用try-catch進行顯式捕獲,而對於像ClassCastException,NullPointerException這類非檢查異常,編譯器是不會提示你了,這每每也是能體現程序員代碼編寫能力的一個方面。html

在spring web特別是spring-boot應用中,當一個請求調用成功時,通常狀況下會返回json格式的對象,就像下面圖所示:java

但若是請求拋出了一個RuntimeException呢?若是咱們不作處理,再次調用時將出現下面的頁面:git

也就是說當調用出現錯誤時,spring-boot默認會將請求映射到/error路徑中去,若是沒有相應的路徑請求處理器,那麼就會返回上面的Whitelabel錯誤頁面。程序員

一、自定義錯誤處理頁面

固然對運行時異常不作處理是不可能的啦!一般的作法是自定義統一錯誤頁面,而後返回。按照上面的思路,咱們實現一個請求路徑爲/error的控制器,控制器返回一個資源路徑地址,定義請求映射路徑爲/error的控制器並實現ErrorController接口,代碼以下:github

MyErrorPageControllerweb

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文件:spring

<!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,以下:json

二、使用@ControllerAdvice@ResponseBody@ExceptionHandler統一處理異常

在spring中可使用上面3個註解進行統一異常處理,默認狀況下咱們能夠針對系統中出現的某種類型的異常定義一個統一的處理器handler,好比說系統拋出了一個NullPointerException,那麼咱們能夠定義一個專門針對NullPointerException的處理器,代碼以下:瀏覽器

getUserInfoWithNullPointerException接口bash

/**
 * 測試空指針錯誤的處理
 * @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("出現空指針異常啦!");
    	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$
 *
 */
@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;
    }
}
複製代碼

瀏覽器執行 : http://localhost:7000/demo/getUserInfoWithNullPointerException.json

能夠發現只執行Exception的處理器,沒有執行空指針的處理器,也就是異常處理往上傳送了。下面再來看看拋出ArithmeticException的狀況:

getUserInfoWithArithmeticException.json

/**
 * 測試ArithmeticException錯誤的處理
 * @return
 * @throws ArithmeticException
 */
@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("算數處理出現異常!");
    	return errorObject;
    }
}
複製代碼

瀏覽器執行 : http://localhost:7000/demo/getUserInfoWithArithmeticException.json

結果發現異常處理並無往上層的ExceptionHandler傳送。

總結:對於既定義特定類型的處理器,又定義Exception等父類型的處理器時要特別當心,並非全部的異常都會往上級處理,若是咱們想只減小處理器類的數量,不想爲每種特定類型的處理器添加類或者方法,那麼小編建議使用instanceof關鍵字對異常類型進行判斷便可。

以下面的代碼,咱們只創建一個公共的異常處理器,處理Exception異常,同時使用instanceof進行判斷。

@ExceptionHandler(Exception.class)
@ResponseBody
public 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的接口,以下:

本文代碼地址: github.com/SmallerCode…

謝謝閱讀,歡迎start,指正!

相關文章
相關標籤/搜索