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

Сколько экземпляров контроллера создается при использовании Spring MVC?

1.8 Middle🔥 161 комментариев
#REST API и микросервисы#Spring Framework

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

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

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

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

Почему это нормально?

  1. Контроллеры 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);
    }
}

  1. 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

  1. Controllers всегда Singleton — design stateless
  2. Services всегда Singleton — переиспользуй
  3. Request-specific data → Request scope
  4. Avoid Prototype — редко нужен
  5. Avoid fields in controllers — используй method parameters

Главный вывод

По умолчанию в Spring контроллеры это Singleton — создаётся 1 раз на всё приложение. Это нормально и эффективно, потому что контроллеры stateless.

Сколько экземпляров контроллера создается при использовании Spring MVC? | PrepBro