Рубрики
Без рубрики

Как найти первопричину исключения в Java

Узнайте, как найти первопричину исключения в Java.

Автор оригинала: 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 .