В чому різниця між singleton та prototype scope?
4. Prototype → самі чистіть ресурси 5. Уникайте циклів у Prototype 6. Якщо потрібен просто об'єкт без DI, @PostConstruct і проксирування — звичайний new швидший, ніж Prototype з...
🟢 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
- Singleton — 95% випадків (Stateless!)
- Prototype — важкі stateful об’єкти
- ObjectProvider — Prototype в Singleton
- Prototype → самі чистіть ресурси
- Уникайте циклів у Prototype
- Якщо потрібен просто об’єкт без 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]]