Что такое Bean Lifecycle?
Три кэша для решения циклических зависимостей:
🟢 Junior Level
Bean Lifecycle — это все этапы жизни бина от создания до удаления.
Зачем знать lifecycle: (1) инициализировать ресурсы в правильный момент, (2) понимать, почему @Transactional не работает из @PostConstruct, (3) правильно очищать ресурсы, (4) решать циклические зависимости.
Простая аналогия: Жизнь человека: рождение → взросление → работа → пенсия.
Основные этапы:
1. Создание (конструктор)
2. Внедрение зависимостей
3. Инициализация (@PostConstruct)
4. Работа (готов к использованию)
5. Уничтожение (@PreDestroy)
В Spring 6 / Boot 3: используйте
jakarta.annotation.PostConstructвместоjavax.annotation.PostConstruct.
@Service
public class MyBean {
public MyBean() {
// 1. Создание
}
@PostConstruct
public void init() {
// 3. Инициализация
}
@PreDestroy
public void cleanup() {
// 5. Уничтожение
}
}
🟡 Middle Level
Фазы жизненного цикла
1. BeanDefinition → мета-информация
2. Instantiation → конструктор
3. Populate Properties → @Autowired
4. Aware Interfaces → setBeanName, setBeanFactory
5. @PostConstruct → инициализация
6. Ready → бин готов
7. @PreDestroy → очистка
Singleton vs Prototype
| Этап | Singleton | Prototype |
|---|---|---|
| Создание | Один раз | Каждый раз при запросе |
| Инициализация | ✅ Вызывается | ✅ Вызывается |
| Уничтожение | ✅ Вызывается | ❌ НЕ вызывается! |
// Prototype: вы сами должны очистить ресурсы!
PrototypeBean bean = context.getBean(PrototypeBean.class);
try {
// использование
} finally {
bean.cleanup(); // Вручную!
}
SmartLifecycle
// Для запуска/остановки компонентов
@Component
public class MyService implements SmartLifecycle {
public void start() { /* Запуск после инициализации всех бинов */ }
public void stop() { /* Остановка при shutdown */ }
public boolean isRunning() { return running; }
public int getPhase() { return 0; } // Порядок запуска
}
🔴 Senior Level
Когда НЕ использовать @PostConstruct
- Тяжёлая инициализация — блокирует запуск всего контекста. Используйте SmartLifecycle или @EventListener(ContextRefreshedEvent)
- Вызов @Transactional методов — прокси ещё не создан, транзакция не работает
- Операции с retry — если инициализация может упасть (нет подключения к БД), используйте SmartLifecycle с повторными попытками
Трёхуровневый кэш и циклы
Три кэша для решения циклических зависимостей:
- singletonObjects — готовые бины (полностью инициализированные)
- earlySingletonObjects — созданные, но не инициализированные (для разрыва циклов)
- singletonFactories — фабрики, создающие ранние ссылки
DefaultListableBeanFactory кэши:
1. singletonObjects → готовые бины
2. earlySingletonObjects → созданы, но не инициализированы
3. singletonFactories → фабрики для получения ранней ссылки
→ Решает циклы для Field/Setter Injection
→ Constructor Injection не может использовать кэш 3
Proxy Creation точка
BPP.postProcessAfterInitialization():
→ Здесь создаются AOP Proxy
→ Бин заменяется на Proxy в контейнере
@PostConstruct вызывается ДО прокси!
→ Вызов @Transactional метода из @PostConstruct НЕ сработает
→ Решение: ApplicationListener<ContextRefreshedEvent>
BPP Ordering
// Порядок BPP важен!
@Order(1)
public class FirstBPP implements BeanPostProcessor { }
@Order(2)
public class SecondBPP implements BeanPostProcessor { }
→ FirstBPP выполнится раньше
Production Experience
Реальный сценарий: @PostConstruct + @Transactional
@PostConstruct
public void init() {
loadData(); // @Transactional → НЕ работает!
}
// Причина: прокси ещё не создан
// Решение:
@EventListener(ContextRefreshedEvent.class)
public void init() {
loadData(); // Теперь прокси есть → транзакция работает
}
Best Practices
- @PostConstruct → валидация, инициализация
- @PreDestroy → очистка ресурсов
- Prototype → сами чистите ресурсы
- SmartLifecycle → порядок запуска
- @PostConstruct → нет прокси ещё!
- BPP order → управляйте через @Order
Резюме для Senior
- Instantiation ≠ Initialization
- Трёхуровневый кэш → решение циклов
- Proxy создаётся ПОСЛЕ @PostConstruct
- Prototype → @PreDestroy НЕ вызывается
- SmartLifecycle → старт/стоп компонентов
- ContextRefreshedEvent → когда всё готово
🎯 Шпаргалка для интервью
Обязательно знать:
- Bean Lifecycle: создание → внедрение зависимостей → инициализация (
@PostConstruct) → готов → уничтожение (@PreDestroy) - Lifecycle нужен для: инициализации ресурсов, понимания почему
@Transactionalне работает из@PostConstruct, очистки ресурсов, решения циклов - Singleton: все этапы 1-12,
@PreDestroyвызывается при закрытии контекста - Prototype: только создание и инициализация,
@PreDestroyНЕ вызывается — чистите вручную - Трёхуровневый кэш (singletonObjects, earlySingletonObjects, singletonFactories) решает циклические зависимости
- Proxy создаётся ПОСЛЕ
@PostConstruct— вызов@Transactionalметода из@PostConstructне сработает SmartLifecycle— для запуска/остановки компонентов с порядком (getPhase())- В Spring 6 / Boot 3:
jakarta.annotation.PostConstructвместоjavax.annotation
Частые уточняющие вопросы:
- Почему
@Transactionalне работает из@PostConstruct? Прокси создаётся после@PostConstruct, вызов идёт к оригинальному объекту. - Как решить проблему с транзакцией при инициализации?
@EventListener(ContextRefreshedEvent.class)— все прокси уже созданы. - Почему Prototype не вызывает
@PreDestroy? Spring не отслеживает уничтожение prototype-бинов — вызывайте cleanup вручную. - Что такое трёхуровневый кэш? Кэш готовых бинов, ранних ссылок и фабрик — решает циклы для Field/Setter Injection.
Красные флаги (НЕ говорить):
- «
@PostConstruct— место для тяжёлой инициализации» (блокирует старт контекста, используйтеSmartLifecycle) - «Prototype бины очищаются автоматически» (
@PreDestroyНЕ вызывается для Prototype) - «Все scope работают одинаково с lifecycle» (Prototype пропускает этапы уничтожения)
- «
@PostConstructи инициализация — одно и то же» (Instantiation ≠ Initialization)
Связанные темы:
- [[7. Какие этапы жизненного цикла Bean]]
- [[8. Что такое BeanPostProcessor]]
- [[9. Что делают методы с аннотацией @PostConstruct и @PreDestroy]]
- [[4. Что такое Bean в Spring]]
- [[10. Что такое scope Bean]]