Открыть сервис

ControllerAdvice

ControllerAdvice — это аннотация в языке программирования Java, используемая в рамках фреймворка Spring (в частности, Spring MVC и Spring WebFlux) для создания глобального обработчика исключений и связывания данных (bindings) в контроллерах. Она позволяет централизованно управлять поведением всех контроллеров приложения, обрабатывая исключения, добавляя общие модели данных и применяя глобальные настройки форматирования и валидации запросов.

История и контекст

Аннотация @ControllerAdvice была введена в Spring Framework 3.2 (выпущен в декабре 2012 года) как расширение существующей аннотации @ExceptionHandler. До её появления разработчикам приходилось либо обрабатывать исключения в каждом контроллере отдельно, либо использовать механизмы на уровне сервлетов (например, web.xml), что было менее гибким и вело к дублированию кода. @ControllerAdvice стала частью подхода «глобального консультирования» (advice), заимствованного из аспектно-ориентированного программирования (AOP), но реализованного на уровне аннотаций Spring.

В версии Spring 4.0 (2013) была добавлена аннотация @RestControllerAdvice, которая является специализированной версией @ControllerAdvice для REST-контроллеров. Она автоматически применяет семантику @ResponseBody ко всем методам, что упрощает обработку исключений в RESTful-приложениях.

Принцип работы

@ControllerAdvice — это аннотация на уровне класса. Класс, помеченный ею, автоматически сканируется Spring при старте приложения и регистрируется как глобальный советник. Механизм работы основан на перехвате исключений, выбрасываемых из методов контроллеров, а также на применении общих настроек через @InitBinder и @ModelAttribute.

Основные компоненты

  1. @ExceptionHandler: Метод, помеченный этой аннотацией внутри класса с @ControllerAdvice, будет вызываться при возникновении указанного типа исключения в любом контроллере. Например, метод с @ExceptionHandler(IllegalArgumentException.class) обработает все исключения этого типа, выброшенные в любом контроллере приложения.
  2. @InitBinder: Позволяет глобально настроить привязку параметров запроса к объектам (например, формат дат, валидацию). Метод с этой аннотацией выполняется перед каждым запросом к контроллеру.
  3. @ModelAttribute: Позволяет добавлять общие атрибуты в модель для всех контроллеров. Например, можно добавить объект текущего пользователя или список ролей, доступный во всех представлениях.

Область применения

По умолчанию @ControllerAdvice применяется ко всем контроллерам в приложении. Однако можно ограничить его действие, указав в аннотации параметры:

  • basePackages — пакеты, контроллеры которых будут обрабатываться.
  • assignableTypes — конкретные классы контроллеров.
  • annotations — аннотации, которыми помечены контроллеры (например, только для @RestController).

Пример использования

Обработка исключений

```java @ControllerAdvice public class GlobalExceptionHandler {

@ExceptionHandler(ResourceNotFoundException.class) @ResponseStatus(HttpStatus.NOT_FOUND) public ResponseEntity<String> handleNotFound(ResourceNotFoundException ex) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage()); }

@ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ResponseEntity<String> handleGeneralError(Exception ex) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Произошла внутренняя ошибка сервера"); } } ```

Глобальное добавление атрибутов модели

```java @ControllerAdvice public class GlobalModelAttributes {

@ModelAttribute("appName") public String getAppName() { return "Мое приложение"; }

@ModelAttribute("currentYear") public int getCurrentYear() { return java.time.Year.now().getValue(); } } ```

Теперь во всех представлениях (JSP, Thymeleaf) доступны переменные appName и currentYear.

Глобальная настройка привязки данных

```java @ControllerAdvice public class GlobalBindingInitializer {

@InitBinder public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); } } ```

Этот код устанавливает единый формат даты для всех контроллеров.

Разновидности

@RestControllerAdvice

Эта аннотация является комбинацией @ControllerAdvice и @ResponseBody. Она предназначена для REST-контроллеров и автоматически сериализует возвращаемые значения в JSON или XML. В отличие от @ControllerAdvice, методы обработчиков исключений в @RestControllerAdvice по умолчанию возвращают тело ответа, а не представление (view). Это удобно при создании RESTful-сервисов, где ответы всегда в формате данных.

Локальные обработчики

В отличие от глобального @ControllerAdvice, можно использовать @ExceptionHandler непосредственно в классе контроллера. Такой локальный обработчик будет действовать только для данного контроллера и имеет приоритет над глобальным, если обрабатывает то же исключение.

Преимущества и недостатки

Преимущества

  • Централизация: Уменьшает дублирование кода — один обработчик для всех контроллеров.
  • Удобство поддержки: Изменение логики обработки ошибок происходит в одном месте.
  • Гибкость: Возможность ограничить область действия (по пакетам, аннотациям, типам контроллеров).
  • Чистота кода: Контроллеры не загромождаются кодом обработки исключений.

Недостатки

  • Сложность отладки: Глобальный обработчик может скрыть специфичные для контроллера исключения, если не настроить приоритеты.
  • Избыточность: В небольших приложениях может быть излишним, проще обрабатывать исключения локально.
  • Потенциальная путаница: При наличии нескольких @ControllerAdvice-классов порядок их выполнения может быть неочевидным (Spring определяет порядок на основе приоритета, задаваемого аннотацией @Order).

Сравнение с альтернативами

МеханизмОбласть действияГибкостьТипичное применение
@ExceptionHandler в контроллереОдин контроллерВысокая (специфичная логика)Обработка исключений, уникальных для конкретного контроллера
@ControllerAdviceВсе контроллеры (или выбранные)Средняя (глобальная, но настраиваемая)Общие ошибки (404, 500), валидация, аутентификация
HandlerExceptionResolverВесь DispatcherServletНизкая (реализация интерфейса)Устаревший подход, редко используется в новых проектах
ErrorController (Spring Boot)Весь сервлетСредняя (настраивается через свойства)Кастомные страницы ошибок, глобальная обработка HTTP-статусов

Применение в Spring Boot

В Spring Boot @ControllerAdvice часто используется вместе с автоматической конфигурацией ошибок. По умолчанию Spring Boot предоставляет базовый обработчик ошибок (BasicErrorController), который возвращает JSON или HTML-страницу с информацией об ошибке. Однако для более тонкой настройки (например, возврата кастомного JSON-формата ошибки) разработчики создают свои классы с @ControllerAdvice. Это позволяет стандартизировать ответы API, добавлять поля вроде timestamp, status, error, message, path и т.д.

Интересные факты

  • Аннотация @ControllerAdvice не является аспектом в классическом понимании AOP (как @Aspect), но использует похожую идею перехвата вызовов.
  • В Spring 5 (2017) была добавлена поддержка реактивного программирования (WebFlux), и @ControllerAdvice работает и в реактивных контроллерах, но с некоторыми ограничениями (например, нельзя использовать @InitBinder в реактивном контексте).
  • Несколько классов с @ControllerAdvice могут сосуществовать в одном приложении. Порядок их выполнения определяется аннотацией @Order (меньшее значение — выше приоритет). Если приоритет не задан, порядок не гарантирован.

Источники

  • Spring Framework Documentation: «Annotation-based Controller Advice» (Spring.io)
  • «Spring in Action» by Craig Walls (Manning Publications, 6th edition, 2022)
  • Baeldung: «A Guide to @ControllerAdvice in Spring» (Baeldung.com)
  • «Pro Spring 5» by Iuliana Cosmina, Rob Harrop, Chris Schaefer, Clarence Ho (Apress, 2017)

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →