TGViewer
Java: fill the gaps Java: fill the gaps @java_fillthegaps · 12.3K subscribers
Post #535 13.6K
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 = …

void serve(Request request, Response response) {
…
var principal = ADMIN;
PRINCIPAL.set(principal);
…}

2️⃣ Бизнес-логика. В любом месте кода можно узнать, кто выполняет запрос:

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)
.call(() -> getResult());

Сначала кажется, что для java синтаксис Scoped Value очень необычный — как будто переменная главнее основного действия. Но такое в java уже есть, вспомните try-with-resources.

Что получаем:

✅ Видимость переменной задаётся для конкретного вызова метода
✅ У ScopedValue нет метода set, переменную нельзя обнулить/поменять внутри блока
✅ Код совместим с виртуальными потоками

Что вызывает вопросы:

🤔 Сценарии использования

Неизменяемый аналог ThreadLocal, совместимый с Project Loom точно нужен, но не вижу смысла задавать область видимости настолько гранулярно

🤔 Нельзя использовать несколько ScopedValue без использования вложенности. Хотя это легко реализовать по аналогии с try-with-resources

⚒ Где использовать: пока вижу только как замену ThreadLocal при переходе на виртуальные потоки.

Фича сейчас в стадии превью, посмотрим, как она будет развиваться. Если будет, конечно:)
  • 👍 70
  • 🔥 14
  • ❤ 10
More from @java_fillthegaps
  1. Apr 30, 2026Get Your Hands Dirty on Clean Architecture: отзыв на книгу Когда я подняла тему чистой арх…
  2. Apr 27, 2026​Clean Architecture: отзыв на книгу Наконец-то дочитала книгу Clean Architecture Роберта М…
  3. Mar 4, 2026Как переиспользовать контекст в интеграционных тестах Сегодня расскажу базовый минимум для…
  4. Mar 4, 2026Post #660
  5. Mar 4, 2026Тестовый контекст поднимается 2 минуты. У нас 4 класса с интеграционными тестами, их конфи…
  6. Feb 25, 2026Чистая архитектура. Главы 3-5 Продолжаем спидран по Clean Architecture Роберта Мартина. ⭐️…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →