Ответ
Непосредственно перед созданием BeanDefinition на этапе обработки аннотаций (во время компиляции или загрузки класса) Spring работает с AnnotationMetadata.
Процесс и ключевые артефакты:
- Чтение класса: Spring (через
ClassPathScanningCandidateComponentProvider) считывает байт-код класса. - Получение метаданных: Для класса извлекается
ClassMetadata(информация о классе) и, что важнее,AnnotationMetadata.AnnotationMetadataсодержит информацию обо всех аннотациях класса (например,@Component,@Service,@Scope,@Lazy).
- Анализ аннотаций: На основе
AnnotationMetadataSpring определяет, является ли класс кандидатом на создание бина, его scope, lazy-инициализацию и другие атрибуты. - Создание BeanDefinition: Только после этого анализа создается объект
BeanDefinition(например,ScannedGenericBeanDefinition), который инкапсулирует эти метаданные для последующего использования IoC-контейнером при создании экземпляров бинов.
Наглядный пример: Для класса:
@Service
@Lazy
@Scope("prototype")
public class OrderService {
// ...
}
Во время обработки Spring сначала получит AnnotationMetadata, содержащее данные об аннотациях @Service, @Lazy и @Scope. На основе этих данных будет построен BeanDefinition, у которого будут установлены флаги: lazyInit = true, scope = "prototype".
Техническая деталь: В современных Spring-приложениях (особенно с Spring Boot) эта обработка часто происходит не во время компиляции javac, а во время загрузки классов JVM или с помощью пост-процессоров (как ConfigurationClassPostProcessor), которые читают аннотации и регистрируют BeanDefinition в BeanFactory. Ключевой ввод — это именно метаданные аннотаций (AnnotationMetadata).
Ответ 18+ 🔞
Слушай, давай разберем эту дичь, как она на самом деле работает, а то в документации такое наворочено, что мозг скукоживается.
Вот представь себе: Spring — это такой дотошный таможенник на границе твоего приложения. И пока ты, довольный, пишешь @Service над своим классом OrderService, он уже начинает свою подлую работу.
Как это происходит, по-человечески:
-
Всё начинается с обыска. Не с создания бина, нет! Сначала контейнер, через своих шпионов (типа
ClassPathScanningCandidateComponentProvider), выкатывает сканер и начинает рыться в байт-коде твоих классов. Читает, сука, как книжку. -
Выуживает компромат. И главное, что его интересует — это
AnnotationMetadata. Это, блядь, такая папочка с досье, где собрано всё: где ты@Componentповесил, где@Serviceприлепил, не@Lazyли ты случаем, и в каком@Scopeтусуешься —singletonилиprototype. -
Принимает решение. Только ПОСЛЕ того, как он изучил это досье (
AnnotationMetadata), он решает: "Ага, этот чувак — кандидат в бины". И вот тут-то, наконец, рождаетсяBeanDefinition— уже как официальная инструкция для фабрики: "Создай объект, делай его ленивым (lazyInit=true), и пусть у каждого будет своя отдельная копия (scope="prototype")". -
Техническая подстава. Важный момент, чтобы не обосраться: вся эта магия с чтением аннотаций происходит не когда ты компилируешь код (это было бы слишком просто), а позже — во время загрузки классов JVM или благодаря специальным засланным казачкам — пост-процессорам (вроде
ConfigurationClassPostProcessor). Они тихой сапой пробираются, читают твои@Serviceи@Lazy, и шепчут на ухоBeanFactory, кого и как регистрировать.
Простой пример, чтобы вообще всё стало ясно:
Вот твой класс, красавец:
@Service
@Lazy
@Scope("prototype")
public class OrderService {
// ...
}
Что делает Spring?
- Видит класс. Вытаскивает из него
AnnotationMetadata— пачку аннотаций. - Читает: "Ага,
@Service— значит, бин.@Lazy— значит, не создавай, пока не прижмёт.@Scope("prototype")— значит, на каждый запрос новый экземпляр, а не один на всех, как жмот". - И только потом формирует
BeanDefinitionс этими настройками. Эта штука — уже не аннотации, а конкретный план действий для контейнера.
Короче, запомни: сначала AnnotationMetadata (досье), потом BeanDefinition (приказ). Без этого досье Spring — как слепой котёнок, нихуя не понимает, что делать с твоим классом.