Що таке 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 lifecycle]]
- [[8. Що таке BeanPostProcessor]]
- [[9. Що роблять методи з анотаціями @PostConstruct та @PreDestroy]]
- [[4. Що таке Bean в Spring]]
- [[10. Що таке Bean scope]]