Browse Source

Support for Collection-based return type

If an `@EventListener` annotated method returns a Collection or an Array,
each individual items are now published as an event instead of publishing
one event with said collection.

Issue: SPR-12733
pull/703/merge
Stephane Nicoll 10 years ago
parent
commit
152a7b645f
  1. 25
      spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java
  2. 73
      spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java
  3. 6
      spring-context/src/test/java/org/springframework/context/event/test/AnotherTestEvent.java

25
spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java

@ -19,8 +19,8 @@ package org.springframework.context.event; @@ -19,8 +19,8 @@ package org.springframework.context.event;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -37,6 +37,7 @@ import org.springframework.core.annotation.AnnotationUtils; @@ -37,6 +37,7 @@ import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
@ -136,7 +137,27 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe @@ -136,7 +137,27 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
protected void handleResult(Object result) {
Assert.notNull(this.applicationContext, "ApplicationContext must no be null.");
this.applicationContext.publishEvent(result);
if (result.getClass().isArray()) {
Object[] events = ObjectUtils.toObjectArray(result);
for (Object event : events) {
publishEvent(event);
}
}
else if (result instanceof Collection<?>) {
Collection<?> events = (Collection<?>) result;
for (Object event : events) {
publishEvent(event);
}
}
else {
publishEvent(result);
}
}
private void publishEvent(Object event) {
if (event != null) {
this.applicationContext.publishEvent(event);
}
}

73
spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java

@ -22,7 +22,9 @@ import java.lang.annotation.RetentionPolicy; @@ -22,7 +22,9 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@ -157,7 +159,7 @@ public class AnnotationDrivenEventListenerTests { @@ -157,7 +159,7 @@ public class AnnotationDrivenEventListenerTests {
this.eventCollector.assertNoEventReceived(replyEventListener);
this.context.publishEvent(event);
this.eventCollector.assertEvent(replyEventListener, event);
this.eventCollector.assertEvent(listener, new TestEvent(replyEventListener, event.getId(), event.msg)); // reply
this.eventCollector.assertEvent(listener, new TestEvent(replyEventListener, event.getId(), "dummy")); // reply
this.eventCollector.assertTotalEventsCount(2);
}
@ -176,6 +178,55 @@ public class AnnotationDrivenEventListenerTests { @@ -176,6 +178,55 @@ public class AnnotationDrivenEventListenerTests {
this.eventCollector.assertTotalEventsCount(1);
}
@Test
public void arrayReply() {
load(TestEventListener.class, ReplyEventListener.class);
AnotherTestEvent event = new AnotherTestEvent(this, new String[]{"first", "second"});
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
TestEventListener listener = this.context.getBean(TestEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertNoEventReceived(replyEventListener);
this.context.publishEvent(event);
this.eventCollector.assertEvent(replyEventListener, event);
this.eventCollector.assertEvent(listener, "first", "second"); // reply
this.eventCollector.assertTotalEventsCount(3);
}
@Test
public void collectionReply() {
load(TestEventListener.class, ReplyEventListener.class);
Set<Object> replies = new LinkedHashSet<>();
replies.add("first");
replies.add(4L);
replies.add("third");
AnotherTestEvent event = new AnotherTestEvent(this, replies);
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
TestEventListener listener = this.context.getBean(TestEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertNoEventReceived(replyEventListener);
this.context.publishEvent(event);
this.eventCollector.assertEvent(replyEventListener, event);
this.eventCollector.assertEvent(listener, "first", "third"); // reply (no listener for 4L)
this.eventCollector.assertTotalEventsCount(3);
}
@Test
public void collectionReplyNullValue() {
load(TestEventListener.class, ReplyEventListener.class);
AnotherTestEvent event = new AnotherTestEvent(this, Arrays.asList(null, "test"));
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
TestEventListener listener = this.context.getBean(TestEventListener.class);
this.eventCollector.assertNoEventReceived(listener);
this.eventCollector.assertNoEventReceived(replyEventListener);
this.context.publishEvent(event);
this.eventCollector.assertEvent(replyEventListener, event);
this.eventCollector.assertEvent(listener, "test");
this.eventCollector.assertTotalEventsCount(2);
}
@Test
public void eventListenerWorksWithInterfaceProxy() throws Exception {
load(ProxyTestBean.class);
@ -464,15 +515,19 @@ public class AnnotationDrivenEventListenerTests { @@ -464,15 +515,19 @@ public class AnnotationDrivenEventListenerTests {
@EventListener
public Object handle(AnotherTestEvent event) {
collectEvent(event);
if (event.msg == null) {
if (event.content == null) {
return null;
}
else if (event.msg.equals("String")) {
return event.msg;
}
else {
return new TestEvent(this, event.getId(), event.msg);
else if (event.content instanceof String) {
String s = (String) event.content;
if (s.equals("String")) {
return event.content;
}
else {
return new TestEvent(this, event.getId(), s);
}
}
return event.content;
}
}
@ -495,7 +550,7 @@ public class AnnotationDrivenEventListenerTests { @@ -495,7 +550,7 @@ public class AnnotationDrivenEventListenerTests {
@Async
public void handleAsync(AnotherTestEvent event) {
collectEvent(event);
if ("fail".equals(event.msg)) {
if ("fail".equals(event.content)) {
countDownLatch.countDown();
throw new IllegalStateException("Test exception");
}
@ -517,7 +572,7 @@ public class AnnotationDrivenEventListenerTests { @@ -517,7 +572,7 @@ public class AnnotationDrivenEventListenerTests {
@EventListener
@Async
public void handleAsync(AnotherTestEvent event) {
assertTrue(!Thread.currentThread().getName().equals(event.msg));
assertTrue(!Thread.currentThread().getName().equals(event.content));
collectEvent(event);
countDownLatch.countDown();
}

6
spring-context/src/test/java/org/springframework/context/event/test/AnotherTestEvent.java

@ -22,11 +22,11 @@ package org.springframework.context.event.test; @@ -22,11 +22,11 @@ package org.springframework.context.event.test;
@SuppressWarnings("serial")
public class AnotherTestEvent extends IdentifiableApplicationEvent {
public final String msg;
public final Object content;
public AnotherTestEvent(Object source, String msg) {
public AnotherTestEvent(Object source, Object content) {
super(source);
this.msg = msg;
this.content = content;
}
}

Loading…
Cancel
Save