Скоуп (Scope) в Spring — это правило, определяющее жизненный цикл бина: когда создавать новый экземпляр объекта, как долго его хранить и когда уничтожать. Это похоже на правила управления ресурсами — некоторые ресурсы нужно создавать один раз и переиспользовать, а другие требуют нового экземпляра при каждом обращении.
Скоупы важны, потому что у разных частей вашего приложения могут быть разные требования к созданию объектов. Например:
- Для сервисов "без состояния" (stateless), мы хотим использовать один и тот же экземпляр во всём приложении.
- А для объектов, которые хранят уникальные данные для каждого HTTP-запроса, нам нужен новый экземпляр для каждого запроса.
В реальном проекте понимание скоупов помогает гибко управлять ресурсами и улучшать производительность приложения.
Стандартные скоупы Spring
Давайте рассмотрим основные скоупы Spring:
- Singleton (по умолчанию)
- Prototype
- Request
- Session
- Application
Singleton: "Один на всех"
Singleton — это скоуп по умолчанию. Когда бин помечен этим скоупом (или не помечен вообще), Spring создаёт только один экземпляр бина на весь ApplicationContext. Этот бин будет использоваться повторно каждый раз, когда он запрашивается.
@Component
public class SingletonExample {
public SingletonExample() {
System.out.println("Singleton бин создан");
}
}
Если вы добавите вышеупомянутый бин в две разные части приложения, например, в два разных класса, Spring создаст один экземпляр и будет передавать его везде. Вот пример:
@Component
public class MyService {
private final SingletonExample singletonExample;
@Autowired
public MyService(SingletonExample singletonExample) {
this.singletonExample = singletonExample;
System.out.println("MyService получил бин SingletonExample");
}
}
@Component
public class AnotherService {
private final SingletonExample singletonExample;
@Autowired
public AnotherService(SingletonExample singletonExample) {
this.singletonExample = singletonExample;
System.out.println("AnotherService получил бин SingletonExample");
}
}
Результат:
Singleton бин создан
MyService получил бин SingletonExample
AnotherService получил бин SingletonExample
Prototype: "На каждый запрос — новый бин"
Prototype — скоуп для случаев, когда вам нужен новый экземпляр бина при каждом запросе. Это полезно, если бин содержит состояние, которое меняется в зависимости от бизнес-логики.
@Component
@Scope("prototype")
public class PrototypeExample {
public PrototypeExample() {
System.out.println("Prototype бин создан");
}
}
Если два разных компонента запросят PrototypeExample, Spring создаст два отдельных экземпляра.
@Component
public class MyPrototypeService {
private final PrototypeExample prototypeExample;
@Autowired
public MyPrototypeService(PrototypeExample prototypeExample) {
this.prototypeExample = prototypeExample;
System.out.println("MyPrototypeService получил бин PrototypeExample");
}
}
@Component
public class AnotherPrototypeService {
private final PrototypeExample prototypeExample;
@Autowired
public AnotherPrototypeService(PrototypeExample prototypeExample) {
this.prototypeExample = prototypeExample;
System.out.println("AnotherPrototypeService получил бин PrototypeExample");
}
}
Результат:
Prototype бин создан
MyPrototypeService получил бин PrototypeExample
Prototype бин создан
AnotherPrototypeService получил бин PrototypeExample
Request: "Новый бин для каждого HTTP-запроса"
Этот скоуп используется в веб-приложениях. На каждый новый HTTP-запрос создаётся отдельный экземпляр бина.
@Component
@Scope("request")
public class RequestScopedBean {
public RequestScopedBean() {
System.out.println("Request бин создан");
}
}
использовать этот скоуп можно только в приложении с поддержкой веб-контекста, например, с Spring MVC.
Session: "Один бин на сессию пользователя"
Session-скоуп создаёт бин, уникальный для сессии пользователя.
@Component
@Scope("session")
public class SessionScopedBean {
public SessionScopedBean() {
System.out.println("Session бин создан");
}
}
Application: "Один бин для всего контекста приложения"
Этот скоуп похож на singleton, но уникален для веб-приложений с несколькими сервлетами. Бин создаётся один раз и используется во всех сервлетах приложения.
Как настроить скоупы?
Скоупы можно настроить с помощью аннотации @Scope. Вот пример, как указать скоупы для бинов:
@Component
@Scope("prototype") // Указываем scope для данного бина
public class MyPrototypeBean {
// Логика бина
}
Для веб-ориентированных скоупов (request, session, application) вы можете использовать аналогичный подход.
Как правильно выбрать скоуп?
Singleton идеально подойдёт для сервисов без состояния. Например, сервис отправки email — он просто выполняет действие и не хранит данные.
Prototype нужен, когда каждому пользователю требуется свой экземпляр. Представьте форму регистрации — у каждого свои данные.
Request/Session помогают работать с веб-данными. Например, корзина в интернет-магазине живёт в рамках сессии пользователя.
Внимание: выбор скоупа влияет на память и быстродействие. Особенно аккуратно используйте prototype — каждый новый экземпляр занимает память, и её нужно вовремя освобождать.
Типичные ошибки при использовании скоупов
- Использование
prototype-бинов вsingleton-бинах: если вы добавитеprototype-бин вsingleton-бин через DI, Spring создаст только один экземпляр
prototypeи прикрепит его кsingleton. Для исправления можно использовать интерфейсObjectFactoryили аннотацию@Lookup.Попытка использовать веб-ориентированные скоупы в НЕ веб-приложении: в этом случае вы получите ошибку
No Scope Registered for ScopeName.
Теперь вы можете уверенно работать со скоупами в Spring Framework, используя их для создания гибких и масштабируемых приложений! В следующей лекции мы разберём аннотации @Configuration и @Bean, которые помогут вам ещё лучше управлять конфигурациями и бин-фабриками.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ