Как контроллер понимает к какому методу будет соответствовать http метод
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как контроллер понимает к какому методу будет соответствовать HTTP метод
В Spring Framework маршрутизация HTTP запросов к методам контроллера — это одна из ключевых задач. Процесс осуществляется через аннотации и механизм DispatcherServlet, который внутренне использует информацию о аннотациях для маршрутизации.
1. Основные аннотации маршрутизации
Spring использует специальные аннотации для связи HTTP запросов с методами контроллера:
@RestController
@RequestMapping("/api/users")
public class UserController {
// GET /api/users
@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}
// GET /api/users/{id}
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.findById(id);
}
// POST /api/users
@PostMapping
public User createUser(@RequestBody User user) {
return userService.save(user);
}
// PUT /api/users/{id}
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user);
}
// DELETE /api/users/{id}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.delete(id);
}
// PATCH /api/users/{id}
@PatchMapping("/{id}")
public User partialUpdate(@PathVariable Long id, @RequestBody User user) {
return userService.partialUpdate(id, user);
}
}
Основные аннотации:
@GetMapping— соответствует HTTP GET@PostMapping— соответствует HTTP POST@PutMapping— соответствует HTTP PUT@DeleteMapping— соответствует HTTP DELETE@PatchMapping— соответствует HTTP PATCH@RequestMapping— универсальная аннотация, можно указать метод явно
2. Как DispatcherServlet маршрутизирует запросы
Внутри Spring работает так:
// Примерный алгоритм DispatcherServlet
public class DispatcherServlet extends HttpServlet {
private List<HandlerMapping> handlerMappings; // Карта URL → обработчик
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
// 1. Получаем путь и HTTP метод
String requestURI = request.getRequestURI(); // "/api/users/123"
String httpMethod = request.getMethod(); // "GET"
// 2. Ищем соответствующий обработчик (HandlerMapping)
HandlerExecutionChain chain = getHandler(request); // Ищет метод контроллера
// 3. Находим подходящий метод контроллера
// Spring сравнивает:
// - Path: /api/users/{id} соответствует /api/users/123
// - HTTP Method: GET соответствует @GetMapping
// - Content-Type, Accept headers
// 4. Вызываем метод контроллера
handler.handle(chain, request, response);
}
}
3. Механизм HandlerMapping
HandlerMapping отвечает за поиск нужного метода. Spring использует несколько стратегий:
// RequestMappingHandlerMapping — главный механизм
// Он анализирует все @RequestMapping, @GetMapping и т.д.
// и создаёт карту: (URL + HTTP Method) → Method
// Пример внутреннего представления:
/*
GET /api/users → UserController.getAllUsers()
GET /api/users/{id} → UserController.getUserById(Long)
POST /api/users → UserController.createUser(User)
PUT /api/users/{id} → UserController.updateUser(Long, User)
DELETE /api/users/{id} → UserController.deleteUser(Long)
*/
4. Порядок сравнения при маршрутизации
Spring проверяет в этом порядке:
1. Путь (Path)
@PostMapping("/api/users") // Точное совпадение
@GetMapping("/api/users/{id}") // Переменная в пути
@GetMapping("/api/users/**") // Wildcard
2. HTTP метод
@GetMapping // Только GET
@PostMapping // Только POST
@RequestMapping(method = {GET, POST}) // Несколько методов
3. Content-Type (для POST/PUT)
@PostMapping(consumes = "application/json")
public User createUser(@RequestBody User user) {}
@PostMapping(consumes = "application/x-www-form-urlencoded")
public User createUserFromForm(@ModelAttribute User user) {}
4. Accept header (тип ответа)
@GetMapping(produces = "application/json")
public User getUser() {
return user;
}
@GetMapping(produces = "application/xml")
public User getUserXml() {
return user;
}
5. Извлечение параметров из HTTP запроса
После того как метод найден, Spring извлекает параметры:
@GetMapping("/search")
public List<User> search(
@RequestParam String name, // Query: ?name=John
@RequestParam(defaultValue = "0") int page,
@RequestParam(required = false) String email
) {
return userService.search(name, page, email);
}
@GetMapping("/{id}/posts/{postId}")
public Post getPost(
@PathVariable Long id, // Path: /users/123
@PathVariable Long postId // Path: /posts/456
) {
return postService.findById(postId);
}
@PostMapping
public User createUser(
@RequestBody User user, // JSON body
@RequestHeader("Authorization") String auth,
@CookieValue("sessionId") String sessionId
) {
return userService.save(user);
}
6. Пример полного потока маршрутизации
HTTP Request: POST /api/users HTTP/1.1
Content-Type: application/json
{"name": "John"}
↓
1. DispatcherServlet получает запрос
2. Извлекает:
- Path: /api/users
- Method: POST
- Content-Type: application/json
3. RequestMappingHandlerMapping ищет соответствие:
- Ищет все @PostMapping с путём "/api/users"
- Находит: UserController.createUser(@RequestBody User)
4. Проверяет consumes:
- consumes = "application/json" ✓ соответствует
5. Инжектирует параметры:
- Парсит JSON из body
- Создаёт объект User
- Передаёт в метод
6. Вызывает метод:
UserController.createUser(user)
7. Возвращает результат в нужном формате (JSON)
7. Приоритет маршрутизации при конфликтах
// Если есть несколько подходящих путей,
// Spring выбирает наиболее специфичный
@GetMapping("/users/{id}") // Приоритет 2 (более специфичный)
public User getUserById(@PathVariable Long id) {}
@GetMapping("/users/{id}/posts") // Приоритет 1 (самый специфичный)
public List<Post> getUserPosts(@PathVariable Long id) {}
@GetMapping("/users/**") // Приоритет 3 (менее специфичный)
public List<User> searchUsers() {}
8. Использование @RequestMapping для явного указания метода
// Старый способ (всё ещё используется)
@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
public User getUserById(@PathVariable Long id) {}
// Эквивалентно:
@GetMapping("/users/{id}")
public User getUserById(@PathVariable Long id) {}
// Несколько методов одного аннотацией
@RequestMapping(value = "/users", method = {RequestMethod.GET, RequestMethod.POST})
public void handleUsersRequest() {}
Заключение
Spring контроллер понимает, к какому методу соответствует HTTP запрос, благодаря:
- Аннотациям (@GetMapping, @PostMapping и т.д.) — определяют путь и HTTP метод
- RequestMappingHandlerMapping — внутреннему механизму, который создаёт карту URL+Method → Method контроллера
- Алгоритму сравнения — Spring проверяет Path, HTTP Method, Content-Type, Accept headers и выбирает наиболее подходящий метод
- Параметрам аннотаций — consumes, produces помогают разрешить конфликты
Всё это происходит в момент запуска приложения (когда Spring сканирует контроллеры) и во время обработки каждого HTTP запроса.