spring events на практике

Руководство по Spring. Обработка событий.

Spring Event Handling
В предыдущих статьях мы уже видели, что одним из ключевых элементов Spring является ApplicationContext, который управляет жизненным циклом бинов. В процессе своей работы он вызывает целый ряд, так называемых, событий (ContextStoppedEvent, ContextStartedEvent и т.д.)
Обработка этих событий обеспечивается классов ApplicationEvent и интерфейсом ApplicationListener. И когда бин иплементирует интерфейс ApplicationListener, то каждый раз, когда вызывается то или иное событие, бин получает об этом информацию.

Существует целый ряд стандартных событий в Spring Framework:

ContextStartedEvent
Это событие публикуется, когда ApplicationContext запущен через метод start() интерфейса ConfigurableApplicationContext. После получения этого события мы можем выполнить необходимые нам действия (например, записать то-то в базу данных и т.д.).

ContextRefreshedEvent
Это событие публикуется когда ApplicationContext обновлён или инициализирован. Оно может быть вызвано использованием метода refresh() интерфейса ConfigurableApplicationContext.

ContextStoppedEvent
Это событие публикуется, когда ApplicationContext остановлен методом stop() интерфейса ConfigurableApplicationContext. Мы также можем дать команду выполнить определённую работу после получения этого события.

ContextClosedEvent
Публикуется, когда ApplicationContext закрыт методом close() интерфейса ConfigurableApplicationContext. Закрытие контекста – это конец файла. После этого он не может быть перезапущен или обновлен.

RequestHandledEvent
Это специальное событие, которое информирует нас о том, что все бины HTTP-запроса были обслужены (ориентирован на веб).

Обработка событий в Spring однопоточна, а значит, если событие опубликовано, то все процессы будут блокированы, пока все адресаты не получат сообщение. Другими словами, необходимо быть крайне внимательными, когда вы планируете использовать обработку событий в вашем приложении.

Для более глубокого понимания, как это устроено и для чего используется, ниже приведён пример простого приложения.

Пример приложения:
Исходный код проекта можно скачать по ЭТОЙ ССЫЛКЕ.

spring events на практике

Конфигурационный файл message-event-handling-config.xml

Результат работы программы

spring events на практике

В данной статье мы ознакомились с основами обработки событий с Spring Framework.

Источник

Spring Blog

Better application events in Spring Framework 4.2

Application events are available since the very beginning of the Spring framework as a mean for loosely coupled components to exchange information. One of the most well known usage of application events is the following:

This allows MyListener to be notified when the context has refreshed and one can use that to run arbitrary code when the application context has fully started.

In Spring Framework 4.2 we have revisited the event infrastructure in three main areas that I am going to explain in this post.

Generics support

It is now possible to define your ApplicationListener implementation with nested generics information in the event type, something like:

When dispatching an event, the signature of your listener is used to determine if it matches said incoming event.

Annotation-driven event listener

The biggest new feature is the support of annotation-driven event listeners, similar to our recent work on JMS and AMQP endpoints in Spring Framework 4.1. In a nutshell, it is now possible to simply annotate a method of a managed-bean with @EventListener to automatically register an ApplicationListener matching the signature of the method. Our example above can be rewritten as follows:

@EventListener is a core annotation that is handled transparently in a similar fashion as @Autowired and others: no extra configuration is necessary with java config and the existing element enables full support for it.

The method signature defines the event type that you’re interested in. It is also possible to define a SpEL expression that should match in order to handle the event. For instance, consider the following event:

The following example showcases an event listener that will only be invoked for an awesome CreationEvent of Order (i.e. if the awesome flag is true ):

As you can see from the sample above, method arguments are exposed via their names if such information can be discovered. The condition expression also exposes a “root” variable with the raw ApplicationEvent ( #root.event ) and the actual method arguments ( #root.args ).

Publishing events

The following sample shows how you can use ApplicationEventPublisher to send an OrderCreatedEvent :

Transaction bound events

Another popular improvement is the ability to bind the listener of an event to a phase of the transaction. The typical example is to handle the event when the transaction has completed successfully: this allows events to be used with more flexibility when the outcome of the current transaction actually matters to the listener.

Spring Framework is currently structured in such a way that the context is not aware of the transaction support and we obviously didn’t want to deviate from that very sane principle so we built an open infrastructure to allow additional components to be registered and influence the way event listeners are created.

The transaction module implements an EventListenerFactory that looks for the new @TransactionalEventListener annotation. When this one is present, an extended event listener that is aware of the transaction is registered instead of the default.

Let’s reuse our example above and rewrite it in such a way that the order creation event will only be processed when the transaction in which the producer is running has completed successfully:

By default, if no transaction is running the event isn’t sent at all as we can’t obviously honor the requested phase, but there is a fallbackExecution attribute in @TransactionalEventListener that tells Spring to invoke the listener immediately if there is no transaction.

Try it out!

If you want to give this a try before the first milestone release of 4.2, grab a nightly SNAPSHOT build via our snapshot repository. You can also create a sample project using start.spring.io using the latest Spring Boot snapshot build, or if you’re super lazy you can copy/paste this in your shell:

And update the project to use Spring Framework 4.2.0.BUILD-SNAPSHOT

As always, we welcome community feedback, please try these features and let us know if you run into any issue.

Источник

Запись событий Spring при тестировании приложений Spring Boot

Настройка для записи ApplicationEvent с помощью Spring Boot

Обязательно используйте версию Spring Boot >= 2.4.2, так как нам нужна версия Spring Framework >= 5.3.3.

Следовательно, она не работает для модульного теста, где не используется поддержка инфраструктуры Spring TestContext. Есть несколько аннотаций тестовых срезов Spring Boot, которые удобно загружают контекст для нашего теста.

Введение в публикацию событий Spring

В качестве примера мы протестируем класс Java, который выдает UserCreationEvent, когда мы успешно создаем нового пользователя. Событие включает метаданные о пользователе, актуальные для последующих задач:

Начиная со Spring Framework 4.2, нам не нужно расширять абстрактный класс ApplicationEvent и мы можем использовать любой POJO в качестве нашего класса событий. В следующий статье привелено отличное введение в события приложений с помощью Spring Boot.

Наш UserService создает и хранит наших новых пользователей. Мы можем создать как одного пользователя, так и группу пользователей:

Например, наше приложение выполняет две дополнительные операции всякий раз, когда мы запускаем такое UserCreationEvent :

Запись и проверка событий приложения с помощью Spring Boot

Давайте напишем наш первый тест, который гарантирует, что UserService генерирует событие всякий раз, когда мы создаем нового пользователя. Мы инструктируем Spring фиксировать наши события с помощью @RecordApplicationEvents аннотации поверх нашего тестового класса:

Несмотря на то, что мы генерируем только одно событие из нашего приложения, Spring захватывает четыре события для теста выше. Остальные три события относятся к Spring, как и PrepareInstanceEvent в среде TestContext.

Поскольку мы используем JUnit Jupiter и SpringExtension (зарегистрированный для нас при использовании @SpringBootTest ), мы также можем внедрить bean-компонент ApplicationEvents в метод жизненного цикла JUnit или непосредственно в тест:

Экземпляр ApplicationEvents создается до и удаляется после каждого теста как часть текущего потока. Следовательно, вы даже можете использовать внедрение поля и @TestInstance(TestInstance.Lifecycle.PER_CLASS) делить тестовый экземпляр между несколькими тестами ( PER_METHOD по умолчанию).

… Или используйте альтернативный подход к тестированию.

Альтернативы тестированию событий Spring

В зависимости от того, чего вы хотите достичь с помощью теста, может быть достаточно проверить эту функциональность с помощью модульного теста:

Обратите внимание, что здесь мы не используем никакой поддержки Spring Test и полагаемся исключительно на Mockito и JUnit Jupiter.

Другой подход заключается в том, чтобы не проверять события публикации явно, а проверять весь сценарий использования с помощью интеграционного теста:

В этом случае нам нужно будет проверить результат работы наших слушателей событий и, например, проверить, что мы помещаем сообщение в очередь или увеличиваем счетчик.

Резюме тестирования событий Spring с помощью Spring Boot

Все различные подходы сводятся к тестированию поведения и состояния. Благодаря новой функции @RecordApplicationEvents в Spring Test у нас может возникнуть соблазн провести больше поведенческих тестов и проверить внутреннюю часть нашей реализации. В общем, мы должны сосредоточиться на тестировании состояния (также известном как результат), поскольку оно поддерживает беспроблемный рефакторинг.

Представьте себе следующее: мы используем, ApplicationEvent чтобы разделять части нашего приложения и гарантировать, что это событие запускается во время теста. Через две недели мы решаем убрать / переработать эту развязку (по каким-то причинам). Наш вариант использования может по-прежнему работать, как ожидалось, но наш тест теперь не проходит, потому что мы делаем предположения о технической реализации, проверяя, сколько событий мы опубликовали.

Помните об этом и не перегружайте свои тесты деталями реализации (если вы хотите провести рефакторинг в будущем :). Тем не менее, есть определенные тестовые сценарии, когда функция @RecordApplicationEvents очень помогает.

Исходный код со всеми альтернативными вариантами для тестирования Spring Event с помощью Spring Boot доступен на GitHub.

Источник

Java Blog

Spring Boot: события приложения и слушатели

В дополнение к обычным событиям Spring Framework, таким как ContextRefreshedEvent, SpringApplication отправляет некоторые дополнительные события приложения.

Некоторые события на самом деле инициируются до создания ApplicationContext, поэтому вы не можете зарегистрировать прослушиватель (listener) для них как @Bean. Вы можете зарегистрировать их с помощью метода SpringApplication.addListeners(…) или метода SpringApplicationBuilder.listeners(…).

Если вы хотите, чтобы эти слушатели регистрировались автоматически, независимо от способа создания приложения, вы можете добавить файл META-INF/spring.factories в свой проект и ссылаться на своих слушателей с помощью org.springframework.context. Ключ ApplicationListener, как показано в следующем примере:

События приложения отправляются в следующем порядке по мере запуска приложения:

Приведенный выше список включает только SpringApplicationEvents, которые связаны с SpringApplication. В дополнение к этому следующие события также публикуются после ApplicationPreparedEvent и перед ApplicationStartedEvent:

Обычно вам не нужно использовать события приложения, но может быть полезно знать, что они существуют. Внутри Spring Boot использует события для решения самых разных задач.

События приложения отправляются с использованием механизма публикации событий Spring Framework. Часть этого механизма гарантирует, что событие, опубликованное для слушателей в дочернем контексте, также будет опубликовано для слушателей в любых контекстах предков. В результате этого, если ваше приложение использует иерархию экземпляров SpringApplication, слушатель может получить несколько экземпляров одного и того же типа события приложения.

Чтобы позволить вашему слушателю различать событие для его контекста и событие для контекста-потомка, он должен запросить, чтобы его контекст приложения был внедрен, а затем сравнить введенный контекст с контекстом события. Контекст может быть введен путем реализации ApplicationContextAware или, если слушатель является компонентом (bean), с помощью @Autowired.

Источник

Управляемые аннотациями слушатели событий в Spring 4.2

Вступление

Обмен событиями в приложении стал неотъемлемой частью многих приложений, и, к счастью, Spring предоставляет полную инфраструктуру для переходных событий (*). Недавний рефакторинг событий, связанных с транзакциями, дал мне повод проверить на практике новые обработчики событий на основе аннотаций, представленные в Spring 4.2. Посмотрим, что можно получить.

(*) — для постоянных событий в Spring-приложении Duramen может быть решением, которое стоит посмотреть

По старому

Это работает нормально, но для каждого события должен быть создан новый класс, который генерирует шаблонный код.

Кроме того, наше событие должно расширить класс ApplicationEvent — базовый класс для всех событий приложения в Spring.

Обратите внимание, что использование доменных объектов в событиях имеет существенный недостаток и не является лучшей идеей во многих ситуациях. Псевдодоменные объекты в примерах кода использовались, чтобы не вносить ненужную сложность.

Кстати, ExternalNotificationSender в этом примере является экземпляром класса, который отправляет внешние уведомления зарегистрированным пользователям (например, по электронной почте, SMS или Slack).

Приемник событий, управляемый аннотациями

Под капотом Spring создаст экземпляр ApplicationListener для события с типом, взятым из аргумента метода. Количество аннотированных методов в одном классе не ограничено — все связанные обработчики событий могут быть сгруппированы в один класс.

Условная обработка событий

Обратите внимание, что в реальном приложении может быть иерархия событий, связанных с блогом.
Также обратите внимание, что в Groovy этот класс будет намного проще.

Для генерации события только для важных изменений можно использовать параметр condition :

Расслабленная иерархия типов событий

Исторически ApplicationEventPublisher имел возможность только публиковать объекты, унаследованные после ApplicationEvent. Начиная с Spring 4.2, интерфейс был расширен для поддержки любых типов объектов. В этом случае объект оборачивается в PayloadApplicationEvent и отправляется через.

Это изменение делает публикацию событий еще проще. Однако, с другой стороны, без внутренней добросовестности (например, с интерфейсом маркера для всех событий нашего домена) это может усложнить отслеживание событий, особенно в больших приложениях.

Публикация событий в ответ на

Еще одна приятная вещь с @EventListener — это тот факт, что в случае не возвращаемого значения тип возврата Spring автоматически публикует возвращаемое событие.

Асинхронная обработка событий

Обновлено Как справедливо предположил Радек Грэбски, также стоит упомянуть, что @EventListener можно легко комбинировать с аннотацией @Async для обеспечения асинхронной обработки событий. Код в конкретном слушателе событий не блокирует ни выполнение основного кода, ни обработку другими слушателями.

Резюме

Слушатели событий, управляемые аннотациями, представленные в Spring 4.2, продолжают тенденцию к сокращению стандартного кода в приложениях, основанных на Spring (Boot). Новый подход выглядит интересным, особенно для небольших приложений с небольшим количеством событий, где затраты на обслуживание ниже. В мире вездесущей Весенней (Загрузочной) магии стоит вспомнить, что с большой силой приходит большая ответственность.

В следующей записи блога я напишу, как новый механизм может также использоваться для упрощения обработки событий, связанных с транзакциями.

Обратите внимание, что Spring Framework 4.2 является зависимостью по умолчанию Spring Boot 1.3 (на момент написания 1.3.0.M5 доступен). В качестве альтернативы можно вручную обновить версию Spring Framework в Gradle / Maven для Spring Boot 1.2.5 — она ​​должна работать в большинстве случаев.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *