Ответ
Для хранения состояния, уникального для каждого HTTP-запроса, контроллеру следует назначить scope request.
Практическое применение:
Используйте аннотацию @RequestScope (Spring 5+) или @Scope("request"). Это гарантирует создание нового экземпляра бина для каждого входящего запроса, изолируя его поля от других запросов.
Пример:
@Controller
@RequestScope // Или @Scope("request")
public class RequestScopedController {
private int requestCounter;
@GetMapping("/increment")
public ResponseEntity<Integer> handleRequest() {
requestCounter++; // Это значение уникально для данного HTTP-запроса
return ResponseEntity.ok(requestCounter);
}
}
Почему это важно:
Без явного указания scope request контроллер по умолчанию будет singleton. В этом случае поле requestCounter стало бы разделяемым состоянием между всеми запросами, приводя к race condition и некорректным данным.
Ответ 18+ 🔞
Ну ты смотри, какие тонкости всплывают! Вот сидишь ты, пишешь свой контроллер, и вроде всё логично: поле requestCounter, инкремент на каждый запрос — красота! А потом бац, и десять тысяч пользователей одновременно на твой эндпоинт навалились, и счётчик у них общий, как грязные носки в общежитии. И вместо 1, 2, 3... получается пиздец, бардак и race condition, который мозги выносит.
А весь прикол в том, что по умолчанию-то твой контроллер — singleton, одна штука на всю приложуху! И поле requestCounter у него одно на всех, общее. Представь: один поток только собрался его увеличить, а второй уже лезет читать — и всё, приехали, данные поехали крышей.
Так вот, чтобы этого не было, нужно каждому HTTP-запросу свой, личный, свеженький экземпляр контроллера. Как одноразовый стаканчик — попил и выкинул. Для этого и нужен scope request.
Делается это проще пареной репы. Вешаешь на контроллер аннотацию @RequestScope (если у тебя Spring 5 или новее) или старую добрую @Scope("request"). Всё! Магия Spring'а сделает своё дело.
Смотри, как это выглядит в коде:
@Controller
@RequestScope // Вот эта волшебная строчка! Можно и @Scope("request")
public class RequestScopedController {
private int requestCounter; // Теперь это поле — личное дело каждого запроса
@GetMapping("/increment")
public ResponseEntity<Integer> handleRequest() {
requestCounter++; // Увеличил свой личный счётчик. Никто другой к нему не подберётся!
return ResponseEntity.ok(requestCounter);
}
}
Теперь когда Вася зайдёт на /increment, для него создастся свой экземпляр RequestScopedController со своим requestCounter, который начнёт с 0. А когда Петя зайдёт следом — для него создастся другой экземпляр, с другим счётчиком, тоже начинающим с 0. Они друг другу мешать не будут, как соседи по коммуналке за толстыми стенами.
Если же эту аннотацию проебать и оставить контроллер синглтоном, то Вася и Петя будут драться за один общий requestCounter. И будет не "1, 1", а "ёб твою мать, почему у меня 7, а я только первый раз зашёл?". Вот это и есть та самая гонка, race condition, который всех так веселит в три часа ночи на проде.
Короче, запомни: состояние конкретного запроса — в бины со scope request. И спать будешь спокойнее, ей-богу.