Получить доступ

Контейнер: события, FactoryBean и тонкое связывание

События приложения и @EventListener, FactoryBean, Aware-интерфейсы, порядок бинов (@Order/@DependsOn) и отложенное получение (ObjectProvider/@Lazy/@Lookup).

События приложения

ApplicationContext умеет публиковать события: ApplicationEventPublisher.publishEvent(event) рассылает их, а методы с @EventListener их принимают (в Spring Boot событием может быть любой объект, не обязательно наследник ApplicationEvent). Это способ развязать модули ВНУТРИ одного приложения — издатель не знает о подписчиках.

Важные нюансы: по умолчанию слушатели СИНХРОННЫ — выполняются в том же потоке и той же транзакции, что и publishEvent (исключение в слушателе откатит транзакцию издателя). Для асинхронности ставят @Async, а чтобы реагировать только после успешного коммита — @TransactionalEventListener(phase = AFTER_COMMIT). Это не межсервисная шина (для неё — брокеры из уроков про messaging).

publisher.publishEvent(new OrderPaid(orderId));

@TransactionalEventListener(phase = AFTER_COMMIT)
void onPaid(OrderPaid e) { notifications.send(e); } // после commit

FactoryBean

FactoryBean<T> — это бин, чья задача — СОЗДАТЬ другой бин: контейнер вызывает его getObject() и кладёт в контекст продукт, а не сам FactoryBean. Его применяют, когда создание объекта сложное/условное или интегрирует чужую фабрику.

Тонкость доступа: getBean("x") вернёт ПРОДУКТ фабрики, а getBean("&x") — сам объект FactoryBean. Многие интеграции Spring (прокси для репозиториев, клиентов, JNDI) внутри устроены именно как FactoryBean.

Aware-интерфейсы

Реализовав Aware-интерфейс, бин получает ссылку на инфраструктуру контейнера на соответствующей стадии жизненного цикла: BeanNameAware (своё имя), ApplicationContextAware (сам контекст), EnvironmentAware (Environment), BeanFactoryAware и др.

Пользоваться ими стоит осторожно — они жёстко связывают код со Spring и усложняют тестирование. В большинстве случаев нужное получают обычной инъекцией зависимости; Aware оправдан в инфраструктурном коде.

Порядок бинов

Когда важна последовательность, её задают явно. @Order (или интерфейс Ordered) управляет ПОРЯДКОМ в коллекциях бинов: инъекция List<Handler> придёт в этом порядке, как и цепочки фильтров, аспектов, CommandLineRunner/ApplicationRunner.

@DependsOn — про другое: он форсирует порядок СОЗДАНИЯ бинов, когда между ними есть неявная зависимость инициализации, не выраженная через прямую инъекцию (например, бин должен подняться после того, как другой зарегистрировал что-то глобально).

Отложенное и опциональное получение

ObjectProvider<T> — современный способ получать зависимость лениво, опционально или во множественном числе: provider.getObject() (каждый раз новый prototype), getIfAvailable() (может отсутствовать), orderedStream() (все реализации по порядку). Это аккуратная замена устаревшему @Lookup и решение проблемы «prototype внутри singleton» (см. урок про бины).

@Lazy откладывает создание бина до первого обращения (и иногда помогает разорвать цикл зависимостей), а @Lookup — легаси-приём method injection. По возможности предпочитают ObjectProvider как самый явный и тестируемый вариант.

Как отвечать на собеседовании

Объясните события (@EventListener для развязки модулей; СИНХРОННЫ и в транзакции издателя по умолчанию → @Async/@TransactionalEventListener AFTER_COMMIT). Расскажите FactoryBean (создаёт другой бин, getBean("&x") — сам фабрика) и Aware-интерфейсы (доступ к контейнеру, но осторожно). Различите @Order (порядок в коллекциях/цепочках) и @DependsOn (порядок создания), и назовите ObjectProvider как современный способ ленивого/опционального/prototype-получения вместо @Lookup.

Вопросы по теме

Читайте также

Закрытая бета

Знать тему — половина дела

На живом собеседовании sotto слышит вопрос интервьюера и показывает короткий ответ и план. Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем