From 0b84f137ccec2a31ced652d5edb1f1565c9670c0 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 18 May 2015 16:47:48 +0200 Subject: [PATCH] Document application event improvements Issue: SPR-12702 --- src/asciidoc/core-beans.adoc | 107 +++++++++++++++++++++++++++++++++- src/asciidoc/data-access.adoc | 38 ++++++++++++ 2 files changed, 142 insertions(+), 3 deletions(-) diff --git a/src/asciidoc/core-beans.adoc b/src/asciidoc/core-beans.adoc index fe4d7f0a11..bb66dba84e 100644 --- a/src/asciidoc/core-beans.adoc +++ b/src/asciidoc/core-beans.adoc @@ -7351,7 +7351,7 @@ package also provides the following functionality: * __Access to messages in i18n-style__, through the `MessageSource` interface. * __Access to resources__, such as URLs and files, through the `ResourceLoader` interface. -* __Event publication__ to beans implementing the `ApplicationListener` interface, +* __Event publication__ to namely beans implementing the `ApplicationListener` interface, through the use of the `ApplicationEventPublisher` interface. * __Loading of multiple (hierarchical) contexts__, allowing each to be focused on one particular layer, such as the web layer of an application, through the @@ -7571,8 +7571,18 @@ Event handling in the `ApplicationContext` is provided through the `ApplicationE class and `ApplicationListener` interface. If a bean that implements the `ApplicationListener` interface is deployed into the context, every time an `ApplicationEvent` gets published to the `ApplicationContext`, that bean is notified. -Essentially, this is the standard __Observer__ design pattern. Spring provides the -following standard events: +Essentially, this is the standard __Observer__ design pattern. + +[TIP] +==== +As of Spring 4.2, the event infrastructure has been significantly improved and offer +an <> as well as the +ability to publish any arbitrary event, that is an object that does not necessarily +extend from `ApplicationEvent`. When such an object is published we wrap it in a +`PayloadApplicationEvent` for you. +==== + +Spring provides the following standard events: [[beans-ctx-events-tbl]] .Built-in Events @@ -7747,6 +7757,97 @@ http://www.enterpriseintegrationpatterns.com[pattern-oriented], event-driven architectures that build upon the well-known Spring programming model. ==== +[[context-functionality-events-annotation]] +==== Annotation-based Event Listeners + +As of Spring 4.2, an event listener can be registered on any public method of a managed +bean via the `EventListener` annotation. The `BlackListNotifier` can be rewritten as +follows: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + public class BlackListNotifier { + + private String notificationAddress; + + public void setNotificationAddress(String notificationAddress) { + this.notificationAddress = notificationAddress; + } + + @EventListener + public void processBlackListEvent(BlackListEvent event) { + // notify appropriate parties via notificationAddress... + } + + } +---- + +As you can see above, the method signature actually _infer_ which even type it listens to. This +also works for nested generics as long as the actual event resolves the generics parameter you +would filter on. + +It is also possible to add additional runtime filtering via the `condition` attribute of the +annotation that defines a <> that should match to actually invoke +the method for a particular event. + +For instance, our notifier can be rewritten to be only invoked if the `test` attribute of the +event is equal to `foo`: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @EventListener(condition = "#event.test == 'foo'") + public void processBlackListEvent(BlackListEvent event) { + // notify appropriate parties via notificationAddress... + } +---- + +Each `SpEL` expression evaluates again a dedicated context. The next table lists the items made +available to the context so one can use them for conditional event processing: + +[[context-functionality-events-annotation-tbl]] +.Event SpEL available metadata +|=== +| Name| Location| Description| Example + +| event +| root object +| The actual `ApplicationEvent` +| `#root.event` + +| args +| root object +| The arguments (as array) used for invoking the target +| `#root.args[0]` + +| __argument name__ +| evaluation context +| Name of any of the method argument. If for some reason the names are not available + (ex: no debug information), the argument names are also available under the `a<#arg>` + where __#arg__ stands for the argument index (starting from 0). +| `iban` or `a0` (one can also use `p0` or `p<#arg>` notation as an alias). +|=== + +Note that `#root.event` allows you to access to the underlying event, even if your method +signature actually refers to an arbitrary object that was published. + +If you need to publish an event as the result of processing another, just change the +method signature to return the event that should be published, something like: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @EventListener + public ListUpdateEvent handleBlackListEvent(BlackListEvent event) { + // notify appropriate parties via notificationAddress and + // then publish a ListUpdateEvent... + } +---- + +This new method will publish a new `ListUpdateEvent` for every `BlackListEvent` handled +by the method above. If you need to publish several events, just return a `Collection` of +events instead. [[context-functionality-resources]] diff --git a/src/asciidoc/data-access.adoc b/src/asciidoc/data-access.adoc index 575deb9327..5bf6459eff 100644 --- a/src/asciidoc/data-access.adoc +++ b/src/asciidoc/data-access.adoc @@ -54,6 +54,8 @@ server integration, and solutions to common problems.) declarative transaction management. * <> covers support for programmatic (that is, explicitly coded) transaction management. +* <> describes how you could use application + events within a transaction. @@ -1924,8 +1926,44 @@ management out of business logic, and is not difficult to configure. When using Spring Framework, rather than EJB CMT, the configuration cost of declarative transaction management is greatly reduced. +[[transaction-event]] +=== Transaction bound event +As of Spring 4.2, the listener of an event can be bound 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. +Registering a regular event listener is done via the `@EventListener` annotation. If you need +to bind it to the transaction use `@TransactionalEventListener`. When you do so, the listener +will be bound to the commit phase of the transaction by default. + +Let's take an example to illustrate this concept. Assume that a component publish an order +created event and we want to define a listener that should only handle that event once the +transaction in which it has been published as committed successfully: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @Component + public class MyComponent { + + @TransactionalEventListener + public void handleOrderCreatedEvent(CreationEvent creationEvent) { + ... + } + + } +---- + +The `TransactionalEventListener` annotation exposes a `phase` attribute that allows to customize +to which phase of the transaction the listener should be bound to. The valid phases are `BEFORE_COMMIT`, +`AFTER_COMMIT` (default), `AFTER_ROLLBACK` and `AFTER_COMPLETION` that aggregates the transaction +completion (be it a commit or a rollback). + +If no transaction is running, the listener is not invoked at all since we can't honor the required +semantics. It is however possible to override that behaviour by setting the `fallbackExecution` attribute +of the annotation to `true`. [[transaction-application-server-integration]] === Application server-specific integration