Разработчик добавил в @RestController поле private User currentUser и заполняет его в начале каждого метода. Что не так?
- Можно ли хранить данные запроса в поле контроллера?
Контроллер — singleton, запросы обслуживаются параллельно в разных потоках. Поле currentUser общее: один пользователь увидит данные другого — гонка и утечка.
- Данные запроса — в локальных переменных и параметрах метода
- Текущий пользователь — @AuthenticationPrincipal или SecurityContextHolder
- Request scope — если очень нужно состояние на запрос
А ещё Ошибка не ловится тестами в одном потоке — проявляется под нагрузкой
Подробно
Каждый запрос обрабатывается своим потоком, но экземпляр контроллера один на всех, поэтому запись в поле из одного запроса видна другому — получается гонка, которая на нагрузке проявляется как чужие данные в ответе. Такие баги крайне неприятны: на разработке с одним пользователем всё работает. Правильные способы передать данные запроса — параметры метода, ThreadLocal-подобные механизмы вроде SecurityContextHolder, либо бин со scope request. Общее правило: бины Spring по умолчанию синглтоны, и изменяемое состояние в их полях недопустимо, если оно относится к конкретному запросу.
Что хотят услышать
- Singleton и параллельные запросы.
- Почему это гонка и утечка.
- Правильные способы.
Частые ошибки
- Хранить в поле сервиса или контроллера что-либо, относящееся к текущему запросу.
- Считать проблему теоретической — она не воспроизводится при одном пользователе.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✗Ничего: Spring создаёт новый экземпляр контроллера на каждый входящий HTTP-запрос
- ✗Поле нужно пометить @Autowired
- ✗Проблема появится только при включённом WebFlux
- ✓Контроллер — singleton: поле общее для параллельных запросов, данные перемешаются
Что спросят дальше
- Как устроен SecurityContextHolder и почему он работает?
- Чем request-scope бин отличается от параметра метода?
Разбор темы
- Сервлеты, фильтры и встроенный контейнер
Сервлет и встроенный Tomcat, модель thread-per-request, потокобезопасность контроллеров, Filter против Interceptor и forward/redirect.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.