Skip to content

Allow programmatic registration of completion and error callbacks to transactional event listeners #24163

@odrotbohm

Description

@odrotbohm

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 the Throwable would not be propagated, so that TransactionSynchronizationUtils.invokeAfterCompletion(…) would not actually see the Throwable 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 accessible onApplicationEvent(…) 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.

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)in: dataIssues in data modules (jdbc, orm, oxm, tx)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions