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

Как контроллер понимает к какому методу будет соответствовать http метод

1.0 Junior🔥 101 комментариев
#Spring Framework

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

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

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

Как контроллер понимает к какому методу будет соответствовать 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 запрос, благодаря:

  1. Аннотациям (@GetMapping, @PostMapping и т.д.) — определяют путь и HTTP метод
  2. RequestMappingHandlerMapping — внутреннему механизму, который создаёт карту URL+Method → Method контроллера
  3. Алгоритму сравнения — Spring проверяет Path, HTTP Method, Content-Type, Accept headers и выбирает наиболее подходящий метод
  4. Параметрам аннотаций — consumes, produces помогают разрешить конфликты

Всё это происходит в момент запуска приложения (когда Spring сканирует контроллеры) и во время обработки каждого HTTP запроса.