tgoop.com/java_fillthegaps/535
Create:
Last Update:
Last Update:
Scoped Value (preview)
Неделю назад вышла java 21. Cегодня разберу интересную фичу в стадии превью — Scoped Value.
Ближайший аналог Scoped Value — ThreadLocal. Это когда мы объявляем переменную
ThreadLocal<Integer> value;и для каждого потока будет своё независимое значение value.
В бизнес-логике это редко нужно, но фреймворки активно пользуются этим классом. Spring Security использует ThreadLocal для хранения информации о текущем пользователе. Давайте на этом кейсе посмотрим недостатки ThreadLocal, и что предлагает ScopedValue.
Как работает секьюрити:
1️⃣ Когда приходит новый запрос, Spring вытаскивает информацию о пользователе и записывает в ThreadLocal переменную:
public static ThreadLocal<Principal> PRINCIPAL = …2️⃣ Бизнес-логика. В любом месте кода можно узнать, кто выполняет запрос:
void serve(Request request, Response response) {
…
var principal = ADMIN;
PRINCIPAL.set(principal);
…}
var principal = PRINCIPAL.get();Обычно каждый запрос обрабатывается в своём потоке, поэтому данные между запросами не пересекаются.
3️⃣ В конце работы с запросом удаляем информацию из ThreadLocal переменной
Что в итоге:
✅ Не надо передавать Principal в параметрах
❌ Надо явно очищать значение ThreadLocal переменной в конце работы
❌ В любом месте можно вызвать set/remove и всё сломать
❌ Подход несовместим с виртуальными потоками
Scoped Value намерен решить проблемы выше. Как это выглядит:
public static ScopedValue<Principal> PRINCIPAL = …Переменная
void serve(Request request, Response response) {
…
var principal = ADMIN;
ScopedValue.where(PRINCIPAL, principal)
.run(() -> process(request, response));
…}
PRINCIPAL
со значением principal будет доступна только внутри конкретного вызова метода process
. Достать значение внутри process
:var principal = PRINCIPAL.get()
;Кроме
run
есть метод call
, который возвращает значение из переданной функции:var result = ScopedValue.where(Server.PRINCIPAL, guest)Сначала кажется, что для java синтаксис Scoped Value очень необычный — как будто переменная главнее основного действия. Но такое в java уже есть, вспомните try-with-resources.
.call(() -> getResult());
Что получаем:
✅ Видимость переменной задаётся для конкретного вызова метода
✅ У ScopedValue нет метода set, переменную нельзя обнулить/поменять внутри блока
✅ Код совместим с виртуальными потоками
Что вызывает вопросы:
🤔 Сценарии использования
Неизменяемый аналог ThreadLocal, совместимый с Project Loom точно нужен, но не вижу смысла задавать область видимости настолько гранулярно
🤔 Нельзя использовать несколько ScopedValue без использования вложенности. Хотя это легко реализовать по аналогии с try-with-resources
⚒ Где использовать: пока вижу только как замену ThreadLocal при переходе на виртуальные потоки.
Фича сейчас в стадии превью, посмотрим, как она будет развиваться. Если будет, конечно:)
BY Java: fill the gaps
Share with your friend now:
tgoop.com/java_fillthegaps/535