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.
Основные компоненты
- @ExceptionHandler: Метод, помеченный этой аннотацией внутри класса с
@ControllerAdvice, будет вызываться при возникновении указанного типа исключения в любом контроллере. Например, метод с@ExceptionHandler(IllegalArgumentException.class)обработает все исключения этого типа, выброшенные в любом контроллере приложения. - @InitBinder: Позволяет глобально настроить привязку параметров запроса к объектам (например, формат дат, валидацию). Метод с этой аннотацией выполняется перед каждым запросом к контроллеру.
- @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 →