Как DispatcherServlet в Spring MVC находит контроллеры, помеченные аннотацией @Controller?

Ответ

DispatcherServlet находит контроллеры через механизм сканирования компонентов Spring (component scanning), который активируется соответствующей конфигурацией.

Необходимые условия:

  1. Конфигурация сканирования пакетов:

    • Java Config: Использование аннотации @ComponentScan в классе конфигурации.
      @Configuration
      @ComponentScan("com.example.controllers") // Указывает пакет для сканирования
      @EnableWebMvc // Включает поддержку аннотаций MVC
      public class WebConfig implements WebMvcConfigurer {
      // ...
      }
    • XML Config: Использование элемента <context:component-scan> в контексте Spring.
      <context:component-scan base-package="com.example.controllers"/>
      <mvc:annotation-driven/>
  2. Наличие аннотации @Controller (или @RestController): Класс должен быть помечен этой стереотипной аннотацией, которая является специализацией @Component.

    @Controller // Будет обнаружен при сканировании пакета "com.example.controllers"
    public class UserController {
        // ...
    }
  3. Регистрация методов обработчиков: Методы в контроллере должны быть помечены аннотациями маппинга запросов (@GetMapping, @PostMapping, @RequestMapping и т.д.).

Процесс:

  1. При старте приложения Spring сканирует указанные пакеты.
  2. Классы с @Controller регистрируются как бины в ApplicationContext.
  3. DispatcherServlet получает доступ к этому контексту.
  4. Внутренний компонент HandlerMapping (по умолчанию RequestMappingHandlerMapping) индексирует все методы с аннотациями маппинга, связывая URL-шаблоны с конкретными методами контроллеров.
  5. Когда приходит HTTP-запрос, DispatcherServlet запрашивает у HandlerMapping подходящий обработчик (контроллер + метод) и вызывает его.

Если сканирование не настроено или контроллер находится вне сканируемых пакетов, DispatcherServlet его не обнаружит.

Ответ 18+ 🔞

Слушай, а вот этот твой DispatcherServlet — он же не шарится по всему проекту как угорелый, ища, где бы ему контроллер пристроить. Нет, сука! У него есть чёткий, блядь, механизм — сканирование компонентов (component scanning). Но чтобы эта магия работала, нужно соблюсти три условия, а то нихуя не найдёт, и будешь потом сидеть и чесать репу.

Что нужно, чтобы он их нашёл, этот распиздяй:

  1. Настроить сканирование пакетов. А то как он узнает, где искать-то? Есть два пути, оба рабочие:

    • Java Config: Тыкаешь в класс конфигурации аннотацию @ComponentScan и указываешь, где рыть.
      @Configuration
      @ComponentScan("com.example.controllers") // Смотри, бля, вот тут ищи!
      @EnableWebMvc
      public class WebConfig implements WebMvcConfigurer {
      // ...
      }
    • XML Config: Старая школа, но тоже пашет. Кидаешь в конфиг строку.
      <context:component-scan base-package="com.example.controllers"/>
      <mvc:annotation-driven/>
  2. Пометить контроллер правильно. Класс должен быть отмечен аннотацией @Controller (или @RestController). Это как маячок, блядь! Без неё Spring посмотрит на класс и подумает: "Ну и хуй с тобой, обычный какой-то класс".

    @Controller // Ага! Вот ты где, сука! Теперь я тебя вижу!
    public class UserController {
        // ...
    }
  3. Методы-обработчики тоже аннотировать. Ну нашёл он класс, а методы-то какие обрабатывают? Те, что с @GetMapping, @PostMapping и прочей хуйней.

Как это всё, блядь, работает внутри:

  1. Приложение стартует, Spring начинает сканировать указанные пакеты, как собака-ищейка.
  2. Все классы с @Controller регистрируются как бины в ApplicationContext. Попали в общую базу, ёпта!
  3. Наш DispatcherServlet имеет доступ к этой базе.
  4. Внутри него HandlerMapping (чаще всего RequestMappingHandlerMapping) делает индексацию — связывает URL-шаблоны с конкретными методами в найденных контроллерах. Получается такая карта, блядь, сокровищ.
  5. Прилетает HTTP-запрос. DispatcherServlet лезет в эту карту, находит нужный обработчик (контроллер + метод) и — хуяк! — вызывает его.

А если сканирование не настроил или контроллер в другом пакете лежит, который не указан, то DispatcherServlet пройдёт мимо и даже не моргнёт. Будешь потом думать: "Почему не работает, ёпта?". А потому что, блядь, не настроил!