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
This commit is contained in:
parent
7191050e26
commit
152a7b645f
|
|
@ -19,8 +19,8 @@ package org.springframework.context.event;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Parameter;
|
|
||||||
import java.lang.reflect.UndeclaredThrowableException;
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
@ -37,6 +37,7 @@ import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
|
@ -136,7 +137,27 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
|
||||||
|
|
||||||
protected void handleResult(Object result) {
|
protected void handleResult(Object result) {
|
||||||
Assert.notNull(this.applicationContext, "ApplicationContext must no be null.");
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,9 @@ import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
|
@ -157,7 +159,7 @@ public class AnnotationDrivenEventListenerTests {
|
||||||
this.eventCollector.assertNoEventReceived(replyEventListener);
|
this.eventCollector.assertNoEventReceived(replyEventListener);
|
||||||
this.context.publishEvent(event);
|
this.context.publishEvent(event);
|
||||||
this.eventCollector.assertEvent(replyEventListener, 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);
|
this.eventCollector.assertTotalEventsCount(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,6 +178,55 @@ public class AnnotationDrivenEventListenerTests {
|
||||||
this.eventCollector.assertTotalEventsCount(1);
|
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
|
@Test
|
||||||
public void eventListenerWorksWithInterfaceProxy() throws Exception {
|
public void eventListenerWorksWithInterfaceProxy() throws Exception {
|
||||||
load(ProxyTestBean.class);
|
load(ProxyTestBean.class);
|
||||||
|
|
@ -464,15 +515,19 @@ public class AnnotationDrivenEventListenerTests {
|
||||||
@EventListener
|
@EventListener
|
||||||
public Object handle(AnotherTestEvent event) {
|
public Object handle(AnotherTestEvent event) {
|
||||||
collectEvent(event);
|
collectEvent(event);
|
||||||
if (event.msg == null) {
|
if (event.content == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else if (event.msg.equals("String")) {
|
else if (event.content instanceof String) {
|
||||||
return event.msg;
|
String s = (String) event.content;
|
||||||
}
|
if (s.equals("String")) {
|
||||||
else {
|
return event.content;
|
||||||
return new TestEvent(this, event.getId(), event.msg);
|
}
|
||||||
|
else {
|
||||||
|
return new TestEvent(this, event.getId(), s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return event.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -495,7 +550,7 @@ public class AnnotationDrivenEventListenerTests {
|
||||||
@Async
|
@Async
|
||||||
public void handleAsync(AnotherTestEvent event) {
|
public void handleAsync(AnotherTestEvent event) {
|
||||||
collectEvent(event);
|
collectEvent(event);
|
||||||
if ("fail".equals(event.msg)) {
|
if ("fail".equals(event.content)) {
|
||||||
countDownLatch.countDown();
|
countDownLatch.countDown();
|
||||||
throw new IllegalStateException("Test exception");
|
throw new IllegalStateException("Test exception");
|
||||||
}
|
}
|
||||||
|
|
@ -517,7 +572,7 @@ public class AnnotationDrivenEventListenerTests {
|
||||||
@EventListener
|
@EventListener
|
||||||
@Async
|
@Async
|
||||||
public void handleAsync(AnotherTestEvent event) {
|
public void handleAsync(AnotherTestEvent event) {
|
||||||
assertTrue(!Thread.currentThread().getName().equals(event.msg));
|
assertTrue(!Thread.currentThread().getName().equals(event.content));
|
||||||
collectEvent(event);
|
collectEvent(event);
|
||||||
countDownLatch.countDown();
|
countDownLatch.countDown();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,11 @@ package org.springframework.context.event.test;
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class AnotherTestEvent extends IdentifiableApplicationEvent {
|
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);
|
super(source);
|
||||||
this.msg = msg;
|
this.content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue