Ответ
По умолчанию Spring создает бины в singleton scope (один экземпляр на контейнер). Несколько экземпляров требуются в следующих сценариях:
-
Прототипные (prototype) бины: Когда состояние бина не должно разделяться между запросами.
@Component @Scope("prototype") // Каждый запрос на внедрение/получение создает новый объект public class ShoppingCart { private List<Item> items = new ArrayList<>(); // У каждого пользователя/сессии своя корзина } -
Несколько реализаций одного интерфейса с разной конфигурацией:
@Configuration public class DataSourceConfig { @Bean @Primary // Основной бин для автопривязки без @Qualifier public DataSource primaryDataSource() { return DataSourceBuilder.create() .url("jdbc:mysql://host1/db") .username("user1") .build(); } @Bean public DataSource secondaryDataSource() { return DataSourceBuilder.create() .url("jdbc:mysql://host2/db") .username("user2") .build(); } }Использование с
@Qualifier:@Service public class ReportingService { private final DataSource readOnlyDataSource; private final DataSource writeDataSource; public ReportingService( @Qualifier("secondaryDataSource") DataSource readOnlyDataSource, @Qualifier("primaryDataSource") DataSource writeDataSource) { this.readOnlyDataSource = readOnlyDataSource; this.writeDataSource = writeDataSource; } } -
Бины, зависящие от внешнего контекста (например, HTTP-запроса или сессии):
@Component @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) public class UserPreferences { // У каждого HTTP-сеанса будет свой экземпляр } -
Создание бинов динамически, на основе условий:
@Configuration public class DynamicBeanConfig { @Bean @ConditionalOnProperty(name = "cache.provider", havingValue = "redis") public CacheManager redisCacheManager() { /* ... */ } @Bean @ConditionalOnProperty(name = "cache.provider", havingValue = "caffeine") public CacheManager caffeineCacheManager() { /* ... */ } }
Ключевые аннотации и подходы:
@Scope("prototype")– для создания нового экземпляра при каждом запросе.@Qualifier("имяБина")– для выбора конкретной реализации при внедрении зависимости.@Primary– чтобы пометить бин по умолчанию.- Условное создание бинов (
@Conditional,@Profile) – для создания разных бинов в разных средах.
Ответ 18+ 🔞
А, ну вот, опять про эти ваши бины в Спринге! Сидит народ и думает: "Один экземпляр на весь контейнер, и всем хватает". А потом такой: "Ой, а у меня у всех пользователей одна корзина в интернет-магазине, блядь!". Ну да, конечно, один на всех ShoppingCart — это ж как в коммуналке мыло, всем одно, ебать.
Так вот, слушай, когда тебе надо несколько экземпляров, а не один синглтон, который на всех поршан:
-
Прототипные бины, ёпта. Это когда состояние у каждого должно быть своё, личное, интимное. Представь, что у тебя
ShoppingCart. Если он синглтон, то я, ты и твоя бабушка — все кладём товары в одну общую корзину. Пиздец, а не заказ. Поэтому вешаем@Scope("prototype"). Каждый раз, когда кто-то просит этот бин — ему выдают свеженький, только что с конвейера, и он там один со своимиitemsможет делать что хочет. Чистая магия, блядь.@Component @Scope("prototype") // Вот эта хуйня делает новый объект каждый раз, когда его просят public class ShoppingCart { private List<Item> items = new ArrayList<>(); // У каждого юзера теперь своя корзина, а не общий сортир } -
Несколько реализаций одного интерфейса. Классика, блядь! Допустим, у тебя два источника данных: один для чтения, другой для записи. Spring смотрит на интерфейс
DataSourceи охуевает: "Какой из двух, нахуй, тебе подсовывать?". А ты ему: "Да вот этот, падла, для чтения, а вот этот — для записи!". Для этого есть@Qualifier. А чтобы не париться и не указывать его везде, одному из бинов можно дать медальку@Primary— он будет главным по умолчанию.@Configuration public class DataSourceConfig { @Bean @Primary // Этот — главный пацан. Будет вставляться, если не кричать "я хочу другой!" public DataSource primaryDataSource() { return DataSourceBuilder.create() .url("jdbc:mysql://host1/db") .username("user1") .build(); } @Bean public DataSource secondaryDataSource() { return DataSourceBuilder.create() .url("jdbc:mysql://host2/db") .username("user2") .build(); } }А вот как их потом различать, когда внедряешь:
@Service public class ReportingService { private final DataSource readOnlyDataSource; private final DataSource writeDataSource; public ReportingService( @Qualifier("secondaryDataSource") DataSource readOnlyDataSource, // Кричим: "Дай мне secondary!" @Qualifier("primaryDataSource") DataSource writeDataSource) { // А тут: "Дай primary!" this.readOnlyDataSource = readOnlyDataSource; this.writeDataSource = writeDataSource; } } -
Бины, привязанные к контексту (сессии, запросу). Это вообще отдельная песня, ебать мои старые костыли. Допустим, настройки пользователя. Они же у каждого свои, мать их! Нельзя же одну на всех
UserPreferencesсделать. Вешаем scopeSCOPE_SESSION— и у каждого HTTP-сеанса будет свой личный экземпляр. АproxyMode— это такая хитрая жопа, чтобы Spring мог вставить прокси, даже если бин ещё не создан.@Component @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS) public class UserPreferences { // У Васи — свои настройки, у Пети — свои. И все довольны. } -
Динамическое создание, на основе каких-то условий. Тут вообще раздолье, блядь. Хочешь Redis-кэш, если в настройках
cache.provider=redis? Пожалуйста! Хочешь Caffeine? Да не вопрос! Spring почитаетapplication.propertiesи создаст только тот бин, который подходит под условие. Остальные — нахуй, их даже не будет в контексте.@Configuration public class DynamicBeanConfig { @Bean @ConditionalOnProperty(name = "cache.provider", havingValue = "redis") public CacheManager redisCacheManager() { /* ... */ } // Создастся, только если property совпало @Bean @ConditionalOnProperty(name = "cache.provider", havingValue = "caffeine") public CacheManager caffeineCacheManager() { /* ... */ } }
Короче, запомни, чувак:
@Scope("prototype")— когда надо "каждому по новенькому".@Qualifier("имяБина")— когда кричишь Spring'у: "Не любой давай, а конкретно вот этот, сука!".@Primary— когда говоришь: "Вот этот — мой любимчик, его вставляй, если я не уточнил".- Условные аннотации (
@Conditional,@Profile) — когда твоя конфигурация зависит от звёзд, фазы луны или, что более вероятно, от переменной окружения.