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

Какой Scope используется по умолчанию?

1.0 Junior🔥 201 комментариев
#Spring Boot и Spring Data#Spring Framework

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

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

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

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 объект на ApplicationContextServices, Repositories, Controllers
PrototypeНовый объект каждый разRequest DTO, Stateful objects
Request1 объект на HTTP запросWeb контекст
Session1 объект на сессиюWeb контекст
Application1 объект на 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 объектов в веб-приложении.