Порядок выполнения ExceptionHandler

Spring

Проблема унифицированной обработки исключений часто встречается при разработке проектов, в springMVC есть решение, использующее ExceptionHandler. Например,

@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.После пошаговой отладки выясняется, что стек вызовов выглядит следующим образом:

image-20190326180205336

основной код

Основной код, который решает, какой ExceptionHandler будет окончательно выбран, — это метод getMappedMethod класса ExceptionHandlerMethodResolver. код показывает, как показано ниже:

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;
  }
}

Это сначала находит все ExceptionHandlers, которые могут соответствовать исключению, а затем сортирует их, выбирая тот с наименьшей глубиной (то есть с самой высокой степенью совпадения).

Что касается алгоритма компаратора глубины, как показано на рисунке ниже, это простая рекурсия для непрерывного определения того, является ли родительское исключение целевым исключением для получения окончательной глубины.

image-20190327224336509

в заключении

Исходный код не длинный, и мы можем легко обрабатывать порядок, чтобы найти ответ --ExceptionHandler, который мы хотим, должен быть определен степенью ненормального матча, и мы не можем указать заказ через другие средства (на самом деле, это не необходимый).