Вопрос 12 · Раздел 5

В чём разница между singleton и prototype scope?

4. Prototype → сами чистите ресурсы 5. Избегайте циклов в Prototype 6. Если нужен просто объект без DI, @PostConstruct и проксирования — обычный new быстрее, чем Prototype из Sp...

Версии по языкам: 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. Что такое прокси в Spring]]
  • [[19. Как решить проблему с self-invocation]]