Сколько экземпляров контроллера создается при использовании Spring MVC?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Spring MVC: Количество экземпляров контроллера
Краткий ответ
По умолчанию: 1 экземпляр на все 1000 запросов.
Контроллеры в Spring это Singleton скоп — создаётся один раз при старте приложения.
Как работает
// Контроллер
@RestController
public class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return new User(id);
}
}
// Spring при старте создаёт 1 экземпляр:
UserController controller = new UserController();
// Все 1000 запросов используют ЭТУ же instance:
controller.getUser(1); controller.getUser(2); controller.getUser(3);
// Все используют controller
Почему это нормально?
- Контроллеры stateless
// Правильно (stateless)
@RestController
public class UserController {
private final UserService service; // Dependency, не state
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return service.getUser(id);
}
}
// Неправильно (stateful)
@RestController
public class BadController {
private Long currentUserId; // STATE! опасно!
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
currentUserId = id; // Race condition!
// Если приходит 2 запроса одновременно...
return service.getUser(currentUserId);
}
}
- Thread-safe по конструкции
Spring гарантирует thread-safety, потому что:
- Контроллер не содержит mutable state
- Все локальные переменные это separate stack frames
- Каждый поток имеет свой stack
Scopes в Spring
1. Singleton (default)
@Component @Scope("singleton")
public class UserController { }
// Или просто:
@Component
public class UserController { } // Singleton по умолчанию
// Результат: 1 instance для всего приложения
2. Prototype
@Component @Scope("prototype")
public class RequestProcessor { }
// Результат: 1 instance на каждый запрос @Autowired
// При каждой инъекции — новый экземпляр
3. Request scope (для контроллеров)
@Component @Scope("request") @ConfigurationProperties(prefix = "request")
public class RequestContext { }
// Результат: 1 instance на HTTP request
// Хранит данные конкретного запроса
4. Session scope
@Component @Scope("session")
public class UserSession { }
// Результат: 1 instance на HTTP session (пользователя)
// Хранит сессионные данные
Практический пример
// Контроллер (Singleton)
@RestController
public class UserController {
private final UserService service;
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// userService — одна instance для всех
return service.getUser(id);
}
}
// Service (тоже Singleton по умолчанию)
@Service
public class UserService {
private final UserRepository repository;
public User getUser(Long id) {
// repository — одна instance
return repository.findById(id);
}
}
// Request-scoped component
@Component @Scope("request")
public class RequestContext {
private Long userId; // Это НЕ thread-safe глобально
// Но это OK, потому что каждый request имеет свой instance
public void setUserId(Long id) {
this.userId = id;
}
public Long getUserId() {
return userId;
}
}
// Использование
@RestController
public class PaymentController {
private final RequestContext context;
private final PaymentService paymentService;
@PostMapping("/pay")
public void pay(@RequestBody PaymentRequest request) {
context.setUserId(request.getUserId());
paymentService.processPayment(request);
}
}
Visualize
Singleton (контроллеры):
UserController@1a2b3c ├── Request 1 → UserController@1a2b3c ├── Request 2 → UserController@1a2b3c ├── Request 3 → UserController@1a2b3c └── Request 1000 → UserController@1a2b3c
// Все 1000 запросов используют ОДНУ instance
Request scope:
├── Request 1 → RequestContext@1 ├── Request 2 → RequestContext@2 ├── Request 3 → RequestContext@3 └── Request 1000 → RequestContext@1000
// Каждый запрос имеет свой экземпляр
Когда менять scope?
Singleton нужен для:
- Контроллеры
- Services
- Repositories
- Utilities
Request scope нужен для:
- Данные специфичные для HTTP request
- Например: текущий пользователь, locale, timezone
Prototype нужен для:
- Объекты с состоянием
- Редко используется в production
Performance
Singleton: ЛУЧШЕ
- 1 instance = меньше memory
- Нет overhead на создание/удаление
Request scope: МЕДЛЕННЕЕ
- Каждый request = new instance
- Garbage collection overhead
Пример: RequestContext с текущим пользователем
@Component @Scope("request")
public class SecurityContext {
private User currentUser;
public void setCurrentUser(User user) {
this.currentUser = user;
}
public User getCurrentUser() {
return currentUser;
}
}
@Component @Aspect
public class SecurityAspect {
private final SecurityContext context;
@Before("@annotation(Secure)")
public void beforeSecure(JoinPoint point) {
// Получаем текущего пользователя из request context
User user = context.getCurrentUser();
if (user == null) {
throw new UnauthorizedException();
}
}
}
Best Practices
- Controllers всегда Singleton — design stateless
- Services всегда Singleton — переиспользуй
- Request-specific data → Request scope
- Avoid Prototype — редко нужен
- Avoid fields in controllers — используй method parameters
Главный вывод
По умолчанию в Spring контроллеры это Singleton — создаётся 1 раз на всё приложение. Это нормально и эффективно, потому что контроллеры stateless.