Питання 12 · Розділ 5

В чому різниця між singleton та prototype scope?

4. Prototype → самі чистіть ресурси 5. Уникайте циклів у Prototype 6. Якщо потрібен просто об'єкт без DI, @PostConstruct і проксирування — звичайний new швидший, ніж Prototype з...

Мовні версії: English Russian Ukrainian

🟢 Junior Level

  Singleton Prototype
Екземплярів Один Новий щоразу
@PreDestroy ✅ Викликається ❌ НЕ викликається
Коли створюється Під час старту При запиті
@Service  // Singleton
public class A { }

@Scope("prototype")
@Service
public class B { }

A a1 = context.getBean(A.class);
A a2 = context.getBean(A.class);
a1 == a2  // → true (один об'єкт!)

B b1 = context.getBean(B.class);
B b2 = context.getBean(B.class);
b1 == b2  // → false (різні об'єкти!)

🟡 Middle Level

Singleton

// За замовчуванням!
// Зберігається в singletonObjects cache
 Швидкий доступ, не створюється щоразу

// Повинен бути Stateless!
 Усі потоки використовують один об'єкт
 Не зберігайте дані в полях!

Prototype

// Кожен запит → новий об'єкт
// Повний lifecycle: конструктор → @PostConstruct
// АЛЕ: @PreDestroy НЕ викликається!

// Ви самі відповідаєте за очищення:
PrototypeBean bean = context.getBean(PrototypeBean.class);
try {
    // використання
} finally {
    bean.cleanup();  // Вручну!
}

Проблема Prototype в Singleton

// ❌ Prototype "заморожується"
@Service
public class SingletonService {
    @Autowired PrototypeBean proto;  // Створився ОДИН раз!
}

// Чому: Singleton створюється один раз, і всі його залежності інжектяться
// під час створення. PrototypeBean створюється один раз при інжекції і
// далі перевикористовується — Spring не створює новий екземпляр при кожному
// зверненні до singleton-біна.

// ✅ ObjectProvider
@Service
public class SingletonService {
    @Autowired ObjectProvider<PrototypeBean> provider;

    public void process() {
        PrototypeBean proto = provider.getObject();  // Новий!
    }
}

🔴 Senior Level

Циклічні залежності

Singleton:
  → Spring може вирішити через earlySingletonObjects

Prototype:
  → Невирішувані в принципі!
  → A → B → A → нескінченна рекурсія
  → BeanCurrentlyInCreationException

Performance overhead

Singleton:
  → Створюється один раз
  → Швидкий доступ з кешу

Prototype:
  → Щоразу повний цикл створення
  → Рефлексія, BPP, @PostConstruct
  → При частих запитах → повільно!

Memory Leaks

Prototype з ресурсами:
  → Відкрив файл/з'єднання
  → Spring не викликає @PreDestroy
  → Ресурс не звільняється → витік!

Рішення:
  → try-finally з cleanup()
  → Або BeanPostProcessor для трекингу

Production Experience

Реальний сценарій: витік Prototype

У сценарії, де кожен Prototype-бін відкриває з'єднання:
10,000 запитів/сек → 10,000 з'єднань → БД падає

Рішення: Singleton + ObjectPool

Best Practices

  1. Singleton — 95% випадків (Stateless!)
  2. Prototype — важкі stateful об’єкти
  3. ObjectProvider — Prototype в Singleton
  4. Prototype → самі чистіть ресурси
  5. Уникайте циклів у Prototype
  6. Якщо потрібен просто об’єкт без DI, @PostConstruct і проксирування — звичайний new швидший, ніж Prototype з Spring-контексту.

Резюме для Senior

  • Singleton = кеш, Stateless, @PreDestroy працює
  • Prototype = новий щоразу, @PreDestroy НЕ працює
  • Prototype в Singleton → ObjectProvider
  • Цикли → невирішувані для Prototype
  • Ресурси → вручну чистіть у Prototype
  • Performance → Prototype дорожчий за Singleton

🎯 Шпаргалка для співбесіди

Обов’язково знати:

  • Singleton = один екземпляр, кешується; Prototype = новий при кожному getBean()
  • @PreDestroy викликається лише для Singleton, для Prototype — НІКОЛИ
  • Prototype в Singleton = проблема: Prototype створюється один раз і «заморожується»
  • Циклічні залежності вирішувані для Singleton (через earlySingletonObjects), але НЕ для Prototype
  • Performance: Prototype дорожчий — повний цикл створення щоразу (рефлексія, BPP, @PostConstruct)
  • Ресурси в Prototype — витік, чистіть вручну через try-finally
  • Якщо потрібен просто об’єкт без DI — використовуйте new, а не Prototype

Часті уточнюючі запитання:

  • Як отримати новий Prototype всередині Singleton? → ObjectProvider.getObject()
  • Чому циклічні залежності невирішувані для Prototype? → Нескінченна рекурсія створення, Spring кидає BeanCurrentlyInCreationException
  • Де зберігається Singleton? → singletonObjects cache (ConcurrentHashMap)
  • Коли виправданий Prototype? → Важкі stateful об’єкти, коли не можна використати Singleton + ThreadLocal

Червоні прапорці (НЕ говорити):

  • «@PreDestroy працює для Prototype» — не працює
  • «Singleton створюється при кожному запиті» — створюється один раз
  • «Prototype швидший за Singleton» — навпаки, повний цикл щоразу
  • «Цикли в Prototype вирішуються через @Lazy» — невирішувані в принципі

Пов’язані теми:

  • [[11. Які scope існують в Spring]]
  • [[13. Що таке proxy в Spring]]
  • [[19. Як вирішити проблему з self-invocation]]