В чём разница между singleton и prototype scope?
4. Prototype → сами чистите ресурсы 5. Избегайте циклов в Prototype 6. Если нужен просто объект без DI, @PostConstruct и проксирования — обычный new быстрее, чем Prototype из Sp...
🟢 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. Что такое прокси в Spring]]
- [[19. Как решить проблему с self-invocation]]