Автор оригинала: Marcos Lopez Gonzalez.
1. введение
В Java довольно часто приходится работать с вложенными исключениями, поскольку они могут помочь нам отследить источник ошибки.
Когда мы имеем дело с такого рода исключениями, иногда мы можем захотеть узнать исходную проблему, вызвавшую исключение, чтобы наше приложение могло реагировать по-разному для каждого случая . Это особенно полезно, когда мы работаем с фреймворками, которые заключают корневые исключения в свои собственные.
В этой короткой статье мы покажем, как получить исключение первопричины с помощью простой Java, а также внешних библиотек, таких как Apache Commons Lang и Google Guava .
2. Приложение Для Калькулятора Возраста
Наше приложение будет представлять собой калькулятор возраста, который сообщает нам, сколько лет человеку с данной даты, полученной в виде String в формате ISO. Мы рассмотрим 2 возможных случая ошибок при разборе даты: плохо отформатированную дату и дату в будущем.
Давайте сначала создадим исключения для наших случаев ошибок:
static class InvalidFormatException extends DateParseException { InvalidFormatException(String input, Throwable thr) { super("Invalid date format: " + input, thr); } } static class DateOutOfRangeException extends DateParseException { DateOutOfRangeException(String date) { super("Date out of range: " + date); } }
Оба исключения наследуются от общего родительского исключения, что сделает наш код немного понятнее:
static class DateParseException extends RuntimeException { DateParseException(String input) { super(input); } DateParseException(String input, Throwable thr) { super(input, thr); } }
После этого мы можем реализовать класс Age Calculator с методом анализа даты:
static class AgeCalculator { private static LocalDate parseDate(String birthDateAsString) { LocalDate birthDate; try { birthDate = LocalDate.parse(birthDateAsString); } catch (DateTimeParseException ex) { throw new InvalidFormatException(birthDateAsString, ex); } if (birthDate.isAfter(LocalDate.now())) { throw new DateOutOfRangeException(birthDateAsString); } return birthDate; } }
Как мы видим, когда формат неправильный, мы оборачиваем DateTimeParseException в наш пользовательский Исключение InvalidFormatException.
Наконец, давайте добавим в наш класс открытый метод, который получает данные, анализирует их и затем вычисляет возраст:
public static int calculateAge(String birthDate) { if (birthDate == null || birthDate.isEmpty()) { throw new IllegalArgumentException(); } try { return Period .between(parseDate(birthDate), LocalDate.now()) .getYears(); } catch (DateParseException ex) { throw new CalculationException(ex); } }
Как показано, мы снова оборачиваем исключения. В этом случае мы заключаем их в исключение Вычисления , которое мы должны создать:
static class CalculationException extends RuntimeException { CalculationException(DateParseException ex) { super(ex); } }
Теперь мы готовы использовать наш калькулятор, минуя его любую дату в формате ISO:
AgeCalculator.calculateAge("2019-10-01");
И если расчет не удался, было бы полезно узнать, в чем проблема, не так ли? Продолжайте читать, чтобы узнать, как мы можем это сделать.
3. Найдите Первопричину С Помощью Простой Java
Первый способ, который мы будем использовать для поиска первопричины исключения, – это создание пользовательского метода, который перебирает все причины, пока не достигнет корня.:
public static Throwable findCauseUsingPlainJava(Throwable throwable) { Objects.requireNonNull(throwable); Throwable rootCause = throwable; while (rootCause.getCause() != null && rootCause.getCause() != rootCause) { rootCause = rootCause.getCause(); } return rootCause; }
Обратите внимание, что мы добавили дополнительное условие в наш цикл, чтобы избежать бесконечных циклов при обработке рекурсивных причин.
Если мы передадим недопустимый формат нашему калькулятору Age , то получим исключение DateTimeParseException в качестве основной причины:
try { AgeCalculator.calculateAge("010102"); } catch (CalculationException ex) { assertTrue(findCauseUsingPlainJava(ex) instanceof DateTimeParseException); }
Однако, если мы используем будущую дату, мы получим Date OutOfRangeException :
try { AgeCalculator.calculateAge("2020-04-04"); } catch (CalculationException ex) { assertTrue(findCauseUsingPlainJava(ex) instanceof DateOutOfRangeException); }
Кроме того, наш метод также работает для не вложенных исключений:
try { AgeCalculator.calculateAge(null); } catch (Exception ex) { assertTrue(findCauseUsingPlainJava(ex) instanceof IllegalArgumentException); }
В этом случае мы получаем IllegalArgumentException , так как мы передали null .
4. Найдите Первопричину С помощью Apache Commons Lang
Теперь мы продемонстрируем поиск первопричины с помощью сторонних библиотек вместо написания нашей пользовательской реализации.
Apache Commons Lang предоставляет класс ExceptionUtils , который предоставляет некоторые служебные методы для работы с исключениями.
Мы будем использовать метод getRootCause() в нашем предыдущем примере:
try { AgeCalculator.calculateAge("010102"); } catch (CalculationException ex) { assertTrue(ExceptionUtils.getRootCause(ex) instanceof DateTimeParseException); }
Мы получаем ту же первопричину, что и раньше. То же самое относится и к другим примерам, которые мы перечислили выше.
5. Найдите Первопричину С Помощью Гуавы
Последний способ, который мы собираемся попробовать, – это использовать Гуаву. Подобно Apache Commons Lang, он предоставляет Throwables класс с getRootCause() служебный метод.
Давайте попробуем это сделать на том же примере:
try { AgeCalculator.calculateAge("010102"); } catch (CalculationException ex) { assertTrue(Throwables.getRootCause(ex) instanceof DateTimeParseException); }
Поведение точно такое же, как и с другими методами.
6. Заключение
В этой статье мы продемонстрировали, как использовать вложенные исключения в нашем приложении, и реализовали служебный метод для поиска первопричины исключения.
Мы также показали, как сделать то же самое с помощью сторонних библиотек, таких как Apache Commons Lang и Google Guava.
Как всегда, полный исходный код примеров доступен на GitHub .