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..612d2752f5f 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,22 +39,34 @@ public class PayloadApplicationEvent extends ApplicationEvent implements Reso private final T payload; + 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) { + 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, 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}) + */ + public PayloadApplicationEvent(Object source, T payload) { + this(source, payload, null); } @Override public ResolvableType getResolvableType() { - return ResolvableType.forClassWithGenerics(getClass(), 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/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; + } + + } + }