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

Какие знаешь способы изменить 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 контексте