From 6283456ef24c4271469ea67ff21b95e303d708c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=85=B6=E8=8B=97?= Date: Wed, 26 Feb 2020 18:57:35 +0800 Subject: [PATCH 1/2] Add support for explicit generic type in PayloadApplicationEvent See gh-24599 --- .../context/PayloadApplicationEvent.java | 20 +++++++++- .../support/AbstractApplicationContext.java | 2 +- .../AnnotationDrivenEventListenerTests.java | 37 +++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/PayloadApplicationEvent.java b/spring-context/src/main/java/org/springframework/context/PayloadApplicationEvent.java index addcbc5dd97..f6121045576 100644 --- a/spring-context/src/main/java/org/springframework/context/PayloadApplicationEvent.java +++ b/spring-context/src/main/java/org/springframework/context/PayloadApplicationEvent.java @@ -20,6 +20,7 @@ import java.util.function.Consumer; import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableTypeProvider; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -27,6 +28,7 @@ import org.springframework.util.Assert; * * @author Stephane Nicoll * @author Juergen Hoeller + * @author Qimiao Chen * @since 4.2 * @param the payload type of the event * @see ApplicationEventPublisher#publishEvent(Object) @@ -37,6 +39,8 @@ public class PayloadApplicationEvent extends ApplicationEvent implements Reso private final T payload; + @Nullable + private ResolvableType payloadType; /** * Create a new PayloadApplicationEvent. @@ -44,15 +48,29 @@ public class PayloadApplicationEvent extends ApplicationEvent implements Reso * @param payload the payload object (never {@code null}) */ public PayloadApplicationEvent(Object source, T payload) { + this(source, payload, null); + } + + /** + * Create a new PayloadApplicationEvent. + * @param source the object on which the event initially occurred (never {@code null}) + * @param payload the payload object (never {@code null}) + * @param payloadType the type object of payload object (can be {@code null}) + */ + public PayloadApplicationEvent(Object source, T payload, @Nullable ResolvableType payloadType) { super(source); Assert.notNull(payload, "Payload must not be null"); this.payload = payload; + this.payloadType = payloadType; } @Override public ResolvableType getResolvableType() { - return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getPayload())); + if (this.payloadType == null) { + this.payloadType = ResolvableType.forInstance(getPayload()); + } + return ResolvableType.forClassWithGenerics(getClass(), this.payloadType); } /** diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java index 27b050838cd..1bb789a46e9 100644 --- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java +++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java @@ -407,7 +407,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader applicationEvent = (ApplicationEvent) event; } else { - applicationEvent = new PayloadApplicationEvent<>(this, event); + applicationEvent = new PayloadApplicationEvent<>(this, event, eventType); if (eventType == null) { eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType(); } diff --git a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java index 8c51dd29420..c95f163007c 100644 --- a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java @@ -60,6 +60,7 @@ import org.springframework.context.event.test.GenericEventPojo; import org.springframework.context.event.test.Identifiable; import org.springframework.context.event.test.TestEvent; import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AliasFor; import org.springframework.core.annotation.Order; import org.springframework.scheduling.annotation.Async; @@ -622,6 +623,17 @@ class AnnotationDrivenEventListenerTests { this.eventCollector.assertNoEventReceived(listener); this.eventCollector.assertTotalEventsCount(0); } + @Test + public void publishEventWithGeneric() throws NoSuchMethodException { + MyAnnotationConfigApplicationContext ctx = new MyAnnotationConfigApplicationContext(); + context = ctx; + ctx.register(MyEventWithGenericListener.class); + ctx.refresh(); + ResolvableType resolvableType = ResolvableType.forMethodParameter(MyEventWithGenericListener.class.getMethod("onMyEventWithGeneric",MyEventWithGeneric.class),0); + ctx.publishEvent(new MyEventWithGeneric(),resolvableType); + assertThat(MyEventWithGenericListener.count).isEqualTo(1); + } + @Test void orderedListeners() { @@ -1134,4 +1146,29 @@ class AnnotationDrivenEventListenerTests { } } + public static class MyAnnotationConfigApplicationContext extends AnnotationConfigApplicationContext{ + + @Override + public void publishEvent(Object event, ResolvableType eventType) { + super.publishEvent(event, eventType); + } + } + + public static class MyEventWithGeneric { + + } + + @Component + public static class MyEventWithGenericListener{ + + public static int count ; + + @EventListener + public void onMyEventWithGeneric(MyEventWithGeneric event){ + count ++; + } + + + } + } From 792b366bda738f9c7d0287f13a61019357df2176 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 2 Dec 2021 10:10:38 +0100 Subject: [PATCH 2/2] Polish "Add support for explicit generic type in PayloadApplicationEvent" See gh-24599 --- .../context/PayloadApplicationEvent.java | 24 ++++----- .../AnnotationDrivenEventListenerTests.java | 37 -------------- .../event/PayloadApplicationEventTests.java | 49 +++++++++++++++++-- 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/PayloadApplicationEvent.java b/spring-context/src/main/java/org/springframework/context/PayloadApplicationEvent.java index f6121045576..612d2752f5f 100644 --- a/spring-context/src/main/java/org/springframework/context/PayloadApplicationEvent.java +++ b/spring-context/src/main/java/org/springframework/context/PayloadApplicationEvent.java @@ -39,37 +39,33 @@ public class PayloadApplicationEvent extends ApplicationEvent implements Reso private final T payload; - @Nullable - private ResolvableType payloadType; + private final ResolvableType payloadType; /** * Create a new PayloadApplicationEvent. * @param source the object on which the event initially occurred (never {@code null}) * @param payload the payload object (never {@code null}) + * @param payloadType the type object of payload object (can be {@code null}) */ - public PayloadApplicationEvent(Object source, T payload) { - this(source, payload, null); + public PayloadApplicationEvent(Object source, T payload, @Nullable ResolvableType payloadType) { + super(source); + Assert.notNull(payload, "Payload must not be null"); + this.payload = payload; + this.payloadType = (payloadType != null) ? payloadType : ResolvableType.forInstance(payload); } /** - * Create a new PayloadApplicationEvent. + * Create a new PayloadApplicationEvent, using the instance to infer its type. * @param source the object on which the event initially occurred (never {@code null}) * @param payload the payload object (never {@code null}) - * @param payloadType the type object of payload object (can be {@code null}) */ - public PayloadApplicationEvent(Object source, T payload, @Nullable ResolvableType payloadType) { - super(source); - Assert.notNull(payload, "Payload must not be null"); - this.payload = payload; - this.payloadType = payloadType; + public PayloadApplicationEvent(Object source, T payload) { + this(source, payload, null); } @Override public ResolvableType getResolvableType() { - if (this.payloadType == null) { - this.payloadType = ResolvableType.forInstance(getPayload()); - } return ResolvableType.forClassWithGenerics(getClass(), this.payloadType); } diff --git a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java index c95f163007c..8c51dd29420 100644 --- a/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/AnnotationDrivenEventListenerTests.java @@ -60,7 +60,6 @@ import org.springframework.context.event.test.GenericEventPojo; import org.springframework.context.event.test.Identifiable; import org.springframework.context.event.test.TestEvent; import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AliasFor; import org.springframework.core.annotation.Order; import org.springframework.scheduling.annotation.Async; @@ -623,17 +622,6 @@ class AnnotationDrivenEventListenerTests { this.eventCollector.assertNoEventReceived(listener); this.eventCollector.assertTotalEventsCount(0); } - @Test - public void publishEventWithGeneric() throws NoSuchMethodException { - MyAnnotationConfigApplicationContext ctx = new MyAnnotationConfigApplicationContext(); - context = ctx; - ctx.register(MyEventWithGenericListener.class); - ctx.refresh(); - ResolvableType resolvableType = ResolvableType.forMethodParameter(MyEventWithGenericListener.class.getMethod("onMyEventWithGeneric",MyEventWithGeneric.class),0); - ctx.publishEvent(new MyEventWithGeneric(),resolvableType); - assertThat(MyEventWithGenericListener.count).isEqualTo(1); - } - @Test void orderedListeners() { @@ -1146,29 +1134,4 @@ class AnnotationDrivenEventListenerTests { } } - public static class MyAnnotationConfigApplicationContext extends AnnotationConfigApplicationContext{ - - @Override - public void publishEvent(Object event, ResolvableType eventType) { - super.publishEvent(event, eventType); - } - } - - public static class MyEventWithGeneric { - - } - - @Component - public static class MyEventWithGenericListener{ - - public static int count ; - - @EventListener - public void onMyEventWithGeneric(MyEventWithGeneric event){ - count ++; - } - - - } - } diff --git a/spring-context/src/test/java/org/springframework/context/event/PayloadApplicationEventTests.java b/spring-context/src/test/java/org/springframework/context/event/PayloadApplicationEventTests.java index c752623bb55..8fb78ceda8a 100644 --- a/spring-context/src/test/java/org/springframework/context/event/PayloadApplicationEventTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/PayloadApplicationEventTests.java @@ -27,6 +27,7 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.PayloadApplicationEvent; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.ResolvableType; import org.springframework.stereotype.Component; import static org.assertj.core.api.Assertions.assertThat; @@ -34,10 +35,40 @@ import static org.assertj.core.api.Assertions.assertThat; /** * @author Juergen Hoeller */ -public class PayloadApplicationEventTests { +class PayloadApplicationEventTests { @Test - public void testEventClassWithInterface() { + void payloadApplicationEventWithNoTypeUsesInstance() { + NumberHolder payload = new NumberHolder<>(42); + PayloadApplicationEvent> event = new PayloadApplicationEvent<>(this, payload); + assertThat(event.getResolvableType()).satisfies(eventType -> { + assertThat(eventType.toClass()).isEqualTo(PayloadApplicationEvent.class); + assertThat(eventType.getGenerics()).hasSize(1); + assertThat(eventType.getGenerics()[0]).satisfies(bodyType -> { + assertThat(bodyType.toClass()).isEqualTo(NumberHolder.class); + assertThat(bodyType.hasUnresolvableGenerics()).isTrue(); + }); + }); + } + + @Test + void payloadApplicationEventWithType() { + NumberHolder payload = new NumberHolder<>(42); + ResolvableType payloadType = ResolvableType.forClassWithGenerics(NumberHolder.class, Integer.class); + PayloadApplicationEvent> event = new PayloadApplicationEvent<>(this, payload, payloadType); + assertThat(event.getResolvableType()).satisfies(eventType -> { + assertThat(eventType.toClass()).isEqualTo(PayloadApplicationEvent.class); + assertThat(eventType.getGenerics()).hasSize(1); + assertThat(eventType.getGenerics()[0]).satisfies(bodyType -> { + assertThat(bodyType.toClass()).isEqualTo(NumberHolder.class); + assertThat(bodyType.hasUnresolvableGenerics()).isFalse(); + assertThat(bodyType.getGenerics()[0].toClass()).isEqualTo(Integer.class); + }); + }); + } + + @Test + void testEventClassWithInterface() { ApplicationContext ac = new AnnotationConfigApplicationContext(AuditableListener.class); AuditablePayloadEvent event = new AuditablePayloadEvent<>(this, "xyz"); @@ -46,7 +77,7 @@ public class PayloadApplicationEventTests { } @Test - public void testProgrammaticEventListener() { + void testProgrammaticEventListener() { List events = new ArrayList<>(); ApplicationListener> listener = events::add; ApplicationListener> mismatch = (event -> event.getPayload().intValue()); @@ -62,7 +93,7 @@ public class PayloadApplicationEventTests { } @Test - public void testProgrammaticPayloadListener() { + void testProgrammaticPayloadListener() { List events = new ArrayList<>(); ApplicationListener> listener = ApplicationListener.forPayload(events::add); ApplicationListener> mismatch = ApplicationListener.forPayload(payload -> payload.intValue()); @@ -102,4 +133,14 @@ public class PayloadApplicationEventTests { } } + static class NumberHolder { + + private T number; + + public NumberHolder(T number) { + this.number = number; + } + + } + }