ExceptionHandler的執行順序

在項目開發中常常會遇到統一異常處理的問題,在springMVC中有一種解決方式,使用ExceptionHandler。舉個例子,java

@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler({IllegalArgumentException.class})
    @ResponseBody
    public Result handleIllegalArgumentException(IllegalArgumentException e) {
        logger.error(e.getLocalizedMessage(), e);
        return Result.fail(e.getMessage());
    }

    @ExceptionHandler({RuntimeException.class})
    @ResponseBody
    public Result handleRuntimeException(RuntimeException e) {
        logger.error(e.getLocalizedMessage(), e);
        return Result.failure();
    }
}
複製代碼

在這段代碼中,咱們能夠看到存在兩個異常處理的函數分別處理IllegalArgumentException和RuntimeException,可是轉念一想,就會想到一個問題,IllegalArgumentException是RuntimeException的子類,那麼對IllegalArgumentException這個異常又會由誰來處理呢?起初在網上看到一些答案,能夠經過Order設置,可是通過簡單的測試,發現Order並不起任何做用。雖然心中已有猜想,但仍是但願可以找到真正能夠證實想法的證據,因而便嘗試找到這一塊的源碼。算法

源碼解讀

調用棧

排出掉緩存的狀況,主動觸發一個IllegalArgumentException異常,通過一步步調試,發現調用棧以下:spring

image-20190326180205336

核心代碼

決定最終選擇哪一個ExceptionHandler的核心代碼爲ExceptionHandlerMethodResolver的getMappedMethod方法。代碼以下:緩存

private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
  List<Class<? extends Throwable>> matches = new ArrayList<Class<? extends Throwable>>();
  for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
    if (mappedException.isAssignableFrom(exceptionType)) {
      matches.add(mappedException);
    }
  }
  if (!matches.isEmpty()) {
    Collections.sort(matches, new ExceptionDepthComparator(exceptionType));
    return this.mappedMethods.get(matches.get(0));
  }
  else {
    return null;
  }
}
複製代碼

這個首先找到能夠匹配異常的全部ExceptionHandler,而後對其進行排序,取深度最小的那個(即匹配度最高的那個)。app

至於深度比較器的算法以下圖,就是作了一個簡單的遞歸,不停地判斷父異常是否爲目標異常來取得最終的深度。函數

image-20190327224336509

結論

源碼不長,咱們也能夠很容易地就找到咱們想要的答案——ExceptionHandler的處理順序是由異常匹配度來決定的,且咱們也沒法經過其餘途徑指定順序(其實也沒有必要)。測試

相關文章
相關標籤/搜索