← Назад к вопросам

Где DispatcherServlet ищет контроллеры?

2.2 Middle🔥 151 комментариев
#Spring Boot и Spring Data#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Где DispatcherServlet ищет контроллеры?

Краткий ответ

DispatcherServlet ищет контроллеры в ApplicationContext (Spring контексте), который был инициализирован при запуске приложения. Контроллеры обнаруживаются через автоматическое сканирование classpath'а и регистрацию бинов, помеченных аннотациями @Controller, @RestController или другими стереотипными аннотациями.

Что такое DispatcherServlet

DispatcherServlet - это центральный Front Controller в Spring MVC, который:

  1. Перехватывает все входящие HTTP запросы
  2. Маршрутизирует их к соответствующим контроллерам
  3. Возвращает ответы клиенту
// DispatcherServlet запускается при инициализации Spring приложения
// Он действует как Front Controller по паттерну
@WebServlet("/")
public class DispatcherServlet extends HttpServlet {
    
    private ApplicationContext applicationContext;
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        // Инициализирует ApplicationContext
        // Сканирует classpath
        // Регистрирует все бины
    }
}

Как DispatcherServlet ищет контроллеры

Шаг 1: Инициализация ApplicationContext

При запуске Spring приложения создаётся ApplicationContext:

@SpringBootApplication
public class OrderServiceApplication {
    public static void main(String[] args) {
        // DispatcherServlet создаётся здесь неявно
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

В классическом Spring MVC это выглядит так:

<!-- web.xml -->
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/servlet-context.xml</param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

Шаг 2: Сканирование classpath'а

Spring сканирует classpath в поиске классов, помеченных стереотипными аннотациями:

@SpringBootApplication
@ComponentScan(basePackages = "com.company")
public class Application {}

Spring ищет классы с аннотациями:

  • @Controller - классический MVC контроллер
  • @RestController - REST контроллер (результат = JSON)
  • @Service - сервис бизнес-логики
  • @Repository - репозиторий для работы с БД
  • @Component - generic компонент

Шаг 3: Регистрация бинов

Обнаруженные классы регистрируются как бины в ApplicationContext:

@RestController
@RequestMapping("/api/orders")
public class OrderController {
    
    @GetMapping
    public List<Order> getOrders() {
        return orderService.getAllOrders();
    }
}

// Это эквивалентно:
// <bean class="com.company.OrderController" />

Где хранятся контроллеры

1. В ApplicationContext как бины

public class DispatcherServlet extends HttpServlet {
    
    // applicationContext содержит все бины
    private ApplicationContext applicationContext;
    
    public void doDispatch(HttpServletRequest request, 
                          HttpServletResponse response) {
        
        // DispatcherServlet получает все бины типа Object.class
        // и ищет среди них контроллеры
        Map<String, Object> beans = applicationContext.getBeansOfType(Object.class);
        
        // Для каждого бина проверяет, является ли он контроллером
        for (Object bean : beans.values()) {
            if (bean.getClass().isAnnotationPresent(Controller.class) ||
                bean.getClass().isAnnotationPresent(RestController.class)) {
                // Это контроллер!
            }
        }
    }
}

2. В HandlerMapping

During initialization, DispatcherServlet создаёт HandlerMapping'и, которые хранят соответствие URL → контроллер метод:

public class RequestMappingHandlerMapping extends AbstractHandlerMapping {
    
    // Вот где хранятся все маршруты
    private Map<RequestMappingInfo, HandlerMethod> mappingLookup;
    
    public RequestMappingHandlerMapping() {
        // При инициализации сканирует все контроллеры
        // и создаёт маппинги
        detectHandlerMethods(new OrderController());
        detectHandlerMethods(new UserController());
        // ...
    }
}

Процесс поиска контроллера при запросе

HTTP Request: GET /api/orders/123
    ↓
DispatcherServlet.doService()
    ↓
DispatcherServlet.doDispatch()
    ↓
getHandler() → ищет в HandlerMapping'ах
    ↓
RequestMappingHandlerMapping.getHandler()
    ↓
Проверяет регулярные выражения маршрутов
    ↓
Находит OrderController.getOrderById()
    ↓
Возвращает HandlerMethod (контроллер + метод)
    ↓
Получает HandlerAdapter
    ↓
Вызывает контроллер через reflection
    ↓
Получает результат
    ↓
Отправляет ответ клиенту

Практический пример

// 1. DispatcherServlet при инициализации сканирует classpath
@SpringBootApplication
@ComponentScan(basePackages = "com.company.order")
public class OrderServiceApplication {}

// 2. Находит эти контроллеры
@RestController
@RequestMapping("/api/orders")
public class OrderController {
    
    @GetMapping("/{id}")
    public Order getOrder(@PathVariable String id) {
        return orderService.getOrderById(id);
    }
    
    @PostMapping
    public Order createOrder(@RequestBody CreateOrderRequest request) {
        return orderService.createOrder(request);
    }
}

// 3. Регистрирует маршруты в HandlerMapping
// GET /api/orders/{id}  → getOrder(id)
// POST /api/orders      → createOrder(request)

// 4. Когда приходит запрос GET /api/orders/123
// DispatcherServlet:
// - Находит в HandlerMapping маршрут /api/orders/{id}
// - Извлекает OrderController из бинов ApplicationContext'а
// - Вызывает getOrder("123")

Иерархия поиска контроллеров

ApplicationContext
├── Root Application Context (глобальные бины)
│   ├── @Service (сервисы)
│   ├── @Repository (репозитории)
│   └── @Component (общие компоненты)
└── Servlet Application Context (для DispatcherServlet)
    ├── @Controller (контроллеры MVC)
    ├── @RestController (REST контроллеры)
    ├── ViewResolver (для отрисовки view'ов)
    ├── HandlerMapping (маршруты)
    └── HandlerAdapter (адаптеры для вызова)

Spring Boot упрощает всё

В Spring Boot всё происходит автоматически:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        // Spring Boot автоматически:
        // 1. Создаёт ApplicationContext
        // 2. Сканирует classpath (basepackage = package приложения)
        // 3. Регистрирует все бины
        // 4. Создаёт DispatcherServlet
        // 5. Инициализирует HandlerMapping'и
    }
}

Вы можете посмотреть найденные бины:

@Component
public class BeansPrinter {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    @PostConstruct
    public void printBeans() {
        String[] beanNames = applicationContext.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            Object bean = applicationContext.getBean(beanName);
            System.out.println(beanName + " -> " + bean.getClass().getName());
        }
    }
}

Кастомизация поиска контроллеров

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // Добавляем Interceptor для перехвата запросов
        registry.addInterceptor(new LoggingInterceptor());
    }
}

public class LoggingInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, 
                            HttpServletResponse response, 
                            Object handler) {
        // handler - это найденный контроллер
        System.out.println("Found handler: " + handler);
        return true;
    }
}

Отладка поиска контроллеров

Для отладки включите DEBUG логирование:

# application.properties
logging.level.org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping=DEBUG
logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG

Вы увидите что-то вроде:

DEBUG: Mapped "GET /api/orders/{id}" onto OrderController#getOrder(String)
DEBUG: Mapped "POST /api/orders" onto OrderController#createOrder(CreateOrderRequest)
DEBUG: Initializing DispatcherServlet 'dispatcherServlet'

Вывод

DispatcherServlet ищет контроллеры в ApplicationContext'е через сканирование classpath'а во время инициализации приложения. Все найденные контроллеры регистрируются как бины и их маршруты сохраняются в HandlerMapping'ах. При получении HTTP запроса DispatcherServlet использует эти маппинги для поиска и вызова соответствующего контроллера.