Для чего используется @ControllerAdvice в Spring?

«Для чего используется @ControllerAdvice в Spring?» — вопрос из категории Spring, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Основные сценарии использования:

  1. Глобальная обработка исключений (Global Exception Handling) — самый частый случай.

    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        @ExceptionHandler(UserNotFoundException.class)
        @ResponseStatus(HttpStatus.NOT_FOUND)
        @ResponseBody
        public ErrorResponse handleUserNotFound(UserNotFoundException ex) {
            return new ErrorResponse("USER_NOT_FOUND", ex.getMessage());
        }
    
        @ExceptionHandler(MethodArgumentNotValidException.class)
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        @ResponseBody
        public ErrorResponse handleValidationExceptions(MethodArgumentNotValidException ex) {
            // Обработка ошибок валидации @Valid
            return new ErrorResponse("VALIDATION_ERROR", ex.getBindingResult().toString());
        }
    
        @ExceptionHandler(Exception.class)
        @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
        @ResponseBody
        public ErrorResponse handleAllUncaughtException(Exception ex) {
            return new ErrorResponse("INTERNAL_ERROR", "An unexpected error occurred");
        }
    }
  2. Глобальная инициализация данных модели (@ModelAttribute).

    @ControllerAdvice
    public class GlobalModelAdvice {
        @ModelAttribute("currentYear")
        public int addCurrentYear() {
            return Year.now().getValue(); // Добавит атрибут "currentYear" в модель каждого контроллера
        }
    }
  3. Глобальная настройка привязки данных (@InitBinder).

    @ControllerAdvice
    public class GlobalBinderAdvice {
        @InitBinder
        public void initBinder(WebDataBinder binder) {
            // Запрет привязки поля "id" из запроса
            binder.setDisallowedFields("id");
            // Настройка форматирования дат и т.д.
        }
    }

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

// Только для контроллеров в пакете "com.example.web"
@ControllerAdvice("com.example.web")
// Только для контроллеров, аннотированных @RestController
@ControllerAdvice(annotations = RestController.class)
// Только для указанных классов контроллеров
@ControllerAdvice(assignableTypes = {UserController.class, AdminController.class})

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