Support for multiple events per method
In addition to specifying the event type to listen to via a method
parameter, any @EventListener annotated method can now alternatively
define the event type(s) to listen to via the "classes" attributes (that
is aliased to "value").
Something like
@EventListener({FooEvent.class, BarEvent.class})
public void handleFooBar() { .... }
Issue: SPR-13156
This commit is contained in:
@@ -25,7 +25,7 @@ import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.event.ApplicationListenerMethodAdapter;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.context.event.GenericApplicationListener;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
@@ -80,8 +80,8 @@ class ApplicationListenerMethodTransactionalAdapter extends ApplicationListenerM
|
||||
}
|
||||
|
||||
static TransactionalEventListener findAnnotation(Method method) {
|
||||
TransactionalEventListener annotation = AnnotationUtils
|
||||
.findAnnotation(method, TransactionalEventListener.class);
|
||||
TransactionalEventListener annotation = AnnotatedElementUtils
|
||||
.findMergedAnnotation(method, TransactionalEventListener.class);
|
||||
if (annotation == null) {
|
||||
throw new IllegalStateException("No TransactionalEventListener annotation found on '" + method + "'");
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
/**
|
||||
* An {@link EventListener} that is invoked according to a {@link TransactionPhase}.
|
||||
@@ -55,6 +56,21 @@ public @interface TransactionalEventListener {
|
||||
*/
|
||||
boolean fallbackExecution() default false;
|
||||
|
||||
/**
|
||||
* Alias for {@link #classes()}.
|
||||
*/
|
||||
@AliasFor(attribute = "classes")
|
||||
Class<?>[] value() default {};
|
||||
|
||||
/**
|
||||
* The event classes that this listener handles. When this attribute is specified
|
||||
* with one value, the method parameter may or may not be specified. When this
|
||||
* attribute is specified with more than one value, the method must not have a
|
||||
* parameter.
|
||||
*/
|
||||
@AliasFor(attribute = "value")
|
||||
Class<?>[] classes() default {};
|
||||
|
||||
/**
|
||||
* Spring Expression Language (SpEL) attribute used for making the event
|
||||
* handling conditional.
|
||||
|
||||
@@ -22,6 +22,9 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.context.PayloadApplicationEvent;
|
||||
import org.springframework.context.event.ApplicationListenerMethodAdapter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
@@ -36,7 +39,7 @@ public class ApplicationListenerMethodTransactionalAdapterTests {
|
||||
|
||||
@Test
|
||||
public void noAnnotation() {
|
||||
Method m = ReflectionUtils.findMethod(PhaseConfigurationTestListener.class,
|
||||
Method m = ReflectionUtils.findMethod(SampleEvents.class,
|
||||
"noAnnotation", String.class);
|
||||
|
||||
thrown.expect(IllegalStateException.class);
|
||||
@@ -46,24 +49,54 @@ public class ApplicationListenerMethodTransactionalAdapterTests {
|
||||
|
||||
@Test
|
||||
public void defaultPhase() {
|
||||
Method m = ReflectionUtils.findMethod(PhaseConfigurationTestListener.class, "defaultPhase", String.class);
|
||||
Method m = ReflectionUtils.findMethod(SampleEvents.class, "defaultPhase", String.class);
|
||||
assertPhase(m, TransactionPhase.AFTER_COMMIT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void phaseSet() {
|
||||
Method m = ReflectionUtils.findMethod(PhaseConfigurationTestListener.class, "phaseSet", String.class);
|
||||
Method m = ReflectionUtils.findMethod(SampleEvents.class, "phaseSet", String.class);
|
||||
assertPhase(m, TransactionPhase.AFTER_ROLLBACK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void phaseAndClassesSet() {
|
||||
Method m = ReflectionUtils.findMethod(SampleEvents.class, "phaseAndClassesSet");
|
||||
assertPhase(m, TransactionPhase.AFTER_COMPLETION);
|
||||
supportsEventType(true, m, createGenericEventType(String.class));
|
||||
supportsEventType(true, m, createGenericEventType(Integer.class));
|
||||
supportsEventType(false, m, createGenericEventType(Double.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void valueSet() {
|
||||
Method m = ReflectionUtils.findMethod(SampleEvents.class, "valueSet");
|
||||
assertPhase(m, TransactionPhase.AFTER_COMMIT);
|
||||
supportsEventType(true, m, createGenericEventType(String.class));
|
||||
supportsEventType(false, m, createGenericEventType(Double.class));
|
||||
}
|
||||
|
||||
private void assertPhase(Method method, TransactionPhase expected) {
|
||||
assertNotNull("Method must not be null", method);
|
||||
TransactionalEventListener annotation = ApplicationListenerMethodTransactionalAdapter.findAnnotation(method);
|
||||
assertEquals("Wrong phase for '" + method + "'", expected, annotation.phase());
|
||||
}
|
||||
|
||||
private void supportsEventType(boolean match, Method method, ResolvableType eventType) {
|
||||
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
|
||||
assertEquals("Wrong match for event '" + eventType + "' on " + method,
|
||||
match, adapter.supportsEventType(eventType));
|
||||
}
|
||||
|
||||
static class PhaseConfigurationTestListener {
|
||||
private ApplicationListenerMethodTransactionalAdapter createTestInstance(Method m) {
|
||||
return new ApplicationListenerMethodTransactionalAdapter("test", SampleEvents.class, m);
|
||||
}
|
||||
|
||||
private ResolvableType createGenericEventType(Class<?> payloadType) {
|
||||
return ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, payloadType);
|
||||
}
|
||||
|
||||
static class SampleEvents {
|
||||
|
||||
public void noAnnotation(String data) {
|
||||
}
|
||||
@@ -76,6 +109,15 @@ public class ApplicationListenerMethodTransactionalAdapterTests {
|
||||
public void phaseSet(String data) {
|
||||
}
|
||||
|
||||
@TransactionalEventListener(classes = {String.class, Integer.class},
|
||||
phase = TransactionPhase.AFTER_COMPLETION)
|
||||
public void phaseAndClassesSet() {
|
||||
}
|
||||
|
||||
@TransactionalEventListener(String.class)
|
||||
public void valueSet() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user