Где DispatcherServlet ищет контроллеры?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Где DispatcherServlet ищет контроллеры?
Краткий ответ
DispatcherServlet ищет контроллеры в ApplicationContext (Spring контексте), который был инициализирован при запуске приложения. Контроллеры обнаруживаются через автоматическое сканирование classpath'а и регистрацию бинов, помеченных аннотациями @Controller, @RestController или другими стереотипными аннотациями.
Что такое DispatcherServlet
DispatcherServlet - это центральный Front Controller в Spring MVC, который:
- Перехватывает все входящие HTTP запросы
- Маршрутизирует их к соответствующим контроллерам
- Возвращает ответы клиенту
// 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 использует эти маппинги для поиска и вызова соответствующего контроллера.