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

Что такое DispatcherServlet Spring MVC?

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

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

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

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

# 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, центральный компонент, который:

  1. Перехватывает все HTTP запросы (благодаря <url-pattern>/</url-pattern>)

  2. Маршрутизирует запросы нужным контроллерам через HandlerMapping

  3. Управляет полным lifecycle запроса:

    • Выполняет перехватчики (preHandle)
    • Вызывает контроллер
    • Выполняет пост-обработку (postHandle)
    • Преобразует результат в JSON/HTML
  4. Обрабатывает исключения через ExceptionResolver

  5. Возвращает HTTP ответ

Это главное узкое место приложения - каждый запрос проходит через DispatcherServlet. Spring Boot регистрирует его автоматически, и в большинстве случаев вам не нужно его настраивать.

Ключевая идея: вместо того, чтобы создавать отдельный сервлет для каждого URL (как в старом JSP), DispatcherServlet один обрабатывает всё, используя маршрутизацию через аннотации @RequestMapping.