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

Как работает @RequestMapping?

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

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

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

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

Как работает аннотация @RequestMapping в Spring

@RequestMapping — это основная аннотация в Spring для маппирования HTTP запросов на методы контроллера. Это центральная часть REST API разработки на Spring.

1. Основное назначение и синтаксис

// Базовый пример
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    // Маппит GET запрос на /api/users
    @RequestMapping(method = RequestMethod.GET)
    public List<User> getAllUsers() {
        return userService.findAll();
    }
    
    // Маппит POST запрос на /api/users
    @RequestMapping(method = RequestMethod.POST)
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }
    
    // Маппит GET запрос на /api/users/{id}
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public User getUserById(@PathVariable Long id) {
        return userService.findById(id);
    }
}

2. Основные параметры @RequestMapping

value/path — путь для маппирования:

@RequestMapping(value = "/users")      // /users
@RequestMapping("/users")              // Короче, /users
@RequestMapping({"/users", "/people"}) // Несколько путей

method — HTTP методы:

@RequestMapping(value = "/users", method = RequestMethod.GET)    // GET
@RequestMapping(value = "/users", method = RequestMethod.POST)   // POST
@RequestMapping(value = "/users", method = RequestMethod.PUT)    // PUT
@RequestMapping(value = "/users", method = RequestMethod.DELETE) // DELETE

// Можно несколько методов
@RequestMapping(value = "/users", method = {RequestMethod.GET, RequestMethod.POST})

consumes — тип контента, который ожидает метод:

// Метод принимает только JSON
@RequestMapping(value = "/users", method = RequestMethod.POST, 
                consumes = "application/json")
public User createUser(@RequestBody User user) {
    return userService.save(user);
}

// Метод может принять и JSON, и XML
@RequestMapping(value = "/data", method = RequestMethod.POST,
                consumes = {"application/json", "application/xml"})
public void processData(@RequestBody Data data) {}

produces — тип контента, который вернет метод:

// Метод возвращает JSON
@RequestMapping(value = "/users", method = RequestMethod.GET,
                produces = "application/json")
public List<User> getUsers() {
    return userService.findAll();
}

// Метод может вернуть JSON или XML
@RequestMapping(value = "/users", method = RequestMethod.GET,
                produces = {"application/json", "application/xml"})
public List<User> getUsersXmlOrJson() {
    return userService.findAll();
}

params — параметры запроса для маппирования:

// Маппит только если есть параметр status=active
@RequestMapping(value = "/users", params = "status=active")
public List<User> getActiveUsers() {
    return userService.findActiveUsers();
}

// Маппит только если параметр role существует
@RequestMapping(value = "/users", params = "role")
public List<User> getUsersByRole(@RequestParam String role) {
    return userService.findByRole(role);
}

// Маппит если параметр НЕ равен значению
@RequestMapping(value = "/users", params = "!deleted")
public List<User> getNotDeletedUsers() {
    return userService.findActive();
}

headers — заголовки запроса для маппирования:

// Маппит только если Content-Type = application/json
@RequestMapping(value = "/users", headers = "Content-Type=application/json")
public User createUser(@RequestBody User user) {
    return userService.save(user);
}

// Маппит только если заголовок X-API-Key существует
@RequestMapping(value = "/secure", headers = "X-API-Key")
public SecureData getSecureData() {
    return secureService.getData();
}

3. Сокращенные аннотации (рекомендуется использовать их)

Вместо @RequestMapping лучше использовать специализированные аннотации:

// @RequestMapping(method = RequestMethod.GET) → @GetMapping
@GetMapping("/users")                    // GET /users
public List<User> getUsers() {}

// @RequestMapping(method = RequestMethod.POST) → @PostMapping
@PostMapping("/users")                   // POST /users
public User createUser(@RequestBody User user) {}

// @RequestMapping(method = RequestMethod.PUT) → @PutMapping
@PutMapping("/users/{id}")               // PUT /users/{id}
public User updateUser(@PathVariable Long id, @RequestBody User user) {}

// @RequestMapping(method = RequestMethod.DELETE) → @DeleteMapping
@DeleteMapping("/users/{id}")            // DELETE /users/{id}
public void deleteUser(@PathVariable Long id) {}

// @RequestMapping(method = RequestMethod.PATCH) → @PatchMapping
@PatchMapping("/users/{id}")             // PATCH /users/{id}
public User partialUpdate(@PathVariable Long id, @RequestBody User user) {}

Исправленный контроллер с сокращениями:

@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    private final UserService userService;
    
    public UserController(UserService userService) {
        this.userService = userService;
    }
    
    @GetMapping
    public List<User> getAllUsers() {
        return userService.findAll();
    }
    
    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }
    
    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.findById(id)
            .orElseThrow(() -> new UserNotFoundException(id));
    }
    
    @PutMapping("/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
        return userService.update(id, user);
    }
    
    @DeleteMapping("/{id}")
    public void deleteUser(@PathVariable Long id) {
        userService.delete(id);
    }
}

4. Как Spring обрабатывает @RequestMapping (внутренний механизм)

Шаг 1: Инициализация контекста (Startup)

Время запуска Spring приложения:

1. Spring сканирует все @RestController и @Controller классы
2. Для каждого метода с @RequestMapping Spring:
   - Читает параметры аннотации (path, method, consumes, produces)
   - Создает RequestMappingInfo объект
   - Регистрирует его в RequestMappingHandlerMapping

Шаг 2: Получение HTTP запроса (Runtime)

Когда приходит HTTP запрос:

1. DispatcherServlet перехватывает запрос
2. Вызывает HandlerMapping.getHandler(request)
3. RequestMappingHandlerMapping проверяет все зарегистрированные маппинги
4. Находит подходящий метод контроллера:
   - Совпадает ли path?
   - Совпадает ли HTTP метод?
   - Если есть consumes - проверяет Content-Type
   - Если есть produces - проверяет Accept
   - Если есть params - проверяет параметры
   - Если есть headers - проверяет заголовки
5. Возвращает HandlerExecutionChain (метод + интерцепторы)

Шаг 3: Вызов метода (Execution)

1. RequestMappingHandlerAdapter получает HandlerExecutionChain
2. Читает параметры метода (@PathVariable, @RequestParam, @RequestBody)
3. Преобразует данные из запроса в параметры
4. Вызывает метод контроллера
5. Результат сериализуется (обычно в JSON)
6. Возвращается клиенту

5. Пример сложного маппирования

@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {
    private final OrderService orderService;
    
    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }
    
    // GET /api/v1/orders?status=pending
    @GetMapping(params = "status=pending")
    public List<Order> getPendingOrders() {
        return orderService.findByStatus("pending");
    }
    
    // GET /api/v1/orders?status=completed
    @GetMapping(params = "status=completed")
    public List<Order> getCompletedOrders() {
        return orderService.findByStatus("completed");
    }
    
    // GET /api/v1/orders (все заказы)
    @GetMapping
    public List<Order> getAllOrders() {
        return orderService.findAll();
    }
    
    // POST /api/v1/orders (создание, только JSON)
    @PostMapping(consumes = "application/json", produces = "application/json")
    public Order createOrder(@RequestBody Order order) {
        return orderService.save(order);
    }
    
    // GET /api/v1/orders/{id}
    @GetMapping("/{id}")
    public Order getOrderById(@PathVariable Long id) {
        return orderService.findById(id)
            .orElseThrow(() -> new OrderNotFoundException(id));
    }
    
    // PUT /api/v1/orders/{id}
    @PutMapping("/{id}")
    public Order updateOrder(@PathVariable Long id, @RequestBody Order order) {
        return orderService.update(id, order);
    }
    
    // DELETE /api/v1/orders/{id}
    @DeleteMapping("/{id}")
    public void deleteOrder(@PathVariable Long id) {
        orderService.delete(id);
    }
    
    // GET /api/v1/orders/{id}/items
    @GetMapping("/{id}/items")
    public List<OrderItem> getOrderItems(@PathVariable Long id) {
        return orderService.getItems(id);
    }
    
    // POST /api/v1/orders/{id}/items
    @PostMapping("/{id}/items")
    public OrderItem addItemToOrder(
        @PathVariable Long id,
        @RequestBody OrderItem item
    ) {
        return orderService.addItem(id, item);
    }
}

6. Частые ошибки и как их избежать

Несовместимые consumes/produces:

// Неправильно - методу нужен JSON, но produces XML
@RequestMapping(value = "/data", method = RequestMethod.POST,
                consumes = "application/json",
                produces = "application/xml")
public void processData(@RequestBody Data data) {}

Дублирующиеся маппинги:

// Ошибка - Spring не знает, какой метод вызвать
@GetMapping("/users")
public List<User> getAllUsers() {}

@GetMapping("/users")
public List<User> getAll() {}  // Конфликт!

Правильно - разные параметры:

@GetMapping("/users")
public List<User> getAllUsers() {}

@GetMapping("/users/{id}")
public User getUserById(@PathVariable Long id) {}

Забыть про trailing slash:

// Может привести к проблемам
@GetMapping("/users/")  // Лучше без /
public List<User> getUsers() {}

7. Иерархия маппирования

@RestController
@RequestMapping("/api/v1")  // Базовый путь
public class UserController {
    
    @RequestMapping("/users")  // Становится /api/v1/users
    public List<User> getUsers() {}
}

// Эквивалентно
@RestController
public class UserController {
    
    @RequestMapping("/api/v1/users")  // Полный путь
    public List<User> getUsers() {}
}

Рекомендации

Используйте специализированные аннотации:

  • @GetMapping вместо @RequestMapping(method = RequestMethod.GET)
  • @PostMapping вместо @RequestMapping(method = RequestMethod.POST)
  • Это делает код более читаемым и явным

Явно указывайте produces и consumes:

  • Помогает документировать API
  • Предотвращает неправильные запросы
  • Улучшает безопасность

Используйте правильную иерархию путей:

  • @RequestMapping на классе для общего префикса
  • @GetMapping и т.д. на методах для конкретных путей

Версионируйте API:

  • Используйте /api/v1/, /api/v2/ для совместимости