Skip to content

Commit 152a7b6

Browse files
committed
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
1 parent 7191050 commit 152a7b6

File tree

3 files changed

+90
-14
lines changed

3 files changed

+90
-14
lines changed

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

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
import java.lang.annotation.Annotation;
2020
import java.lang.reflect.InvocationTargetException;
2121
import java.lang.reflect.Method;
22-
import java.lang.reflect.Parameter;
2322
import java.lang.reflect.UndeclaredThrowableException;
23+
import java.util.Collection;
2424

2525
import org.apache.commons.logging.Log;
2626
import org.apache.commons.logging.LogFactory;
@@ -37,6 +37,7 @@
3737
import org.springframework.core.annotation.Order;
3838
import org.springframework.expression.EvaluationContext;
3939
import org.springframework.util.Assert;
40+
import org.springframework.util.ObjectUtils;
4041
import org.springframework.util.ReflectionUtils;
4142
import org.springframework.util.StringUtils;
4243

@@ -136,7 +137,27 @@ protected Object[] resolveArguments(ApplicationEvent event) {
136137

137138
protected void handleResult(Object result) {
138139
Assert.notNull(this.applicationContext, "ApplicationContext must no be null.");
139-
this.applicationContext.publishEvent(result);
140+
if (result.getClass().isArray()) {
141+
Object[] events = ObjectUtils.toObjectArray(result);
142+
for (Object event : events) {
143+
publishEvent(event);
144+
}
145+
}
146+
else if (result instanceof Collection<?>) {
147+
Collection<?> events = (Collection<?>) result;
148+
for (Object event : events) {
149+
publishEvent(event);
150+
}
151+
}
152+
else {
153+
publishEvent(result);
154+
}
155+
}
156+
157+
private void publishEvent(Object event) {
158+
if (event != null) {
159+
this.applicationContext.publishEvent(event);
160+
}
140161
}
141162

142163

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

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
import java.lang.annotation.Target;
2323
import java.util.ArrayList;
2424
import java.util.Arrays;
25+
import java.util.LinkedHashSet;
2526
import java.util.List;
27+
import java.util.Set;
2628
import java.util.concurrent.CountDownLatch;
2729
import java.util.concurrent.TimeUnit;
2830

@@ -157,7 +159,7 @@ public void simpleReply() {
157159
this.eventCollector.assertNoEventReceived(replyEventListener);
158160
this.context.publishEvent(event);
159161
this.eventCollector.assertEvent(replyEventListener, event);
160-
this.eventCollector.assertEvent(listener, new TestEvent(replyEventListener, event.getId(), event.msg)); // reply
162+
this.eventCollector.assertEvent(listener, new TestEvent(replyEventListener, event.getId(), "dummy")); // reply
161163
this.eventCollector.assertTotalEventsCount(2);
162164
}
163165

@@ -176,6 +178,55 @@ public void nullReplyIgnored() {
176178
this.eventCollector.assertTotalEventsCount(1);
177179
}
178180

181+
@Test
182+
public void arrayReply() {
183+
load(TestEventListener.class, ReplyEventListener.class);
184+
AnotherTestEvent event = new AnotherTestEvent(this, new String[]{"first", "second"});
185+
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
186+
TestEventListener listener = this.context.getBean(TestEventListener.class);
187+
188+
this.eventCollector.assertNoEventReceived(listener);
189+
this.eventCollector.assertNoEventReceived(replyEventListener);
190+
this.context.publishEvent(event);
191+
this.eventCollector.assertEvent(replyEventListener, event);
192+
this.eventCollector.assertEvent(listener, "first", "second"); // reply
193+
this.eventCollector.assertTotalEventsCount(3);
194+
}
195+
196+
@Test
197+
public void collectionReply() {
198+
load(TestEventListener.class, ReplyEventListener.class);
199+
Set<Object> replies = new LinkedHashSet<>();
200+
replies.add("first");
201+
replies.add(4L);
202+
replies.add("third");
203+
AnotherTestEvent event = new AnotherTestEvent(this, replies);
204+
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
205+
TestEventListener listener = this.context.getBean(TestEventListener.class);
206+
207+
this.eventCollector.assertNoEventReceived(listener);
208+
this.eventCollector.assertNoEventReceived(replyEventListener);
209+
this.context.publishEvent(event);
210+
this.eventCollector.assertEvent(replyEventListener, event);
211+
this.eventCollector.assertEvent(listener, "first", "third"); // reply (no listener for 4L)
212+
this.eventCollector.assertTotalEventsCount(3);
213+
}
214+
215+
@Test
216+
public void collectionReplyNullValue() {
217+
load(TestEventListener.class, ReplyEventListener.class);
218+
AnotherTestEvent event = new AnotherTestEvent(this, Arrays.asList(null, "test"));
219+
ReplyEventListener replyEventListener = this.context.getBean(ReplyEventListener.class);
220+
TestEventListener listener = this.context.getBean(TestEventListener.class);
221+
222+
this.eventCollector.assertNoEventReceived(listener);
223+
this.eventCollector.assertNoEventReceived(replyEventListener);
224+
this.context.publishEvent(event);
225+
this.eventCollector.assertEvent(replyEventListener, event);
226+
this.eventCollector.assertEvent(listener, "test");
227+
this.eventCollector.assertTotalEventsCount(2);
228+
}
229+
179230
@Test
180231
public void eventListenerWorksWithInterfaceProxy() throws Exception {
181232
load(ProxyTestBean.class);
@@ -464,15 +515,19 @@ static class ReplyEventListener extends AbstractTestEventListener {
464515
@EventListener
465516
public Object handle(AnotherTestEvent event) {
466517
collectEvent(event);
467-
if (event.msg == null) {
518+
if (event.content == null) {
468519
return null;
469520
}
470-
else if (event.msg.equals("String")) {
471-
return event.msg;
472-
}
473-
else {
474-
return new TestEvent(this, event.getId(), event.msg);
521+
else if (event.content instanceof String) {
522+
String s = (String) event.content;
523+
if (s.equals("String")) {
524+
return event.content;
525+
}
526+
else {
527+
return new TestEvent(this, event.getId(), s);
528+
}
475529
}
530+
return event.content;
476531
}
477532

478533
}
@@ -495,7 +550,7 @@ public void handle(TestEvent event) {
495550
@Async
496551
public void handleAsync(AnotherTestEvent event) {
497552
collectEvent(event);
498-
if ("fail".equals(event.msg)) {
553+
if ("fail".equals(event.content)) {
499554
countDownLatch.countDown();
500555
throw new IllegalStateException("Test exception");
501556
}
@@ -517,7 +572,7 @@ static class AsyncEventListener extends AbstractTestEventListener {
517572
@EventListener
518573
@Async
519574
public void handleAsync(AnotherTestEvent event) {
520-
assertTrue(!Thread.currentThread().getName().equals(event.msg));
575+
assertTrue(!Thread.currentThread().getName().equals(event.content));
521576
collectEvent(event);
522577
countDownLatch.countDown();
523578
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@
2222
@SuppressWarnings("serial")
2323
public class AnotherTestEvent extends IdentifiableApplicationEvent {
2424

25-
public final String msg;
25+
public final Object content;
2626

27-
public AnotherTestEvent(Object source, String msg) {
27+
public AnotherTestEvent(Object source, Object content) {
2828
super(source);
29-
this.msg = msg;
29+
this.content = content;
3030
}
3131

3232
}

0 commit comments

Comments
 (0)