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

Какие знаешь виды Scope в Spring?

1.7 Middle🔥 171 комментариев
#Spring Boot и Spring Data#Spring Framework

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

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

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

Виды Scope в Spring

Scope в Spring определяет жизненный цикл и область видимости bean'а. Это один из ключевых концептов контейнера зависимостей Spring.

1. Singleton Scope (по умолчанию)

Определение: Bean создаётся один раз и переиспользуется на всё время жизни приложения.

@Configuration
public class AppConfig {
    @Bean
    @Scope("singleton")  // Или просто без указания
    public UserService userService() {
        return new UserService();
    }
}

// Или через аннотацию
@Component
@Scope("singleton")  // По умолчанию
public class UserService {
    public void getUser(Long id) { ... }
}

Характеристики:

  • Одна единственная копия bean'а на приложение
  • Создаётся при инициализации контейнера Spring
  • Потокобезопасность — ответственность разработчика
  • Очень быстро, минимум памяти
@SpringBootTest
public class SingletonTest {
    @Autowired
    private UserService userService1;
    
    @Autowired
    private UserService userService2;
    
    @Test
    public void testSingleton() {
        // Одинаковые объекты
        assertThat(userService1).isSameAs(userService2);
    }
}

Когда использовать:

  • Stateless сервисы (UserService, EmailService)
  • DAO/Repository
  • Конфигурация и утилиты

Потенциальная проблема:

@Component
public class CounterService {
    private int count = 0;  // Проблема: state делится между потоками!
    
    public void increment() {
        count++;  // Race condition
    }
}

2. Prototype Scope

Определение: Для каждого запроса создаётся новый bean.

@Component
@Scope("prototype")
public class UserRequest {
    private String userId;
    private long timestamp = System.currentTimeMillis();
    
    public UserRequest() {
        System.out.println("UserRequest создан");
    }
}

@Service
public class UserController {
    @Autowired
    private UserRequest userRequest;  // Новый bean для каждого инъекта
}

Характеристики:

  • Новый bean каждый раз при инъекции
  • Больше памяти, выше CPU
  • Идеален для stateful объектов
  • Нет гарантии потокобезопасности
@SpringBootTest
public class PrototypeTest {
    @Autowired
    private ApplicationContext context;
    
    @Test
    public void testPrototype() {
        UserRequest req1 = context.getBean(UserRequest.class);
        UserRequest req2 = context.getBean(UserRequest.class);
        
        // Разные объекты
        assertThat(req1).isNotSameAs(req2);
    }
}

Когда использовать:

  • Request-specific данные
  • Stateful объекты
  • Объекты с mutable state

3. Request Scope

Определение: Bean существует на время одного HTTP запроса.

@Component
@Scope("request")
public class HttpRequest {
    private String userId;
    private Map<String, String> headers = new HashMap<>();
    private long createdAt = System.currentTimeMillis();
    
    public HttpRequest() {
        System.out.println("HttpRequest создан для нового запроса");
    }
}

@RestController
public class UserController {
    @Autowired
    private HttpRequest httpRequest;  // Новый для каждого HTTP запроса
    
    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        // httpRequest содержит данные текущего запроса
        String userId = httpRequest.getUserId();
        return ResponseEntity.ok(new User(id));
    }
}

Характеристики:

  • Один bean на один HTTP запрос
  • Автоматически уничтожается после запроса
  • Веб-специфичный scope
  • Много памяти для high-traffic приложений

Проблема с Singleton + Request scope:

@Service  // Singleton
public class UserService {
    @Autowired
    private HttpRequest httpRequest;  // Request scope
    // Ошибка! Singleton пытается инъектировать Request-scoped bean
}

Решение — использовать ObjectFactory или Proxy:

@Service
public class UserService {
    @Autowired
    private ObjectFactory<HttpRequest> httpRequestFactory;
    
    public void processRequest() {
        HttpRequest req = httpRequestFactory.getObject();  // Получить текущий
    }
}

// Или с proxyMode
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class HttpRequest { ... }

4. Session Scope

Определение: Bean существует на время HTTP сессии пользователя.

@Component
@Scope("session")
public class UserSession {
    private Long userId;
    private String username;
    private LocalDateTime loginTime;
    
    public void login(Long id, String name) {
        this.userId = id;
        this.username = name;
        this.loginTime = LocalDateTime.now();
    }
}

@RestController
public class AuthController {
    @Autowired
    private UserSession userSession;  // Один на сессию
    
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest req) {
        userSession.login(req.getId(), req.getUsername());
        return ResponseEntity.ok().build();
    }
    
    @GetMapping("/profile")
    public ResponseEntity<User> getProfile() {
        // userSession содержит данные текущего пользователя
        return ResponseEntity.ok(new User(userSession.getUserId()));
    }
}

Характеристики:

  • Один bean на одну сессию HTTP
  • Живёт столько же, сколько session
  • Ideal для пользовательских данных
  • Требует работающего session management

5. Application Scope (Global Session)

Определение: Bean существует на всё время жизни приложения (только для web приложений).

@Component
@Scope("application")
public class ApplicationStats {
    private long totalRequests = 0;
    private long totalErrors = 0;
    
    public synchronized void recordRequest() {
        totalRequests++;
    }
}

Характеристики:

  • Живёт столько же, сколько приложение
  • Используется редко
  • Требует синхронизации для потокобезопасности

6. WebSocket Scope

Определение: Bean существует на время WebSocket соединения.

@Component
@Scope("websocket")
public class WebSocketSession {
    private String sessionId;
    private List<Message> messages = new ArrayList<>();
}

Сравнение всех Scope

Scope        Жизненный цикл           Использование
─────────────────────────────────────────────────────
Singleton    Всё приложение           Stateless сервисы
Prototype    Каждый запрос            Stateful объекты
Request      Один HTTP запрос         Web-специфичные данные
Session      Одна HTTP сессия         Данные пользователя
Application  Всё приложение           Global states
WebSocket    WebSocket соединение     WebSocket сессии

Практический пример

@Configuration
public class AppConfig {
    // Singleton — переиспользуется
    @Bean
    public UserRepository userRepository() {
        return new UserRepository();
    }
    
    // Request scope — новый для каждого запроса
    @Bean
    @Scope("request")
    public RequestContext requestContext() {
        return new RequestContext();
    }
    
    // Session scope — хранит данные пользователя
    @Bean
    @Scope("session")
    public UserSessionData userSessionData() {
        return new UserSessionData();
    }
}

@RestController
public class UserController {
    @Autowired
    private UserRepository userRepository;  // Singleton
    
    @Autowired
    private ObjectFactory<RequestContext> requestContext;  // Request
    
    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        RequestContext ctx = requestContext.getObject();
        User user = userRepository.findById(id);
        return ResponseEntity.ok(user);
    }
}

Вывод

Выбор правильного scope критичен для производительности и корректности приложения:

  • Singleton — default для большинства компонентов
  • Request/Session — когда нужны request-specific или user-specific данные
  • Prototype — редко, когда нужен stateful объект

Ошибка выбора scope может привести к утечкам памяти, race conditions или неправильной работе с данными пользователя.