-
Notifications
You must be signed in to change notification settings - Fork 38.7k
Description
Currently, event listeners bound to transaction commits, rollbacks or completion in general can only be used via the @TransactionalEventListener annotation. It would be nice if there was a way to programmatically register those via context.addApplicationListener(…)
, pointing to a method, configuring the transaction phase to apply the listener in etc.
After revisiting this with @jhoeller, here's the current state and derived ideas for what to improve on: ApplicationListenerMethodTransactionalAdapter
is currently package protected and thus prevents programatic access to transactional event listeners as there's no type you can refer to even in instance of checks. We had the idea to introduce a public type that ALMTA
would extend / implement that exposes the following API:
- the transaction phase the listener is bound to
- a (nullable) identifier whose default is derived from the annotated method's name, potentially overridable via a to be introduced annotation attribute on
@TransactionalEventListener
or via the to be introduced programatic API - a
….registerCompletionCallback(Consumer<ApplicationEvent>)
that will be called for every successfully completed invocation of the listener - a
….registerErrorCallback(Consumer<Throwable>)
that will be called in case the listener threw an exception. If that callback is registered theThrowable
would not be propagated, so thatTransactionSynchronizationUtils.invokeAfterCompletion(…)
would not actually see theThrowable
and avoid the logging. - if the type to be introduced would become an interface, it would be nice if it also exposed
ApplicationListenerMethodAdapter.processEvent(…)
to make sure the listener instance can be invoked explicitly. This is needed in the context of transactional even listeners as the already externally accessibleonApplicationEvent(…)
implements the event publication by registering a transaction synchronization, which does not immediately execute the listener.
Most of this is driven by a prototypical event publication log implementation that makes sure that events handled by transactional event listeners do not get lost in case of exceptions occurring during the handling or unanticipated application shutdowns during that work. That work can be found here. The implementation currently relies on deep reflection inspection of framework internals and AOP usage to implement the log and log entry completion. That implementation would become significantly simpler.
Another aspect that makes this worthwhile is the general ability to use event listener registrations programmatically to avoid reflection and annotation detection overhead which in turn aligns with our functional bean registration API efforts.