В каких случаях в Spring требуется создавать несколько экземпляров бина одного типа?

«В каких случаях в Spring требуется создавать несколько экземпляров бина одного типа?» — вопрос из категории Spring, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

По умолчанию Spring создает бины в singleton scope (один экземпляр на контейнер). Несколько экземпляров требуются в следующих сценариях:

  1. Прототипные (prototype) бины: Когда состояние бина не должно разделяться между запросами.

    @Component
    @Scope("prototype") // Каждый запрос на внедрение/получение создает новый объект
    public class ShoppingCart {
        private List<Item> items = new ArrayList<>();
        // У каждого пользователя/сессии своя корзина
    }
  2. Несколько реализаций одного интерфейса с разной конфигурацией:

    @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;
        }
    }
  3. Бины, зависящие от внешнего контекста (например, HTTP-запроса или сессии):

    @Component
    @Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
    public class UserPreferences {
        // У каждого HTTP-сеанса будет свой экземпляр
    }
  4. Создание бинов динамически, на основе условий:

    @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) – для создания разных бинов в разных средах.