1717package org .springframework .context .event ;
1818
1919import java .lang .reflect .Constructor ;
20- import java .util .function .Function ;
2120
2221import org .aopalliance .intercept .MethodInterceptor ;
2322import org .aopalliance .intercept .MethodInvocation ;
3130import org .springframework .util .Assert ;
3231
3332/**
34- * {@link MethodInterceptor Interceptor} that publishes an {@code ApplicationEvent}
35- * to all {@code ApplicationListeners} registered with an {@code ApplicationEventPublisher}.
33+ * {@link MethodInterceptor Interceptor} that publishes an {@code ApplicationEvent} to
34+ * all {@code ApplicationListeners} registered with an {@code ApplicationEventPublisher}
3635 * after each <i>successful</i> method invocation.
3736 *
3837 * <p>Note that this interceptor is capable of publishing a custom event after each
3938 * <i>successful</i> method invocation, configured via the
4039 * {@link #setApplicationEventClass "applicationEventClass"} property. As of 7.0.3,
41- * you can configure a {@link #setApplicationEventFactory factory function} instead.
40+ * you can configure a {@link #setApplicationEventFactory factory function} instead,
41+ * implementing the primary {@link ApplicationEventFactory#onSuccess} method there.
4242 *
43- * <p>As of 7.0.3, this interceptor publishes a {@link MethodFailureEvent} for
44- * every exception encountered from a method invocation. This can be conveniently
43+ * <p>By default (as of 7.0.3) , this interceptor publishes a {@link MethodFailureEvent}
44+ * for every exception encountered from a method invocation. This can be conveniently
4545 * tracked via an {@code ApplicationListener<MethodFailureEvent>} class or an
46- * {@code @EventListener(MethodFailureEvent.class)} method.
46+ * {@code @EventListener(MethodFailureEvent.class)} method. The failure event can be
47+ * customized through overriding the {@link ApplicationEventFactory#onFailure} method.
4748 *
4849 * @author Dmitriy Kopylenko
4950 * @author Juergen Hoeller
5758public class EventPublicationInterceptor
5859 implements MethodInterceptor , ApplicationEventPublisherAware , InitializingBean {
5960
60- private @ Nullable Function < MethodInvocation , ? extends ApplicationEvent > applicationEventFactory ;
61+ private ApplicationEventFactory applicationEventFactory = ( invocation , returnValue ) -> null ;
6162
6263 private @ Nullable ApplicationEventPublisher applicationEventPublisher ;
6364
6465
6566 /**
66- * Set the application event class to publish.
67+ * Set the application event class to publish after each successful invocation .
6768 * <p>The event class <b>must</b> have a constructor with a single
6869 * {@code Object} argument for the event source. The interceptor
6970 * will pass in the invoked object.
@@ -79,7 +80,8 @@ public void setApplicationEventClass(Class<? extends ApplicationEvent> applicati
7980 }
8081 try {
8182 Constructor <? extends ApplicationEvent > ctor = applicationEventClass .getConstructor (Object .class );
82- this .applicationEventFactory = (invocation -> BeanUtils .instantiateClass (ctor , invocation .getThis ()));
83+ this .applicationEventFactory = ((invocation , returnValue ) ->
84+ BeanUtils .instantiateClass (ctor , invocation .getThis ()));
8385 }
8486 catch (NoSuchMethodException ex ) {
8587 throw new IllegalArgumentException ("ApplicationEvent class [" +
@@ -89,12 +91,12 @@ public void setApplicationEventClass(Class<? extends ApplicationEvent> applicati
8991
9092 /**
9193 * Specify a factory function for {@link ApplicationEvent} instances built from a
92- * {@link MethodInvocation}, representing a <i>successful</i> method invocation.
94+ * {@link MethodInvocation}, representing each <i>successful</i> method invocation.
9395 * @since 7.0.3
9496 * @see #setApplicationEventClass
9597 */
96- public void setApplicationEventFactory (Function < MethodInvocation , ? extends ApplicationEvent > factoryFunction ) {
97- this .applicationEventFactory = factoryFunction ;
98+ public void setApplicationEventFactory (ApplicationEventFactory applicationEventFactory ) {
99+ this .applicationEventFactory = applicationEventFactory ;
98100 }
99101
100102 @ Override
@@ -119,14 +121,52 @@ public void afterPropertiesSet() throws Exception {
119121 retVal = invocation .proceed ();
120122 }
121123 catch (Throwable ex ) {
122- this .applicationEventPublisher .publishEvent (new MethodFailureEvent (invocation , ex ));
124+ // Publish event after failed invocation.
125+ ApplicationEvent event = this .applicationEventFactory .onFailure (invocation , ex );
126+ if (event != null ) {
127+ this .applicationEventPublisher .publishEvent (event );
128+ }
123129 throw ex ;
124130 }
125131
126- if (this .applicationEventFactory != null ) {
127- this .applicationEventPublisher .publishEvent (this .applicationEventFactory .apply (invocation ));
132+ // Publish event after successful invocation.
133+ ApplicationEvent event = this .applicationEventFactory .onSuccess (invocation , retVal );
134+ if (event != null ) {
135+ this .applicationEventPublisher .publishEvent (event );
128136 }
129137 return retVal ;
130138 }
131139
140+
141+ /**
142+ * Callback interface for building an {@link ApplicationEvent} after a method invocation.
143+ * @since 7.0.3
144+ */
145+ @ FunctionalInterface
146+ public interface ApplicationEventFactory {
147+
148+ /**
149+ * Build an {@link ApplicationEvent} for the given successful method invocation.
150+ * <p>This is the primary method to implement since there is no such default event.
151+ * This may also return {@code null} for not publishing an event on success at all.
152+ * @param invocation the successful method invocation
153+ * @param returnValue the value that the method returned, if any
154+ * @return the event to publish, or {@code null} for none
155+ */
156+ @ Nullable ApplicationEvent onSuccess (MethodInvocation invocation , @ Nullable Object returnValue );
157+
158+ /**
159+ * Build an {@link ApplicationEvent} for the given failed method invocation.
160+ * <p>The default implementation builds a common {@link MethodFailureEvent}.
161+ * This can be overridden to build a custom event instead, or to return
162+ * {@code null} for not publishing an event on failure at all.
163+ * @param invocation the failed method invocation
164+ * @param failure the exception thrown from the method
165+ * @return the event to publish, or {@code null} for none
166+ */
167+ default @ Nullable ApplicationEvent onFailure (MethodInvocation invocation , Throwable failure ) {
168+ return new MethodFailureEvent (invocation , failure );
169+ }
170+ }
171+
132172}
0 commit comments