Какие способы инициализации бинов в Spring существуют?
Что проверяют
Знаете ли вы способы объявления бина и точки, где можно вмешаться после его создания.
Как отвечать
Разделите два вопроса: как бин объявляется и что происходит после его создания.
Объявление:
- аннотации стереотипов (
@Component,@Service,@Repository,@Controller) плюс сканирование пакетов; @Beanв классе с@Configuration: единственный вариант для чужих классов, которые нельзя пометить аннотацией, и для случая, когда объект надо собрать руками;- XML: наследие, но встречается в старых проектах;
- программная регистрация через
BeanDefinitionRegistryилиregisterBean, когда набор бинов зависит от конфигурации; - автоконфигурация Spring Boot: классы, перечисленные в
AutoConfiguration.imports, подключаются по условиям вроде@ConditionalOnMissingBean.
После создания, по порядку выполнения:
- конструктор и внедрение зависимостей;
- интерфейсы осведомлённости (
BeanNameAware,ApplicationContextAware); BeanPostProcessor.postProcessBeforeInitialization, внутри которого срабатывает@PostConstruct;InitializingBean.afterPropertiesSet;initMethod, указанный в@Bean;BeanPostProcessor.postProcessAfterInitialization, где создаются прокси для@Transactionalи подобного.
Из трёх способов дописать логику инициализации предпочтителен @PostConstruct: он не привязывает класс к интерфейсам Spring, в отличие от InitializingBean.
Что стоит упомянуть дополнительно: @Lazy откладывает создание до первого обращения, @Scope задаёт область жизни, @DependsOn навязывает порядок, а @Order влияет на порядок в списке одинаковых бинов, но не на порядок создания.
Важная оговорка: тяжёлую работу в @PostConstruct делать не стоит. Она удлиняет старт приложения, а исключение оттуда роняет весь контекст.
Где спотыкаются
Не знают, что @PostConstruct выполняется внутри постпроцессора.
Считают, что бин с @Lazy создаётся при старте.
Обращаются в конструкторе к тому, что ещё не внедрено.
Ходят по сети в инициализации и получают приложение, которое не стартует без внешнего сервиса.