Для чего используется scope prototype в Spring?

Ответ

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
    // Геттер и сеттер
}

А теперь, внимание, ебáная магия и подводные грабли:

  1. Spring — не нянька. Для prototype-бина контейнер делает только одно: рожает новый экземпляр, когда его просят (getBean() или инъекция). А дальше — хуй с ним. Spring не будет следить за его жизненным циклом, не вызовет методы уничтожения (@PreDestroy), ничего. Убить его, если надо, — это твоя головная боль, чувак.
  2. Главная засада. Если ты заинжектишь 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, а не просто потому что модно.