Ответ
Scope prototype в Spring указывает контейнеру создавать новый экземпляр бина при каждом запросе (через getBean() или инъекцию), в отличие от scope singleton, который предоставляет один общий экземпляр на весь контекст.
Зачем это нужно:
- Для хранения состояния, уникального для отдельного клиента (например, данные пользовательской сессии в веб-запросе).
- Когда бин не является потокобезопасным и должен быть изолирован.
- Чтобы избежать побочных эффектов от изменений состояния бина в разных частях приложения.
Пример объявления:
@Component
@Scope("prototype")
public class PrototypeScopedBean {
private String requestId;
// Геттер и сеттер
}
Важно:
- Полный жизненный цикл prototype-бина (включая уничтожение) лежит на ответственности клиентского кода, Spring не управляет им после создания и инъекции.
- При инъекции prototype-бина в singleton-бин необходимо использовать проксирование (
@Scope(scopeName = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)) или lookup-метод, чтобы гарантировать получение нового экземпляра при каждом обращении.
Ответ 18+ 🔞
Давай разберём эту вашу прототипную хуйню в Spring, а то как будто на лекции по квантовой механике сидишь.
Смотри, есть у нас обычный синглтон — это как одна бутылка водки на всю компанию. Все тянутся, пьют из неё, и состояние у неё одно на всех: сегодня полная, завтра пустая, послезавтра с запиской «спасибо, было вкусно». А prototype — это когда каждому пришедшему наливают новую стопку. Свою, отдельную. Хочешь — выпей, хочешь — разлей, хочешь — в морду соседу брось. У каждого своя стопка, и состояние у неё уникальное.
Зачем это, спрашивается, нахуй?
- Чтобы хранить какие-то данные, которые только для одного клиента. Например, в каждом HTTP-запросе свой набор данных — корзина, ID сессии, хуйня какая-то временная. Если всё в одну общую кучу пихать, получится пиздец: один пользователь увидит корзину другого, и все обоссутся.
- Если твой бин — не потокобезопасный уродец, который при параллельном использовании начинает глючить и всё ломает. Лучше каждому потоку дать свой экземпляр, и пусть сами в нём ковыряются.
- Чтобы избежать ситуации, когда один еблан в одном углу приложения поменял состояние бина, а второй еблан в другом углу от этого охуел и всё упало.
Как объявить, чтоб не облажаться:
@Component
@Scope("prototype") // Вот этот волшебный пиздёж
public class PrototypeScopedBean {
private String requestId; // У каждого нового экземпляра будет своя requestId
// Геттер и сеттер
}
А теперь, внимание, ебáная магия и подводные грабли:
- Spring — не нянька. Для prototype-бина контейнер делает только одно: рожает новый экземпляр, когда его просят (
getBean()или инъекция). А дальше — хуй с ним. Spring не будет следить за его жизненным циклом, не вызовет методы уничтожения (@PreDestroy), ничего. Убить его, если надо, — это твоя головная боль, чувак. - Главная засада. Если ты заинжектишь prototype-бин в singleton-бин обычным способом, то получишь пиздец. Singleton-бин создаётся один раз, и в него при создании вставится один-единственный экземпляр prototype-бина. И всё. На этом про «прототипность» можно забыть. Будешь 100500 раз обращаться к singleton-у — он будет тыкать в один и тот же, закэшированный при инъекции, экземпляр. Полный облом.
Как это побороть, ёпта?
-
Проксирование (
ScopedProxyMode.TARGET_CLASS). Самый частый способ. Ты как бы говоришь Spring: «Слушай, вставь сюда не настоящий бин, а свою прокси-заглушку. И чтобы каждый раз, когда кто-то к ней обращается, ты, сука, создавал новый экземпляр и делегировал вызов ему».@Component @Scope(scopeName = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) public class PrototypeScopedBean { ... }Теперь, даже если этот бин будет заинжекчен в синглтон, при каждом обращении к его методам Spring будет делать
getBean()и отдавать свеженький экземпляр. Хитро, блядь. -
Lookup-метод. Старый, дедовский способ, через абстрактный метод. Но сейчас все обычно проксями обходятся, это попроще.
Короче, prototype — это мощный инструмент, когда нужно изоляцию и свежесть экземпляров. Но если использовать его без мозгов, можно такую дичь получить, что потом неделю будешь дебажить, почему у всех пользователей один и тот же requestId. Думай головой, когда выбираешь scope, а не просто потому что модно.