Какие знаешь виды Scope в Spring?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Виды 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 или неправильной работе с данными пользователя.