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

Что такое Prototype Bean?

1.3 Junior🔥 161 комментариев
#JavaScript Core#TypeScript

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

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

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

Что такое Prototype Bean?

В контексте Spring Framework (и других IoC-контейнеров, таких как Spring Boot), Prototype Bean — это бин, обладающий областью видимости (scope) "prototype". Это означает, что каждый раз, когда контейнер получает запрос на этот бин (через ApplicationContext.getBean(), инъекцию зависимости или lookup), создаётся новый экземпляр объекта. В отличие от бина с областью видимости singleton (который является значением по умолчанию в Spring), где контейнер создаёт и управляет единственным экземпляром на весь контекст приложения.

Ключевые характеристики Prototype Bean

  • Множественные экземпляры: Для каждого запроса или инъекции создаётся новый объект.
  • Управление жизненным циклом: Контейнер Spring отвечает за создание и инициализацию prototype-бина (вызывает методы @PostConstruct или init-method), но НЕ управляет его уничтожением. Методы @PreDestroy или destroy-method не вызываются. Ответственность за очистку ресурсов (если требуется) ложится на клиентский код или сам объект (например, через try-with-resources или явный вызов метода закрытия).
  • Использование памяти: Может привести к повышенному потреблению памяти, если создаётся слишком много экземпляров без контроля.
  • Внедрение зависимостей: Если singleton-бин зависит от prototype-бина, то prototype-бин инжектируется один раз при создании singleton-а. Чтобы получать новый экземпляр при каждом вызове, необходимо использовать проксирование или программный lookup (например, через ApplicationContextAware или @Lookup аннотацию).

Как объявить Prototype Bean?

В Spring существует несколько способов указать область видимости:

1. Использование аннотации @Scope:

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("prototype") // или @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeService {
    private String id = UUID.randomUUID().toString();

    public String getId() {
        return id;
    }
}

2. В XML-конфигурации:

<bean id="prototypeService" class="com.example.PrototypeService" scope="prototype"/>

3. Использование в Java Config:

@Configuration
public class AppConfig {
    @Bean
    @Scope("prototype")
    public MyBean myBean() {
        return new MyBean();
    }
}

Практический пример и отличие от Singleton

Рассмотрим наглядный пример:

@Component
@Scope("prototype")
public class ShoppingCart {
    private List<String> items = new ArrayList<>();
    
    public void addItem(String item) {
        items.add(item);
    }
    
    public List<String> getItems() {
        return items;
    }
}

@Service
public class OrderService {
    private final ShoppingCart cart;
    
    @Autowired
    public OrderService(ShoppingCart cart) {
        this.cart = cart;
    }
    
    public void processOrder() {
        cart.addItem("Product1");
        System.out.println(cart.getItems());
    }
}

Если ShoppingCart — prototype, то каждый раз, когда создаётся OrderService (или если используется @Lookup), будет создаваться новая корзина. Если же ShoppingCart — singleton, все заказы будут использовать одну и ту же корзину, что приведёт к ошибкам в данных.

Важные нюансы и best practices

  • Производительность: Создание prototype-бинов дороже, чем singleton-ов, из-за постоянной инстанциации и инициализации. Следует избегать их чрезмерного использования для тяжёлых объектов.
  • Взаимодействие с Singleton-бинами: Как упоминалось, проблема "замороженного" prototype-бина внутри singleton-а решается:
    * Через `javax.inject.Provider`:
```java
@Autowired
private Provider<ShoppingCart> cartProvider;

public void process() {
    ShoppingCart cart = cartProvider.get(); // Новый экземпляр каждый раз
}
```
    * Через метод с аннотацией `@Lookup`.
    * Через `ObjectFactory` (Spring-аналог `Provider`).
  • Очистка ресурсов: Поскольку Spring не вызывает методы уничтожения, для ресурсов, требующих освобождения (например, соединения с БД, файловые дескрипторы), необходимо реализовать собственный механизм (паттерн DisposableBean, явный метод close(), использование try-with-resources в клиентском коде).
  • Тестирование: Prototype-бины проще тестировать изолированно, так как каждый тест получает свежий экземпляр без остаточного состояния.

Типичные сценарии использования

  • Состояние, уникальное для каждого запроса: Например, пользовательская сессия, корзина покупок в интернет-магазине.
  • Потокобезопасность: Если объект не является потокобезопасным и используется в многопоточной среде, prototype-область позволяет избежать состояния гонки, предоставляя каждому потоку свой экземпляр.
  • Сложные объекты с изменяемым состоянием, которые не должны делиться между разными клиентами.

Вывод: Prototype Bean — мощный инструмент в Spring, позволяющий управлять жизненным циклом объектов с требованием уникального экземпляра на запрос. Однако его использование требует понимания особенностей управления памятью, жизненным циклом и инъекциями зависимостей, чтобы избежать типичных ошибок, таких как утечки памяти или неожиданное поведение из-за неправильного взаимодействия с singleton-бина.