← Назад к вопросам

Что такое аннотация Scope?

1.0 Junior🔥 201 комментариев
#Soft Skills и карьера

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Аннотация @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Количество экземпляровКогда создаётсяКогда удаляется
Singleton1 на приложениеПри запускеПри выключении
Prototype1 на каждый запрос бинаНа каждый запросСразу после использования
Request1 на HTTP запросНачало HTTP запросаКонец HTTP запроса
Session1 на сессиюПервый доступ в сессиюКонец сессии
Application1 на 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 напрямую влияет на безопасность, производительность и масштабируемость приложения.

Что такое аннотация Scope? | PrepBro