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

Как Spring MVC взаимодействует с сервлетами

2.0 Middle🔥 181 комментариев
#Spring Framework

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

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

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

Как Spring MVC взаимодействует с сервлетами

Основная идея

Spring MVC построена НА БАЗЕ сервлетов, но скрывает их сложность. Вместо создания многих сервлетов, Spring MVC использует один фронт-контроллер сервлета (DispatcherServlet), который маршрутизирует все HTTP запросы нужным обработчикам.

Сервлет: фундамент

Забыли, что такое сервлет?

// Сервлет — это Java класс, который обрабатывает HTTP запросы
public class GreetingServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        // 1. Получаю параметр из URL: /greeting?name=John
        String name = req.getParameter("name");
        
        // 2. Формирую ответ
        resp.setContentType("text/html");
        resp.getWriter().println("<h1>Hello, " + name + "!</h1>");
    }
}

Без Spring MVC: много сервлетов

Требование: обработать разные URL:
- GET /users → получить всех пользователей
- GET /users/{id} → получить одного пользователя
- POST /users → создать пользователя
- PUT /users/{id} → обновить
- DELETE /users/{id} → удалить

Решение БЕЗ Spring:
Нужно создать МНОГО сервлетов и зарегистрировать каждый в web.xml
<!-- web.xml — адское настраивание -->
<web-app>
    <servlet>
        <servlet-name>GetUsersServlet</servlet-name>
        <servlet-class>com.example.GetUsersServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>GetUsersServlet</servlet-name>
        <url-pattern>/users</url-pattern>
    </servlet-mapping>
    
    <servlet>
        <servlet-name>GetUserByIdServlet</servlet-name>
        <servlet-class>com.example.GetUserByIdServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>GetUserByIdServlet</servlet-name>
        <url-pattern>/users/{id}</url-pattern>
    </servlet-mapping>
    
    <!-- ... и так для каждого эндпоинта ... -->
</web-app>
// GetUsersServlet.java
public class GetUsersServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        // Получу всех пользователей и отправлю JSON
    }
}

// GetUserByIdServlet.java
public class GetUserByIdServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        String id = req.getPathInfo(); // /users/123
        // Получу пользователя с ID=123
    }
}

// ... дублирование кода для каждого эндпоинта!

С Spring MVC: фронт-контроллер

Вместо:
  /users       → GetUsersServlet
  /users/{id}  → GetUserByIdServlet
  /posts       → GetPostsServlet

Spring делает:
  ВСЕ запросы → DispatcherServlet (один сервлет!) → маршрутизирует контроллерам

Теперь просто:

// Один контроллер обрабатывает ВСЕ эндпоинты
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping  // GET /api/users
    public List<User> getAllUsers() {
        return userService.findAll();
    }
    
    @GetMapping("/{id}")  // GET /api/users/123
    public User getUserById(@PathVariable Long id) {
        return userService.findById(id);
    }
    
    @PostMapping  // POST /api/users
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }
    
    @PutMapping("/{id}")  // PUT /api/users/123
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.update(id, user);
    }
    
    @DeleteMapping("/{id}")  // DELETE /api/users/123
    public void deleteUser(@PathVariable Long id) {
        userService.delete(id);
    }
}

Всё! Никаких web.xml, никаких отдельных сервлетов.

Архитектура: как Spring MVC работает с сервлетами

1. HTTP запрос идёт в DispatcherServlet (фронт-контроллер)
2. DispatcherServlet определяет, какой контроллер обработает запрос
3. Контроллер обрабатывает запрос
4. Spring преобразует результат в HTTP ответ
5. Ответ идёт клиенту

Визуально:

HTTP запрос (GET /api/users/123)
     |
     v
 DispatcherServlet (extends HttpServlet)
     |
     +-- Определяет маршрут /api/users/{id}
     |
     +-- Находит UserController.getUserById()
     |
     v
 HandlerMapping (/api/users/{id} -> UserController#getUserById)
     |
     v
 UserController.getUserById(123)  // Бизнес-логика
     |
     v
 return User(123, "John", ...)
     |
     v
 ViewResolver / MessageConverter (объект -> JSON)
     |
     v
 HTTP 200 OK с JSON: {"id": 123, "name": "John", ...}

web.xml настройка DispatcherServlet

<web-app>
    <!-- DispatcherServlet — основной сервлет Spring MVC -->
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        
        <!-- Spring контекст конфиг -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring-servlet.xml</param-value>
        </init-param>
        
        <!-- Загружать при старте приложения -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <!-- Все запросы идут в DispatcherServlet -->
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

Сейчас (Spring Boot):

// Никаких XML!
// Spring Boot автоматически регистрирует DispatcherServlet

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// Конфиг через application.properties:
server.servlet.context-path=/api

Компоненты Spring MVC

public class RequestProcessingFlow {
    /*
    1. DispatcherServlet (extends HttpServlet)
       └─ Фронт-контроллер, получает все запросы
    
    2. HandlerMapping
       └─ Определяет, какой контроллер обработает запрос
       └─ Примеры: @RequestMapping, путь /api/users/{id}
    
    3. HandlerAdapter
       └─ Адаптер для вызова метода контроллера
       └─ Обрабатывает аннотации (@PathVariable, @RequestBody и т.д.)
    
    4. Controller (ваш код)
       └─ Бизнес-логика
       └─ Возвращает модель (Model) или значение (@ResponseBody)
    
    5. ViewResolver
       └─ Преобразует имя представления в View (только для HTML)
       └─ Пример: "userList" -> /WEB-INF/views/userList.jsp
    
    6. View (опционально)
       └─ JSP, Thymeleaf, FreeMarker — отрисовывает HTML
       └─ Для REST API не нужно (используются MessageConverters)
    
    7. MessageConverter
       └─ Преобразует объект в JSON/XML (для REST)
       └─ Пример: User -> {"id": 123, "name": "John"}
    */
}

Пример: запрос обрабатывается пошагово

Запрос: POST /api/users с JSON телом {"name": "John", "email": "john@example.com"}

1. DispatcherServlet.service(request, response)
   └─ Фронт-контроллер ловит запрос

2. HandlerMapping.getHandler(request)
   └─ Ищет контроллер для /api/users
   └─ Находит: UserController.createUser()

3. HandlerAdapter.handle(handler, request, response)
   └─ Адаптер вызывает контроллер
   └─ Парсит параметры, аннотации (@RequestBody, @PathVariable и т.д.)
   └─ Преобразует JSON -> User объект

4. UserController.createUser(@RequestBody User user)
   └─ Ваша логика
   └─ userService.save(user);
   └─ return new User(1, "John", "john@example.com");

5. MessageConverter.write(user, response)
   └─ Jackson преобразует User -> JSON
   └─ JSON отправляется клиенту

Ответ: HTTP 200 OK с телом {"id": 1, "name": "John", "email": "john@example.com"}

Интернальное устройство DispatcherServlet

public class DispatcherServlet extends FrameworkServlet {
    
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) {
        // Это вызывается для КАЖДОГО HTTP запроса
        
        // 1. Определяю маршрут
        HandlerExecutionChain chain = getHandler(request);
        // chain содержит: контроллер + интерцепторы
        
        // 2. Вызываю интерцепторы (до обработки)
        for (HandlerInterceptor interceptor : chain.getInterceptors()) {
            interceptor.preHandle(request, response);
        }
        
        // 3. Получаю адаптер контроллера
        HandlerAdapter adapter = getHandlerAdapter(chain.getHandler());
        
        // 4. Вызываю контроллер
        ModelAndView mav = adapter.handle(request, response, chain.getHandler());
        
        // 5. Интерцепторы (после обработки)
        for (HandlerInterceptor interceptor : chain.getInterceptors()) {
            interceptor.postHandle(request, response, mav);
        }
        
        // 6. Отрисовка (ViewResolver) или преобразование в JSON (MessageConverter)
        render(mav, request, response);
    }
}

Сравнение: сервлет vs Spring MVC

┌─────────────────────┬──────────────────────┬──────────────────────┐
│ Аспект              │ Чистый Сервлет       │ Spring MVC           │
├─────────────────────┼──────────────────────┼──────────────────────┤
│ Количество классов  │ По одному на URL     │ Один DispatcherServlet|
│ Маршрутизация       │ Вручную в web.xml    │ @RequestMapping       │
│ Парсинг параметров  │ req.getParameter()   │ @PathVariable,        │
│                     │ вручную              │ @RequestBody          │
│ Преобразование JSON │ Вручную GSON/Jackson │ Автоматически         │
│ Код                 │ Многословный         │ Лаконичный            │
│ Масштабируемость    │ Сложно добавлять URL │ Просто добавить метод │
└─────────────────────┴──────────────────────┴──────────────────────┘

Итого

Spring MVC — это фреймворк, построенный НА ОСНОВЕ сервлетов. Он использует:

  1. DispatcherServlet — один основной сервлет, который получает все запросы
  2. HandlerMapping — определяет, какой контроллер обработать запрос
  3. HandlerAdapter — вызывает контроллер с правильными параметрами
  4. MessageConverter — преобразует объекты в JSON

Вместо создания множества сервлетов вручную, вы просто пишете контроллеры с методами, аннотированными @RequestMapping, и Spring всё остальное делает сам.

Как Spring MVC взаимодействует с сервлетами | PrepBro