Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Аннотация @Scope в Spring
@Scope - это аннотация в Spring, которая определяет жизненный цикл и область видимости бина (bean) в контексте приложения. Она контролирует, как часто создаются новые экземпляры бина и как долго они хранятся в памяти.
Основное назначение
По умолчанию Spring создаёт один экземпляр каждого бина и переиспользует его везде. Но иногда нужны разные поведения:
- Один экземпляр на всё приложение (singleton)
- Новый экземпляр каждый раз (prototype)
- По экземпляру на HTTP request (request scope)
- По экземпляру на HTTP session (session scope)
Основные типы Scope
1. Singleton (по умолчанию)
Singleton - Spring создаёт один экземпляр бина на всё приложение и переиспользует его:
import org.springframework.stereotype.Service;
import org.springframework.context.annotation.Scope;
@Service
@Scope("singleton") // Это значение по умолчанию
public class UserService {
public User findById(Long id) {
System.out.println("Finding user: " + id);
return new User(id);
}
}
Примерно так это работает:
@RestController
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService; // Один и тот же экземпляр
}
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// userService - это тот же самый объект для всех запросов
return userService.findById(id);
}
}
Плюсы:
- Экономия памяти
- Быстрая инъекция зависимостей
- Простота
Минусы:
- Потокобезопасность: один объект используется несколькими потоками
- Нельзя хранить состояние, специфичное для конкретного пользователя
2. Prototype
Prototype - Spring создаёт новый экземпляр бина каждый раз, когда его запрашивают:
import org.springframework.stereotype.Service;
import org.springframework.context.annotation.Scope;
@Service
@Scope("prototype")
public class OrderBuilder {
private List<Item> items = new ArrayList<>();
public void addItem(Item item) {
items.add(item);
}
public Order build() {
return new Order(items);
}
}
Каждый запрос получает свой экземпляр:
@RestController
public class OrderController {
private final ApplicationContext context;
public OrderController(ApplicationContext context) {
this.context = context;
}
@PostMapping("/orders")
public Order createOrder(@RequestBody List<Item> items) {
// Получаем новый экземпляр OrderBuilder для каждого запроса
OrderBuilder builder = context.getBean(OrderBuilder.class);
for (Item item : items) {
builder.addItem(item);
}
return builder.build();
}
}
Плюсы:
- Изоляция состояния между запросами
- Потокобезопасность (каждый поток свой объект)
- Подходит для stateful объектов
Минусы:
- Больше использования памяти
- Медленнее singleton
- Нужно вручную управлять bean'ами
3. Request Scope
Request scope - один экземпляр бина на один HTTP запрос:
import org.springframework.web.context.annotation.RequestScope;
import org.springframework.stereotype.Component;
@Component
@RequestScope
public class RequestLogger {
private String requestId;
private long startTime;
@PostConstruct
public void init() {
this.requestId = UUID.randomUUID().toString();
this.startTime = System.currentTimeMillis();
System.out.println("Request started: " + requestId);
}
public String getRequestId() {
return requestId;
}
public long getElapsedTime() {
return System.currentTimeMillis() - startTime;
}
@PreDestroy
public void cleanup() {
System.out.println("Request completed in " + getElapsedTime() + "ms");
}
}
В контроллере:
@RestController
public class DataController {
private final RequestLogger logger;
public DataController(RequestLogger logger) {
this.logger = logger;
}
@GetMapping("/data")
public Map<String, String> getData() {
// logger создан заново для каждого запроса
return Map.of(
"requestId", logger.getRequestId(),
"elapsed", logger.getElapsedTime() + "ms"
);
}
}
Плюсы:
- Идеально для логирования и трейсирования запросов
- Изоляция данных между HTTP запросами
- Автоматическая очистка после запроса
Минусы:
- Работает только в веб-контексте (не в console app)
- Требует servlet контейнера
4. Session Scope
Session scope - один экземпляр бина на HTTP сессию (для одного пользователя):
import org.springframework.web.context.annotation.SessionScope;
import org.springframework.stereotype.Component;
@Component
@SessionScope
public class ShoppingCart {
private List<CartItem> items = new ArrayList<>();
public void addItem(CartItem item) {
items.add(item);
}
public List<CartItem> getItems() {
return items;
}
public void clear() {
items.clear();
}
}
Использование:
@RestController
@RequestMapping("/cart")
public class CartController {
private final ShoppingCart cart;
public CartController(ShoppingCart cart) {
this.cart = cart; // Один экземпляр на сессию
}
@PostMapping("/items")
public List<CartItem> addItem(@RequestBody CartItem item) {
cart.addItem(item);
return cart.getItems();
}
@GetMapping
public List<CartItem> getCart() {
return cart.getItems();
}
@DeleteMapping
public void clearCart() {
cart.clear();
}
}
Плюсы:
- Идеально для хранения пользовательского состояния
- Автоматическое управление жизненным циклом
- Простая работа с сессиями
Минусы:
- Потребление памяти (активных сессий = активных бинов)
- Не масштабируется горизонтально (если несколько серверов)
5. Application Scope
Application scope - один экземпляр на всё ServletContext приложения (похож на singleton, но специфичен для веб):
import org.springframework.web.context.annotation.ApplicationScope;
import org.springframework.stereotype.Component;
@Component
@ApplicationScope
public class GlobalCounter {
private long requestCount = 0;
public synchronized void incrementCount() {
requestCount++;
}
public long getCount() {
return requestCount;
}
}
Сравнение Scope
| Scope | Количество экземпляров | Когда создаётся | Когда удаляется |
|---|---|---|---|
| Singleton | 1 на приложение | При запуске | При выключении |
| Prototype | 1 на каждый запрос бина | На каждый запрос | Сразу после использования |
| Request | 1 на HTTP запрос | Начало HTTP запроса | Конец HTTP запроса |
| Session | 1 на сессию | Первый доступ в сессию | Конец сессии |
| Application | 1 на ServletContext | При запуске | При выключении |
Потокобезопасность и Scope
Singleton + многопоточность = опасно:
@Service
@Scope("singleton")
public class UnsafeUserService {
private User currentUser; // ОПАСНО! Общий для всех потоков
public void setUser(User user) {
this.currentUser = user;
}
public User getUser() {
return currentUser; // Один поток может перезаписать currentUser
}
}
Решение - использовать Request или Prototype scope:
@Service
@Scope("request")
public class SafeUserService {
private User currentUser; // Безопасно, один на запрос
public void setUser(User user) {
this.currentUser = user;
}
public User getUser() {
return currentUser;
}
}
Практические рекомендации
Используй Singleton для:
- Stateless сервисов (UserService, ProductService)
- Конфигурационных бинов
- Тяжёлых ресурсов (DatabaseConnection Pool)
Используй Request для:
- Логирования текущего запроса
- Трейсирования (RequestId)
- Временных данных запроса
Используй Session для:
- Shopping cart
- Пользовательских настроек
- Сессионного состояния
Используй Prototype для:
- Stateful объектов, которые должны быть изолированы
- Builder pattern
- Редко - в большинстве случаев лучше Request
@Scope - это критическая аннотация для правильной разработки Spring приложений. Выбор правильного scope напрямую влияет на безопасность, производительность и масштабируемость приложения.