Как правильно обрабатывать исключения в приложении
Что проверяют
Умеете ли вы строить обработку ошибок на уровне всего приложения, а не только писать try-catch. Следом обычно спрашивают, чем checked отличаются от unchecked и где в Spring-приложении ловить исключения централизованно.
Как отвечать
Каркас из нескольких правил:
- checked и unchecked: наследников
Exception(кромеRuntimeException) компилятор заставляет обработать или объявить вthrows. Это ожидаемые ситуации, из которых вызывающий код может восстановиться. Ошибки программиста и бизнес-ошибки обычно делают unchecked; - try-with-resources: всё, что реализует
AutoCloseable, закрывается само, а исключение при закрытии попадает вgetSuppressed()и не затирает основное. Ручное закрытие вfinallyустарело с Java 7; - обёртка с причиной: переводя низкоуровневое исключение в своё, передавайте исходное в конструктор,
new OrderException("...", e), иначе стек-трейс теряется; - ловите там, где можете помочь: повторить, подставить значение, ответить клиенту. Остальное пусть летит вверх;
- централизованная обработка: в Spring это
@RestControllerAdviceс методами@ExceptionHandler, которые переводят исключения в HTTP-ответы. В Spring 6 для тела ответа есть стандартныйProblemDetail; - логирование один раз: там, где исключение окончательно обработано, с полным стеком.
Две детали выдают опыт. @Transactional по умолчанию откатывает транзакцию только на unchecked-исключениях и Error, а на checked фиксирует, если не указать rollbackFor. Поймав InterruptedException, восстановите флаг через Thread.currentThread().interrupt().
Где спотыкаются
Оставляют пустой catch или ограничиваются e.printStackTrace(). Ошибка исчезает, а через месяц данные испорчены без единой записи в логе.
Логируют и тут же пробрасывают дальше. Один сбой даёт пять одинаковых стек-трейсов на разных уровнях.
Ловят Exception или Throwable целиком и заворачивают без причины. Так глушатся и NullPointerException, и ошибки конфигурации, которые должны падать громко.