Зачем использовать centralized exception handler
Что проверяют
Понимаете ли вы, зачем убирать перевод исключений в ответы из бизнес-кода, и знаете ли, как это устроено в Spring. Следом обычно спрашивают, какие исключения такой обработчик не поймает.
Как отвечать
Централизованный обработчик решает три задачи: убирает однотипные try/catch из контроллеров, даёт клиенту единый формат ошибки и собирает логирование в одном месте. Бизнес-код бросает осмысленные исключения (OrderNotFoundException, InsufficientFundsException), а какой HTTP-статус им соответствует, решает один класс.
В Spring это выглядит так:
@RestControllerAdvice: класс с методами@ExceptionHandler(OrderNotFoundException.class), каждый возвращает ответ с нужным статусом. Выбирается самый конкретный обработчик по иерархии исключений, порядок между несколькими advice задаёт@Order;ResponseEntityExceptionHandler: базовый класс, который уже обрабатывает стандартные исключения Spring MVC, например ошибки валидацииMethodArgumentNotValidException;ProblemDetail: начиная со Spring 6 и Boot 3 есть готовый формат ошибок по RFC 9457 (бывший RFC 7807), в Boot он включаетсяspring.mvc.problemdetails.enabled=true;- обработчик на
Exception: последний рубеж. Отдаёт 500 с нейтральным текстом и обязательно пишет в лог стек и идентификатор запроса.
Хорошо добавить про границы. @ControllerAdvice видит только то, что вылетело из цепочки вызова контроллера внутри DispatcherServlet. Ошибки фильтров Spring Security обрабатывают AuthenticationEntryPoint и AccessDeniedHandler, у @Async свой AsyncUncaughtExceptionHandler, у слушателей Kafka свой DefaultErrorHandler.
Где спотыкаются
Ловят Exception и возвращают 500 без логирования. Ошибка исчезает, и разбираться потом не с чем.
Отдают клиенту e.getMessage() или стек вызовов. Наружу утекают имена таблиц, куски SQL и внутренние классы.
Считают, что advice перехватит всё в приложении, и удивляются, почему ошибка аутентификации или фоновой задачи приходит в другом формате или не приходит вовсе.