🔍 BeanDefinition Pipeline
Spring ApplicationContext при старте проходит через чётко определённую последовательность фаз. Понимание этой последовательности объясняет, почему одни расширения работают, а другие нет, и почему порядок объявления бинов иногда имеет значение.
Фазы загрузки контекста
1️⃣ Загрузка BeanDefinition'ов
Контекст читает конфигурацию и строит реестр BeanDefinition. На этом этапе объекты ещё не создаются. BeanDefinition — это метаданные: класс, scope, зависимости, lazy/eager, init/destroy методы.
2️⃣ BeanFactoryPostProcessor
После того как все BeanDefinition загружены, но до создания каких-либо бинов, вызываются BeanFactoryPostProcessor. Они получают доступ к ConfigurableListableBeanFactory и могут модифицировать, добавлять или удалять BeanDefinition.
Самый известный пример — PropertySourcesPlaceholderConfigurer. Он резолвит ${...} плейсхолдеры в BeanDefinition прямо на этом этапе. Именно поэтому @Value("${some.property}") работает — к моменту создания бина значение уже подставлено в метаданные.
ConfigurationClassPostProcessor — ещё один критический BeanFactoryPostProcessor. Он обрабатывает @Configuration классы, @ComponentScan, @Import, регистрирует дополнительные BeanDefinition. Без него Spring Boot не работал бы.
⚠️ BeanFactoryPostProcessor сам является бином, но Spring создаёт его раньше остальных отдельным путём. Если BeanFactoryPostProcessor зависит от другого бина через @Autowired — этот бин будет создан раньше срока, до фазы BeanPostProcessor. Это ведёт к тому что аспекты и другие пост-процессоры не применяются к нему. Spring даже выводит предупреждение в лог в таких случаях.
3️⃣ Создание экземпляра и инициализация
Для каждого eager singleton:
— Создание инстанса через конструктор или фабричный метод.
— Внедрение зависимостей (setter injection, field injection).
— Вызов BeanPostProcessor.postProcessBeforeInitialization.
— Вызов init-метода (@PostConstruct → InitializingBean.afterPropertiesSet() → initMethod)
— Вызов BeanPostProcessor.postProcessAfterInitialization.
BeanPostProcessor — именно здесь работает Spring AOP. AbstractAutoProxyCreator реализует BeanPostProcessor и в postProcessAfterInitialization оборачивает бин в прокси если нужно.
4️⃣ SmartInitializingSingleton
После инициализации всех singleton-бинов Spring вызывает afterSingletonsInstantiated() на каждом бине, реализующем этот интерфейс. Полезно, когда нужно выполнить логику гарантированно после того, как все синглтоны готовы — в отличие от @PostConstruct, который срабатывает до завершения инициализации остальных бинов.
5️⃣ Публикация события ContextRefreshedEvent
После того как все бины готовы. ApplicationListener на этот ивент — стандартный способ выполнить логику после полного старта контекста.
🔹 Порядок BeanPostProcessor'ов
Порядок определяется через Ordered или PriorityOrdered. PriorityOrdered применяется раньше Ordered, Ordered раньше остальных. Внутри одного уровня по getOrder().
Это важно когда несколько BeanPostProcessor обрабатывают один бин: AOP-прокси, валидация, кастомный мониторинг. Неправильный порядок может привести к тому что один пост-процессор получит прокси вместо оригинального объекта или наоборот.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#CoreJava
Post #7486
2.84K
- 👍 14
- 🔥 4
- 🤔 3
- 🙏 2