Какой Scope используется по умолчанию?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Scope бинов в Spring: значение по умолчанию
В Spring Framework каждый бин имеет scope (область действия), который определяет жизненный цикл и видимость объекта. Если разработчик не указывает scope явно, используется scope по умолчанию: Singleton.
Singleton — скоп по умолчанию
Singleton означает, что Spring создаёт ровно один экземпляр бина на весь ApplicationContext и переиспользует его всегда.
@Component
public class UserService { // Без явного указания scope
public UserService() {
System.out.println("UserService создан");
}
}
// Это эквивалентно:
@Component
@Scope("singleton") // явное указание
public class UserService {
public UserService() {
System.out.println("UserService создан");
}
}
// Вывод при запуске приложения:
// UserService создан <- вывод ОДИН раз
// Все инъекции получают ВЕЛ ЖЕ объект
Жизненный цикл Singleton
1. ApplicationContext запустился
↓
2. Spring сканирует @Component классы
↓
3. Создаёт бины (вызывает конструктор один раз)
↓
4. Инъектирует зависимости (@Autowired)
↓
5. Вызывает @PostConstruct методы
↓
6. Бин живёт до завершения ApplicationContext
↓
7. Вызывает @PreDestroy методы
↓
8. ApplicationContext закрывается
Пример: все получают ОДН объект
@Component
public class UserService {
private int counter = 0;
public void increment() {
counter++;
System.out.println("Counter: " + counter);
}
}
@Component
public class OrderService {
@Autowired
private UserService userService;
public void process() {
userService.increment();
}
}
@Component
public class PaymentService {
@Autowired
private UserService userService;
public void pay() {
userService.increment();
}
}
// При запуске:
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
OrderService order = context.getBean(OrderService.class);
PaymentService payment = context.getBean(PaymentService.class);
order.process(); // Counter: 1
payment.pay(); // Counter: 2 <- один и тот же объект!
// Проверка:
UserService us1 = context.getBean(UserService.class);
UserService us2 = context.getBean(UserService.class);
System.out.println(us1 == us2); // true <- ОДИН объект
Другие скопы в Spring
Для сравнения, вот остальные скопы:
Prototype — новый объект для каждой инъекции/запроса
@Component
@Scope("prototype")
public class RequestContext {
}
// Результат: новый объект каждый раз
RequestContext rc1 = context.getBean(RequestContext.class);
RequestContext rc2 = context.getBean(RequestContext.class);
System.out.println(rc1 == rc2); // false <- разные объекты
Request — один объект на HTTP запрос (Web сценарий)
@Component
@Scope("request")
public class UserSession {
}
// Результат: один объект на весь HTTP запрос
// Request 1 → один экземпляр UserSession
// Request 2 → новый экземпляр UserSession
Session — один объект на HTTP сессию
@Component
@Scope("session")
public class ShoppingCart {
}
// Результат: один объект на весь пользователя
// Сессия пользователя 1 → один экземпляр ShoppingCart
// Сессия пользователя 2 → новый экземпляр ShoppingCart
Application — один объект на весь ServletContext
@Component
@Scope("application")
public class AppConfig {
}
// Результат: один объект на всё приложение
// (редко используется)
Почему Singleton по умолчанию?
1. Эффективность памяти
// Singleton (по умолчанию)
@Component
public class UserService { } // 1 объект × 1000 инъекций = 1 объект в памяти
// Prototype
@Component
@Scope("prototype")
public class UserService { } // 1 объект × 1000 инъекций = 1000 объектов!
2. Потокобезопасность (важно!)
Singleton бины обычно stateless (без состояния):
@Component
public class UserService { // Singleton
// ✅ Безопасно для многопоточности
public User getUser(int id) {
return userRepository.findById(id);
}
}
// vs
@Component
@Scope("prototype")
public class RequestContext { // Prototype
// ✅ Каждый поток имеет свой объект (безопасно)
private Map<String, Object> data = new HashMap<>();
}
3. Ленивая инициализация
Для Singleton можно включить ленивую инициализацию (создание при первом использовании):
@Component
@Lazy
public class HeavyService {
// Создастся только при первом @Autowired, не при старте
}
Проверка скопа программно
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
ConfigurableListableBeanFactory factory = context.getBeanFactory();
// Для UserService (singleton по умолчанию)
String scope = factory.getBeanDefinition("userService").getScope();
System.out.println(scope); // Вывод: "singleton"
// Получить все скопы
for (String beanName : context.getBeanDefinitionNames()) {
BeanDefinition def = factory.getBeanDefinition(beanName);
System.out.println(beanName + " -> " + def.getScope());
}
@Scope с параметрами
// Явное указание singleton (эквивалентно умолчанию)
@Component
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class UserService { }
// С проксированием
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestContext {
// Проксирование позволяет инъектировать prototype в singleton
}
Таблица скопов
| Скоп | Значение | Применение |
|---|---|---|
| Singleton (default) | 1 объект на ApplicationContext | Services, Repositories, Controllers |
| Prototype | Новый объект каждый раз | Request DTO, Stateful objects |
| Request | 1 объект на HTTP запрос | Web контекст |
| Session | 1 объект на сессию | Web контекст |
| Application | 1 объект на ServletContext | Редко |
Потенциальные проблемы с Singleton
Проблема 1: Shared State (опасно!)
// ❌ НЕПРАВИЛЬНО
@Component
public class UserService {
private User currentUser; // ОПАСНО! Singleton совместно используется
public void setCurrentUser(User user) {
this.currentUser = user; // Один поток перезаписывает другому
}
}
// ✅ ПРАВИЛЬНО
@Component
public class UserService {
// Используй ThreadLocal или RequestContext для thread-specific данных
public User getCurrentUser() {
return RequestContext.getCurrentUser();
}
}
Проблема 2: Инъекция Prototype в Singleton
// ❌ ПЛОХО
@Component
public class UserService { // Singleton
@Autowired
private RequestContext context; // Prototype, но инъектирован один раз!
}
// ✅ ХОРОШО
@Component
public class UserService { // Singleton
@Autowired
private ObjectFactory<RequestContext> contextFactory; // Фабрика для создания
public void process() {
RequestContext context = contextFactory.getObject(); // Новый каждый раз
}
}
Заключение
По умолчанию используется Singleton scope.
Это правильный выбор для большинства компонентов (сервисы, репозитории, контроллеры), так как:
- Экономит память
- Хорош для stateless объектов
- Быстрее создания
- Подходит для многопоточной работы
Изменяй scope только если имеет смысл для конкретного компонента, например для Request-scoped объектов в веб-приложении.