Что такое DispatcherServlet Spring MVC?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# DispatcherServlet в Spring MVC
Краткий ответ
DispatcherServlet - это Front Controller, центральный компонент Spring MVC, который перехватывает все HTTP запросы и маршрутизирует их нужным обработчикам (контроллерам).
Это узкое место (в хорошем смысле) всей системы - каждый запрос проходит через DispatcherServlet.
Что это такое
Простое объяснение
Диаграмма работы:
HTTP Request
↓
[DispatcherServlet] ← Главный страж
↓
Найти нужный контроллер
↓
Пройти через фильтры
↓
[Controller]
↓
Вернуть ответ
↓
HTTP Response
Аналогия
DispatcherServlet - это как администратор на ресепшене:
Вы приходите в офис (HTTP Request)
Администратор (DispatcherServlet) спрашивает:
"Привет! Кому ты хочешь встретиться?"
Вы отвечаете:
"Мне нужен менеджер по продажам (GET /users)"
Администратор смотрит в список:
"Ах да, это к нему в кабинет 205 (UserController)"
Администратор провожает вас к нужному менеджеру
и ждёт результата встречи
Менеджер говорит результат:
"Вот список пользователей"
Администратор оформляет это красиво (JSON ответ)
и отправляет вам назад
Как DispatcherServlet работает
Шаг за шагом
// Когда вы делаете запрос:
GET /api/v1/users/123
// 1. Запрос попадает в DispatcherServlet
public class DispatcherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) {
// Это точка входа для ВСЕХ запросов
this.doDispatch(request, response);
}
}
// 2. DispatcherServlet анализирует запрос
HttpServletRequest request
↓
URL: /api/v1/users/123
Method: GET
Headers: {Content-Type: application/json}
// 3. Ищет соответствующий контроллер и метод
HandlerMapping.getHandler(request)
↓
Проверяет все @RequestMapping
Находит: UserController.getUserById(Long id)
// 4. Выполняет пред-обработку (перехватчики)
Interceptor.preHandle(request, response, handler)
↓
Логирование
Security checks
CORS validation
// 5. Вызывает контроллер
UserController.getUserById(123)
↓
SELECT * FROM users WHERE id = 123
↓
User(id=123, name="John", email="john@example.com")
// 6. Получает результат и преобразует его
HttpMessageConverter.write(user, response)
↓
Преобразует User объект в JSON:
{
"id": 123,
"name": "John",
"email": "john@example.com"
}
// 7. Выполняет пост-обработку
Interceptor.postHandle(request, response, handler, modelAndView)
↓
Логирование
Кэширование
Analytics
// 8. Возвращает ответ
HTTP 200 OK
{
"id": 123,
"name": "John",
"email": "john@example.com"
}
Компоненты DispatcherServlet
1. HandlerMapping - находит контроллер
// Spring ищет нужный контроллер
public interface HandlerMapping {
HandlerExecutionChain getHandler(HttpServletRequest request);
}
// Пример:
GET /api/v1/users/{id}
↓
RequestMappingHandlerMapping проверяет все @RequestMapping
↓
Находит:
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.findById(id);
}
}
2. HandlerAdapter - выполняет контроллер
public interface HandlerAdapter {
ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler);
}
// Пример:
RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
// Это знает, как вызвать @GetMapping методы
// Преобразует параметры
// Обрабатывает аннотации
adapter.handle(request, response, userController);
3. ViewResolver - преобразует результат (для JSP/Template)
public interface ViewResolver {
View resolveViewName(String viewName, Locale locale);
}
// Пример (для старых MVC приложений):
@Controller
public class UserController {
@GetMapping("/users")
public String listUsers(Model model) {
model.addAttribute("users", userService.findAll());
return "users-list"; // Имя view
}
}
// ViewResolver найдёт: /WEB-INF/views/users-list.jsp
// или: /templates/users-list.html (Thymeleaf)
// И преобразует в HTML
4. HttpMessageConverter - преобразует JSON/XML
public interface HttpMessageConverter<T> {
boolean canWrite(Class<?> clazz, MediaType mediaType);
void write(T t, MediaType contentType,
HttpOutputMessage outputMessage);
}
// Пример:
@RestController
public class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
}
// MappingJackson2HttpMessageConverter автоматически:
// User → JSON {
// "id": 1,
// "name": "John"
// }
// Для этого нужна зависимость:
// <dependency>
// <groupId>com.fasterxml.jackson.core</groupId>
// <artifactId>jackson-databind</artifactId>
// </dependency>
5. Interceptors - cross-cutting concerns
public interface HandlerInterceptor {
// Выполняется ДО контроллера
boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler);
// Выполняется ПОСЛЕ контроллера
void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView);
// Выполняется ВСЕГДА (даже при ошибках)
void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex);
}
// Практический пример:
@Component
public class LoggingInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(LoggingInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
logger.info("Incoming: {} {}", request.getMethod(), request.getRequestURI());
request.setAttribute("startTime", System.currentTimeMillis());
return true; // true = continue, false = stop
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) {
long duration = System.currentTimeMillis() -
(long) request.getAttribute("startTime");
logger.info("Response: {} {} - {}ms",
response.getStatus(),
request.getRequestURI(),
duration);
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
if (ex != null) {
logger.error("Error: " + request.getRequestURI(), ex);
}
}
}
// Зарегистрировать перехватчик:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private LoggingInterceptor loggingInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loggingInterceptor);
}
}
Полный lifecycle запроса
1. DispatcherServlet.doDispatch(request, response) ← ВХОДИМ
↓
2. HandlerMapping.getHandler(request)
← Находим контроллер и перехватчики
↓
3. HandlerInterceptor.preHandle()
← Логирование, security, CORS
↓
4. HandlerAdapter.handle()
← Вызываем контроллер
↓
5. @Controller метод выполняется
← UserController.getUserById(id)
↓
6. HandlerInterceptor.postHandle()
← Логирование результата
↓
7. ViewResolver.resolveViewName() (для @Controller)
ИЛИ
HttpMessageConverter.write() (для @RestController)
← Преобразуем результат в HTML/JSON
↓
8. HandlerInterceptor.afterCompletion()
← Финальное логирование, cleanup
↓
9. HTTP Response отправляется клиенту ← ВЫХОДИМ
Конфигурация DispatcherServlet
В старых версиях (web.xml)
<!-- web.xml (не используется в Boot) -->
<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/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern> <!-- Все запросы! -->
</servlet-mapping>
В Spring Boot (автоматически)
// Spring Boot автоматически регистрирует DispatcherServlet
// Вам не нужно ничего делать!
// application.properties
server.servlet.context-path=/api // Optional: /api/v1/users
spring.mvc.throw-exception-if-no-handler-found=true
Практический пример: Custom DispatcherServlet
// Почти никогда не нужно, но вот как это выглядит:
@Configuration
public class CustomDispatcherServletConfig {
@Bean
public DispatcherServlet dispatcherServlet(
WebApplicationContext webApplicationContext) {
DispatcherServlet dispatcher = new DispatcherServlet(webApplicationContext);
// Настройки
dispatcher.setThrowExceptionIfNoHandlerFound(true);
dispatcher.setDetectAllHandlerAdapters(true);
dispatcher.setDetectAllHandlerExceptionResolvers(true);
return dispatcher;
}
@Bean
public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(
DispatcherServlet dispatcherServlet) {
ServletRegistrationBean<DispatcherServlet> registration =
new ServletRegistrationBean<>(dispatcherServlet, "/");
registration.setName("dispatcher");
registration.setLoadOnStartup(1);
return registration;
}
}
Производительность
DispatcherServlet - узкое место
Все запросы идут через одну точку:
При нагрузке 10,000 RPS:
- 10,000 вызовов doDispatch() в секунду
- Каждый вызов проходит через все перехватчики
- Кэширование маршрутов критично!
Spring обычно кэширует:
- Найденные контроллеры (HandlerMapping cache)
- Найденные адаптеры (HandlerAdapter cache)
Кроме того, с Java 17+:
- Virtual threads могут обрабатывать 100x больше
- Меньше системного overhead
Оптимизация
// Можно прямо в @Configuration указать параметры
@Configuration
public class DispatcherServletConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
// Добавить fast JSON serializer
converters.add(new MappingJackson2HttpMessageConverter());
}
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// Использовать suffix pattern matching
configurer.setUseRegisteredSuffixPatternMatch(false);
}
}
Ответ для собеседования
Вопрос: Что такое DispatcherServlet Spring MVC?
Ответ:
DispatcherServlet - это Front Controller в Spring MVC, центральный компонент, который:
-
Перехватывает все HTTP запросы (благодаря
<url-pattern>/</url-pattern>) -
Маршрутизирует запросы нужным контроллерам через HandlerMapping
-
Управляет полным lifecycle запроса:
- Выполняет перехватчики (preHandle)
- Вызывает контроллер
- Выполняет пост-обработку (postHandle)
- Преобразует результат в JSON/HTML
-
Обрабатывает исключения через ExceptionResolver
-
Возвращает HTTP ответ
Это главное узкое место приложения - каждый запрос проходит через DispatcherServlet. Spring Boot регистрирует его автоматически, и в большинстве случаев вам не нужно его настраивать.
Ключевая идея: вместо того, чтобы создавать отдельный сервлет для каждого URL (как в старом JSP), DispatcherServlet один обрабатывает всё, используя маршрутизацию через аннотации @RequestMapping.