Какие этапы жизненного цикла Bean?
BPP (BeanPostProcessor) — механизм Spring для изменения бинов. Ключевые BPP:
🟢 Junior Level
Этапы создания бина:
1. Конструктор → объект создан
2. @Autowired → зависимости внедрены
3. @PostConstruct → инициализация
4. Готов к работе
5. @PreDestroy → очистка при удалении
@Service
public class MyService {
private final Repo repo;
// 1. Конструктор
public MyService(Repo repo) {
this.repo = repo;
}
// 2. @Autowired уже сработал (через конструктор)
// 3. Инициализация
@PostConstruct
public void init() {
System.out.println("Bean готов!");
}
// 5. Очистка
@PreDestroy
public void cleanup() {
System.out.println("Bean удаляется");
}
}
🟡 Middle Level
Этот вопрос детализирует этапы из файла [[6. Что такое Bean Lifecycle]]. Если тот файл объясняет ЗАЧЕМ, то этот — КАК именно (12 шагов).
Подробный порядок
1. Instantiation (конструктор)
2. Populate Properties (@Autowired поля/сеттеры)
3. Aware Interfaces
→ BeanNameAware.setBeanName()
→ BeanFactoryAware.setBeanFactory()
→ ApplicationContextAware.setApplicationContext()
4. BeanPostProcessor.postProcessBeforeInitialization()
5. @PostConstruct
6. InitializingBean.afterPropertiesSet()
7. init-method (@Bean(initMethod="init"))
8. BeanPostProcessor.postProcessAfterInitialization() ← Proxy!
9. READY
10. @PreDestroy
11. DisposableBean.destroy()
12. destroy-method
Singleton vs Prototype
// Singleton:
// → Все этапы 1-12
// → Уничтожается при закрытии контекста
// Prototype:
// → Только этапы 1-9
// → @PreDestroy НЕ вызывается автоматически. Spring не отслеживает их уничтожение. Если нужно — вызывайте cleanup вручную или используйте `ConfigurableBeanFactory.registerDestructionCallback()`.
ContextRefreshedEvent
// Когда ВСЕ бины созданы и инициализированы:
@EventListener(ContextRefreshedEvent.class)
public void onApplicationReady() {
// Все прокси созданы, все бины готовы
// Можно вызывать @Transactional методы!
}
🔴 Senior Level
Instantiation vs Initialization
Instantiation:
→ new Service() через рефлексию
→ Объект существует в Heap
Initialization:
→ Внедрение зависимостей
→ @PostConstruct
→ Proxy creation
→ В конструкторе зависимости ещё не внедрены (кроме Constructor Injection)
BPP цепочка
BPP (BeanPostProcessor) — механизм Spring для изменения бинов. Ключевые BPP:
- CommonAnnotationBeanPostProcessor — обрабатывает @PostConstruct/@PreDestroy
- AutowiredAnnotationBeanPostProcessor — обрабатывает @Autowired
- AnnotationAwareAspectJAutoProxyCreator — создаёт прокси для @Transactional, @Async
Много BPP выполняются последовательно:
1. CommonAnnotationBeanPostProcessor → @PostConstruct
2. AutowiredAnnotationBeanPostProcessor → @Autowired
3. AnnotationAwareAspectJAutoProxyCreator → Proxy
→ Каждый может изменить или заменить бин
→ Порядок важен! → @Order
Ранняя инициализация BPP
// Если ваш BPP зависит от MyService:
// → MyService создаётся РАНЬШЕ остальных BPP
// → MyService может не получить прокси!
// Почему: BPP, создающие прокси (например, AnnotationAwareAspectJAutoProxyCreator для @Transactional), ещё не зарегистрированы. MyService создаётся «голым», без прокси-обёртки. Решение: не инжектируйте бины в BPP — используйте ObjectProvider или ApplicationContext.getBean() с осторожностью.
// Решение: @Lazy или независимые BPP
Production Experience
Реальный сценарий: BPP не получил прокси
- Кастомный BPP зависел от DataSource
- DataSource создался раньше → без транзакций
- Решение: @Lazy зависимость в BPP
- Результат: прокси создался корректно
Best Practices
- Конструктор → только присвоение полей
- @PostConstruct → валидация, лёгкая инициализация
- @PreDestroy → очистка ресурсов
- Prototype → сами чистите
- ContextRefreshedEvent → когда всё готово
- BPP dependencies → @Lazy
Резюме для Senior
- 12 этапов → от конструктора до destroy
- Proxy создаётся на этапе 8 (после @PostConstruct)
- Prototype → нет этапов 10-12
- BPP ordering → важно через @Order
- ContextRefreshedEvent → все бины готовы
- Ранняя инициализация → может сломать прокси
🎯 Шпаргалка для интервью
Обязательно знать:
- 12 этапов: Instantiation → Populate Properties → Aware Interfaces → BPP Before → @PostConstruct → afterPropertiesSet → init-method → BPP After (Proxy!) → Ready → @PreDestroy → DisposableBean → destroy-method
- Aware-интерфейсы:
BeanNameAware,BeanFactoryAware,ApplicationContextAware— бин получает информацию от контейнера - Proxy создаётся на этапе 8 —
BPP.postProcessAfterInitialization(), после@PostConstruct - Singleton проходит все 12 этапов, Prototype — только 1-9 (нет destroy)
ContextRefreshedEvent— публикуется когда ВСЕ бины и прокси созданы, можно вызывать@Transactionalметоды- Ранняя инициализация BPP: если BPP зависит от бина, тот создаётся раньше остальных BPP и может не получить прокси
- BPP ordering через
@Order— порядок важен, каждый BPP может заменить бин
Частые уточняющие вопросы:
- В чём разница между Instantiation и Initialization? Instantiation =
newчерез рефлексию (объект существует). Initialization = DI,@PostConstruct, Proxy creation. - Какие Aware-интерфейсы вы знаете?
BeanNameAware(имя бина),BeanFactoryAware(фабрика),ApplicationContextAware(контекст). - Почему кастомный BPP может не получить прокси? Если бин создаётся раньше BPP, создающего прокси — бин остаётся «голым».
- Как управлять порядком BPP? Аннотация
@Order— меньшее значение = раньше выполнение.
Красные флаги (НЕ говорить):
- «В конструкторе все зависимости уже внедрены» (кроме Constructor Injection — зависимости ещё NULL)
- «Prototype бины вызывают
@PreDestroy» (нет, Spring не отслеживает их уничтожение) - «Порядок BPP не важен» (критически важен — первый BPP может заменить бин для второго)
- «Можно тянуть любые зависимости в BPP» (это вызывает раннюю инициализацию и ломает прокси)
Связанные темы:
- [[6. Что такое Bean Lifecycle]]
- [[8. Что такое BeanPostProcessor]]
- [[9. Что делают методы с аннотацией @PostConstruct и @PreDestroy]]
- [[4. Что такое Bean в Spring]]
- [[10. Что такое scope Bean]]