← Назад к вопросам
Какие знаешь способы изменить Scope Bean?
2.0 Middle🔥 201 комментариев
#Spring Boot и Spring Data#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Способы изменить Scope Bean в Spring
В Spring есть несколько встроенных scopes для beans, и разные способы их использовать и изменять.
Встроенные Scopes в Spring
1. Singleton (default)
@Component // по умолчанию singleton
@Scope("singleton") // явно указываем
public class UserService {
// Один экземпляр на весь контекст
// Создаётся при загрузке контекста
// Потокобезопасность - ответственность разработчика
}
// Usage
@Service
public class Controller {
@Autowired
private UserService userService; // всегда один и тот же объект
}
2. Prototype
@Component
@Scope("prototype")
public class RequestHandler {
// Новый экземпляр каждый раз
// Не управляется Spring (нет destroy callback)
}
// Usage
@Service
public class Service {
@Autowired
private ObjectFactory<RequestHandler> handlerFactory;
public void process() {
RequestHandler handler1 = handlerFactory.getObject(); // новый
RequestHandler handler2 = handlerFactory.getObject(); // новый, другой
}
}
3. Request (Web scope)
@Component
@Scope("request")
public class RequestContext {
// Один экземпляр на HTTP request
// Создаётся для каждого запроса
// Уничтожается после запроса
}
// Usage в контроллере
@RestController
public class MyController {
@Autowired
private RequestContext context; // разный для каждого запроса
@GetMapping("/data")
public String getData() {
context.setUserId(123); // сохраняется в контексте запроса
return "OK";
}
}
4. Session (Web scope)
@Component
@Scope("session")
public class UserSession {
private String userName;
// Один экземпляр на HTTP session
// Когда session умирает - bean разрушается
}
// Usage
@RestController
public class UserController {
@Autowired
private UserSession session; // разный для каждого пользователя
@GetMapping("/profile")
public User getProfile() {
return userService.findBy(session.getUserId());
}
}
5. Application/Global Session (deprecated, но ещё используется)
@Component
@Scope("application")
public class GlobalConfig {
// Один на весь servlet контекст
// Практически то же самое что singleton
}
Способы изменить Scope Bean
Способ 1: Аннотация @Scope
// Было
@Component
public class UserService {}
// Изменили на
@Component
@Scope("prototype")
public class UserService {
// Теперь новый экземпляр каждый раз
}
Способ 2: Через @Scope с proxyMode
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestHandler {
// Создаёт proxy который инжектирует нужный экземпляр
}
// Без proxyMode singleton bean не может получить request bean
// Потому что request не существует при создании singleton
@Service
public class MyService {
@Autowired
private RequestHandler handler; // работает благодаря proxy
}
Способ 3: ObjectFactory (программный доступ)
@Component
public class Service {
@Autowired
private ObjectFactory<PrototypeBean> factory;
public void process() {
// Получаешь новый bean каждый раз
PrototypeBean bean1 = factory.getObject();
PrototypeBean bean2 = factory.getObject(); // разные
}
}
@Component
@Scope("prototype")
class PrototypeBean {}
Способ 4: ObjectProvider (лучший вариант ObjectFactory)
@Component
public class Service {
@Autowired
private ObjectProvider<PrototypeBean> provider;
public void process() {
PrototypeBean bean1 = provider.getObject();
PrototypeBean bean2 = provider.getObject(); // разные
// ObjectProvider умнее - может возвращать Optional
provider.ifAvailable(bean -> bean.process());
}
}
Способ 5: Через конфигурационный класс
@Configuration
public class BeanConfig {
// Было
@Bean
public UserService userService() {
return new UserService(); // singleton по умолчанию
}
// Изменили на
@Bean
@Scope("prototype")
public UserService userService() {
return new UserService(); // теперь prototype
}
}
Способ 6: BeanFactory напрямую
@Service
public class DynamicBeanService {
@Autowired
private BeanFactory beanFactory;
public void getBean() {
// Получить bean из factory
Object bean = beanFactory.getBean("userService");
}
}
Способ 7: Через properties/конфигурацию
@Component
@Scope(value = "${bean.scope:singleton}")
public class ConfigurableBean {
// Scope можно менять через properties
}
// application.yml
// bean:
// scope: prototype
Практические примеры
Проблема: Singleton bean не может использовать Request scope
// ОШИБКА - Error creating bean
@Service
public class UserService {
@Autowired
private RequestContext context; // context - request scope
// Singleton UserService пытается инжектировать Request bean
// Это невозможно!
}
// Решение 1: Использовать proxy
@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestContext {
// Теперь работает
}
@Service
public class UserService {
@Autowired
private RequestContext context; // работает! proxy решает проблему
}
// Решение 2: Использовать ObjectProvider
@Service
public class UserService {
@Autowired
private ObjectProvider<RequestContext> contextProvider;
public void process() {
RequestContext context = contextProvider.getObject();
// получили нужный для этого запроса context
}
}
// Решение 3: Сам bean сделать prototype
@Component
@Scope("request")
public class RequestUserService {
@Autowired
private RequestContext context; // теперь оба request scope
}
Пример: Request scope для логирования
@Component
@Scope("request")
public class RequestLogger {
private String requestId;
private long startTime;
@PostConstruct
public void init() {
requestId = UUID.randomUUID().toString();
startTime = System.currentTimeMillis();
}
public String getRequestId() {
return requestId;
}
public long getDuration() {
return System.currentTimeMillis() - startTime;
}
}
@RestController
public class Controller {
@Autowired
private RequestLogger logger;
@GetMapping("/api/data")
public String getData() {
System.out.println("Request ID: " + logger.getRequestId());
// ID будет разным для каждого запроса
return "OK";
}
}
Сравнение scopes
| Scope | Создание | Жизненный цикл | Потокобезопасность |
|---|---|---|---|
| Singleton | При загрузке контекста | Весь контекст | Нет |
| Prototype | Каждый раз | До GC | Да |
| Request | Каждый запрос | До конца запроса | Да |
| Session | Каждая сессия | До конца сессии | Да |
Когда менять scope?
Singleton (default) - 99% случаев
@Service
public class UserService {}
@Repository
public class UserRepository {}
Prototype - если bean содержит state
@Component
@Scope("prototype")
public class RequestHandler {
private String data; // state - нужен prototype
}
Request scope - для логирования, трейсинга
@Component
@Scope("request")
public class RequestContext {
private String userId; // переменная на запрос
}
Вывод
Чаще всего используются Singleton и Prototype. Request/Session scopes нужны для web приложений. Запомни:
- По умолчанию все beans - singleton
- Используй @Scope аннотацию для изменения
- Для инжекции beans с меньшим scope используй proxy или ObjectProvider
- Prototype beans требуют ручного управления
- Request/Session scopes работают только в web контексте