Что происходит при инициализации Spring контекста, как он сканирует классы, собирает бины и внедряет зависимости
Что проверяют
Знаете ли вы Spring глубже аннотаций: из каких фаз состоит refresh() и в какой момент бин подменяется прокси. От этого зависят реальные баги с @Transactional, циклами зависимостей и @PostConstruct. Следом обычно спрашивают, почему транзакция не работает при вызове метода изнутри того же класса.
Как отвечать
В Spring Boot SpringApplication.run() создаёт контекст и вызывает AbstractApplicationContext.refresh(). Дальше по фазам:
- сбор определений:
ConfigurationClassPostProcessorразбирает@Configuration,@ComponentScan,@Import,@Bean. Сканер читает метаданные классов через ASM, не загружая их, и отбирает помеченные@Componentи производными. На выходе не объекты, аBeanDefinition: класс, scope, зависимости; - автоконфигурация: Boot берёт список из
META-INF/spring/...AutoConfiguration.importsи применяет классы по условиям@ConditionalOnClass,@ConditionalOnMissingBean; BeanFactoryPostProcessor: правит определения до создания объектов, например подставляет${...};- создание синглтонов: для каждого не-lazy бина вызов конструктора с разрешением аргументов, внедрение в поля и сеттеры через
AutowiredAnnotationBeanPostProcessor,@PostConstruct,afterPropertiesSet(), последнимpostProcessAfterInitialization. ЗдесьBeanPostProcessorможет вернуть вместо бина прокси, так работают@Transactional,@Async,@Cacheable; - завершение:
ContextRefreshedEvent, старт веб-сервера, затемApplicationReadyEvent.
Зависимость ищется по типу. Если кандидатов несколько, решают @Qualifier, @Primary, затем имя параметра.
Где спотыкаются
Говорят, что при сканировании Spring грузит все классы рефлексией. Байткод читается через ASM, рефлексия нужна уже при создании бина.
Вызывают @Transactional-метод из соседнего метода того же бина. Вызов идёт через this, мимо прокси, и транзакции нет.
Не знают, что с Boot 2.6 циклические зависимости запрещены по умолчанию (spring.main.allow-circular-references), а цикл через конструкторы не разрешается вовсе и падает с BeanCurrentlyInCreationException.