Ответ
Scope prototype — это область видимости бина в Spring, при которой контейнер IoC создает новый экземпляр бина при каждом запросе (через getBean() или инъекцию зависимости).
Отличие от singleton (по умолчанию):
- Singleton: Один экземпляр на весь контекст приложения.
- Prototype: Новый экземпляр при каждом обращении.
Как объявить:
@Component
@Scope("prototype") // или @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeService {
private String id = UUID.randomUUID().toString();
public String getId() { return id; }
}
Когда использовать Prototype Scope:
- Состояние (Stateful): Когда бин хранит изменяемое состояние, специфичное для отдельного запроса или операции.
- Потокобезопасность: Чтобы избежать проблем с параллелизмом, если бин не является потокобезопасным.
- Дорогое создание, легкое использование: Если создание объекта недорогое, но его нельзя использовать повторно (например, объект-обертка для короткоживущих данных).
Важное замечание о внедрении в Singleton: При инъекции prototype-бина в singleton-бин, singleton получит один экземпляр prototype-бина при своей инициализации. Чтобы каждый вызов метода singleton возвращал новый prototype, используйте проксирование:
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
Или инжектируйте ObjectProvider<PrototypeService>.
Недостаток: Увеличивает нагрузку на сборщик мусора, так как создается много короткоживущих объектов.
Ответ 18+ 🔞
Да ты посмотри, что эти умники придумали! Scope prototype, блядь. Это ж как в пивнушке: каждый раз, когда ты кричишь «Налей!», тебе новый стакан суют, а не тот же самый, облизанный, из которого ты вчера пил.
В чём соль, если по-простому:
- Singleton (обычный режим): Один мужик на весь район. Все ходят к нему, он всем одно и то же бормочет. Экономно, но если он с утра не в духе — всем пиздец.
- Prototype: Новый мужик на каждый чих. Позвал — прибежал свеженький, только из душа. Отпустил — он пошёл нахуй, и больше его не видно.
Как такого урода создать:
@Component
@Scope("prototype")
public class PrototypeService {
private String id = UUID.randomUUID().toString();
public String getId() { return id; }
}
Видишь? Каждый новый экземпляр будет со своим уникальным id. Не как у всех, блядь, а особенный.
Когда это вот вообще надо:
- Когда бин — как носки. Сегодня надел — завтра выбросил. Хранит какую-то временную хуиту, которая каждому клиенту своя.
- Когда боишься, что всё поломается в многопоточке. Один на всех — он может запутаться и всем насрать в логи. А тут каждому потоку — своя отдельная копия, и пусть себе в неё срет.
- Когда создавать объект — дешево, а мучать его — недолго. Сделал, три раза ткнул — и в утиль. Жалеть нечего.
А теперь главная подстава, ебать её в сраку!
Если ты воткнёшь этот prototype-бин прямо в обычный singleton-бин через @Autowired, то нихуя не выйдет! Singleton родится один раз и схватит себе один-единственный экземпляр prototype’а на всю жизнь. И будет тыкать в него пальцем, как в чучело.
Что делать, чтобы обмануть систему? Вариант первый — прокси, блядь!
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
Спринг подсунет заглушку, а когда надо — будет создавать новый объект. Магия, епта!
Вариант второй — ObjectProvider. Это как диспенсер в туалете: потянул — тебе новый кусок бумаги выдали.
@Autowired
private ObjectProvider<PrototypeService> prototypeProvider;
public void doWork() {
PrototypeService freshOne = prototypeProvider.getObject(); // Новый каждый раз!
}
Чем это всё пахнет? Мусором, блядь! Сборщик мусора будет охуевать от количества трупов этих короткоживущих бинов. Так что если создание объекта — это как построить дворец, а живёт он две секунды, то лучше подумать триста раз. А так — да, иногда без этого никуда, иначе все в одной куче перепутаются.