Вопрос 11 · Раздел 5

Какие scope существуют в Spring?

4. Scoped Proxy — для внедрения узких scope в широкие 5. ObjectProvider — альтернатива Scoped Proxy 6. RefreshScope — для динамической конфигурации

Версии по языкам: English Russian Ukrainian

🟢 Junior Level

Scope определяет, сколько экземпляров бина создаёт Spring.

Scope Сколько экземпляров
Singleton Один на всё приложение (по умолчанию)
Prototype Новый при каждом запросе
Request Один на HTTP запрос
Session Один на HTTP сессию
@Service              // Singleton
public class UserService { }

@Scope("prototype")
@Service
public class Report { }  // Новый каждый раз

🟡 Middle Level

Standard Scopes

Singleton:

// Один экземпляр на ApplicationContext
// Все потоки используют один объект
// → Должен быть Stateless!

Prototype:

// Новый при каждом getBean()
// Spring НЕ вызывает @PreDestroy!
// → Вы сами чистите ресурсы

Web Scopes

@Scope("request")     // Один на HTTP запрос
@Scope("session")     // Один на сессию пользователя
@Scope("application") // Один на ServletContext

Custom Scopes

// Свой scope!
context.getBeanFactory().registerScope("thread", new ThreadScope());
// ThreadScope — кастомная реализация интерфейса org.springframework.beans.factory.config.Scope.
// Хранит бины в ThreadLocal. В стандартном Spring такой scope не встроен — это пример.

// Spring Cloud: Refresh Scope
// → Бин пересоздаётся при обновлении конфигурации

Проблема: узкий scope в Singleton

// ❌ Request бин создастся один раз при старте Singleton
@Service
public class SingletonService {
    @Autowired RequestBean requestBean;  // Застрял первый!
}

// ✅ Scoped Proxy
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
@Bean public RequestBean requestBean() { }

// ✅ ObjectProvider
@Autowired ObjectProvider<RequestBean> provider;
RequestBean bean = provider.getObject();  // Актуальный!

🔴 Senior Level

ScopedProxyMode

TARGET_CLASS (CGLIB):
  → Создаёт прокси-наследник
  → Прокси → RequestContextHolder → актуальный бин
  
INTERFACES (JDK):
  → Проксирует только интерфейсы
  
NO:
  → Без прокси → проблема с Singleton

Under the hood: где хранятся

Singleton → DefaultSingletonBeanRegistry (ConcurrentHashMap)
Request → хранится в атрибутах HttpServletRequest, доступ через RequestContextHolder (ThreadLocal)
Session → SessionAttributes
Prototype → Не хранится, создаётся каждый раз

Refresh Scope (Spring Cloud)

@RefreshScope
@ConfigurationProperties("my.config")
public class MyConfig { }

// При обновлении через Spring Cloud Bus:
// → Прокси инвалидируется
// → При следующем запросе создаётся заново с новыми данными

Production Experience

Реальный сценарий: Session scope в микросервисе

Session scope → ThreadLocal → работает на одной ноде
→ Load Balancer → запрос на другую ноду → Session потерялся!

Решение: Spring Session + Redis
→ Session хранится в Redis → доступен на всех нодах

Best Practices

  1. Singleton — для сервисов и репозиториев
  2. Prototype — для тяжёлых stateful объектов
  3. Web scopes — для данных пользователя
  4. Scoped Proxy — для внедрения узких scope в широкие
  5. ObjectProvider — альтернатива Scoped Proxy
  6. RefreshScope — для динамической конфигурации

Резюме для Senior

  • Singleton = один на ApplicationContext
  • Prototype = новый каждый раз, @PreDestroy НЕ работает
  • Request/Session = web scope через ThreadLocal
  • Scoped Proxy = прокси → RequestContextHolder
  • RefreshScope = пересоздание при обновлении конфига
  • Custom Scope = свой lifecycle

🎯 Шпаргалка для интервью

Обязательно знать:

  • 4 стандартных scope: Singleton (по умолчанию), Prototype, Request, Session
  • Singleton = один экземпляр на ApplicationContext, Prototype = новый при каждом запросе
  • Web scope (Request/Session) хранятся через ThreadLocal
  • ScopedProxyMode (TARGET_CLASS/CGLIB, INTERFACES/JDK, NO) решает проблему внедрения узких scope в широкие
  • @PreDestroy НЕ вызывается для Prototype — ресурсы чистите вручную
  • ObjectProvider — альтернатива Scoped Proxy для Prototype/Request в Singleton
  • RefreshScope (Spring Cloud) — пересоздание бина при обновлении конфигурации

Частые уточняющие вопросы:

  • Как внедрить Request scope в Singleton? → Scoped Proxy (proxyMode = TARGET_CLASS) или ObjectProvider
  • Что произойдёт, если Prototype бин содержит ресурсы? → Утечка, Spring не вызовет @PreDestroy, нужно чистить вручную через try-finally
  • Где Spring хранит Singleton бины? → DefaultSingletonBeanRegistry (ConcurrentHashMap)
  • Что делает RefreshScope? → Инвалидирует прокси, при следующем запросе создаёт бин заново с новыми данными

Красные флаги (НЕ говорить):

  • «Prototype вызывает @PreDestroy» — не вызывает
  • «Request scope хранится в кэше Spring» — хранится в атрибутах HttpServletRequest
  • «Singleton создаётся при каждом обращении» — создаётся один раз
  • «Custom Scope встроен в Spring» — нужно писать свою реализацию интерфейса Scope

Связанные темы:

  • [[12. В чём разница между singleton и prototype scope]]
  • [[13. Что такое прокси в Spring]]
  • [[14. Когда Spring создаёт прокси]]
  • [[16. Что такое аспект, advice, pointcut, join point]]