Skip to content

Commit 0b84f13

Browse files
committed
Document application event improvements
Issue: SPR-12702
1 parent e6f99ff commit 0b84f13

File tree

2 files changed

+142
-3
lines changed

2 files changed

+142
-3
lines changed

src/asciidoc/core-beans.adoc

Lines changed: 104 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7351,7 +7351,7 @@ package also provides the following functionality:
73517351

73527352
* __Access to messages in i18n-style__, through the `MessageSource` interface.
73537353
* __Access to resources__, such as URLs and files, through the `ResourceLoader` interface.
7354-
* __Event publication__ to beans implementing the `ApplicationListener` interface,
7354+
* __Event publication__ to namely beans implementing the `ApplicationListener` interface,
73557355
through the use of the `ApplicationEventPublisher` interface.
73567356
* __Loading of multiple (hierarchical) contexts__, allowing each to be focused on one
73577357
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
75717571
class and `ApplicationListener` interface. If a bean that implements the
75727572
`ApplicationListener` interface is deployed into the context, every time an
75737573
`ApplicationEvent` gets published to the `ApplicationContext`, that bean is notified.
7574-
Essentially, this is the standard __Observer__ design pattern. Spring provides the
7575-
following standard events:
7574+
Essentially, this is the standard __Observer__ design pattern.
7575+
7576+
[TIP]
7577+
====
7578+
As of Spring 4.2, the event infrastructure has been significantly improved and offer
7579+
an <<context-functionality-events-annotation,annotation-based model>> as well as the
7580+
ability to publish any arbitrary event, that is an object that does not necessarily
7581+
extend from `ApplicationEvent`. When such an object is published we wrap it in a
7582+
`PayloadApplicationEvent` for you.
7583+
====
7584+
7585+
Spring provides the following standard events:
75767586

75777587
[[beans-ctx-events-tbl]]
75787588
.Built-in Events
@@ -7747,6 +7757,97 @@ http://www.enterpriseintegrationpatterns.com[pattern-oriented], event-driven
77477757
architectures that build upon the well-known Spring programming model.
77487758
====
77497759

7760+
[[context-functionality-events-annotation]]
7761+
==== Annotation-based Event Listeners
7762+
7763+
As of Spring 4.2, an event listener can be registered on any public method of a managed
7764+
bean via the `EventListener` annotation. The `BlackListNotifier` can be rewritten as
7765+
follows:
7766+
7767+
[source,java,indent=0]
7768+
[subs="verbatim,quotes"]
7769+
----
7770+
public class BlackListNotifier {
7771+
7772+
private String notificationAddress;
7773+
7774+
public void setNotificationAddress(String notificationAddress) {
7775+
this.notificationAddress = notificationAddress;
7776+
}
7777+
7778+
@EventListener
7779+
public void processBlackListEvent(BlackListEvent event) {
7780+
// notify appropriate parties via notificationAddress...
7781+
}
7782+
7783+
}
7784+
----
7785+
7786+
As you can see above, the method signature actually _infer_ which even type it listens to. This
7787+
also works for nested generics as long as the actual event resolves the generics parameter you
7788+
would filter on.
7789+
7790+
It is also possible to add additional runtime filtering via the `condition` attribute of the
7791+
annotation that defines a <<expressions,`SpEL` expression>> that should match to actually invoke
7792+
the method for a particular event.
7793+
7794+
For instance, our notifier can be rewritten to be only invoked if the `test` attribute of the
7795+
event is equal to `foo`:
7796+
7797+
[source,java,indent=0]
7798+
[subs="verbatim,quotes"]
7799+
----
7800+
@EventListener(condition = "#event.test == 'foo'")
7801+
public void processBlackListEvent(BlackListEvent event) {
7802+
// notify appropriate parties via notificationAddress...
7803+
}
7804+
----
7805+
7806+
Each `SpEL` expression evaluates again a dedicated context. The next table lists the items made
7807+
available to the context so one can use them for conditional event processing:
7808+
7809+
[[context-functionality-events-annotation-tbl]]
7810+
.Event SpEL available metadata
7811+
|===
7812+
| Name| Location| Description| Example
7813+
7814+
| event
7815+
| root object
7816+
| The actual `ApplicationEvent`
7817+
| `#root.event`
7818+
7819+
| args
7820+
| root object
7821+
| The arguments (as array) used for invoking the target
7822+
| `#root.args[0]`
7823+
7824+
| __argument name__
7825+
| evaluation context
7826+
| Name of any of the method argument. If for some reason the names are not available
7827+
(ex: no debug information), the argument names are also available under the `a<#arg>`
7828+
where __#arg__ stands for the argument index (starting from 0).
7829+
| `iban` or `a0` (one can also use `p0` or `p<#arg>` notation as an alias).
7830+
|===
7831+
7832+
Note that `#root.event` allows you to access to the underlying event, even if your method
7833+
signature actually refers to an arbitrary object that was published.
7834+
7835+
If you need to publish an event as the result of processing another, just change the
7836+
method signature to return the event that should be published, something like:
7837+
7838+
[source,java,indent=0]
7839+
[subs="verbatim,quotes"]
7840+
----
7841+
@EventListener
7842+
public ListUpdateEvent handleBlackListEvent(BlackListEvent event) {
7843+
// notify appropriate parties via notificationAddress and
7844+
// then publish a ListUpdateEvent...
7845+
}
7846+
----
7847+
7848+
This new method will publish a new `ListUpdateEvent` for every `BlackListEvent` handled
7849+
by the method above. If you need to publish several events, just return a `Collection` of
7850+
events instead.
77507851

77517852

77527853
[[context-functionality-resources]]

src/asciidoc/data-access.adoc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ server integration, and solutions to common problems.)
5454
declarative transaction management.
5555
* <<transaction-programmatic,Programmatic transaction management>> covers support for
5656
programmatic (that is, explicitly coded) transaction management.
57+
* <<transaction-event,Transaction bound event>> describes how you could use application
58+
events within a transaction.
5759

5860

5961

@@ -1924,8 +1926,44 @@ management out of business logic, and is not difficult to configure. When using
19241926
Spring Framework, rather than EJB CMT, the configuration cost of declarative transaction
19251927
management is greatly reduced.
19261928

1929+
[[transaction-event]]
1930+
=== Transaction bound event
19271931

1932+
As of Spring 4.2, the listener of an event can be bound to a phase of the transaction. The
1933+
typical example is to handle the event when the transaction has completed successfully: this
1934+
allows events to be used with more flexibility when the outcome of the current transaction
1935+
actually matters to the listener.
19281936

1937+
Registering a regular event listener is done via the `@EventListener` annotation. If you need
1938+
to bind it to the transaction use `@TransactionalEventListener`. When you do so, the listener
1939+
will be bound to the commit phase of the transaction by default.
1940+
1941+
Let's take an example to illustrate this concept. Assume that a component publish an order
1942+
created event and we want to define a listener that should only handle that event once the
1943+
transaction in which it has been published as committed successfully:
1944+
1945+
[source,java,indent=0]
1946+
[subs="verbatim,quotes"]
1947+
----
1948+
@Component
1949+
public class MyComponent {
1950+
1951+
@TransactionalEventListener
1952+
public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) {
1953+
...
1954+
}
1955+
1956+
}
1957+
----
1958+
1959+
The `TransactionalEventListener` annotation exposes a `phase` attribute that allows to customize
1960+
to which phase of the transaction the listener should be bound to. The valid phases are `BEFORE_COMMIT`,
1961+
`AFTER_COMMIT` (default), `AFTER_ROLLBACK` and `AFTER_COMPLETION` that aggregates the transaction
1962+
completion (be it a commit or a rollback).
1963+
1964+
If no transaction is running, the listener is not invoked at all since we can't honor the required
1965+
semantics. It is however possible to override that behaviour by setting the `fallbackExecution` attribute
1966+
of the annotation to `true`.
19291967

19301968
[[transaction-application-server-integration]]
19311969
=== Application server-specific integration

0 commit comments

Comments
 (0)