Merge pull request #24599 from chenqimiao

* pr/24599:
  Polish "Add support for explicit generic type in PayloadApplicationEvent"
  Add support for explicit generic type in PayloadApplicationEvent

Closes gh-24599
This commit is contained in:
Stephane Nicoll 2021-12-02 10:23:11 +01:00
commit 5589e6ceed
3 changed files with 62 additions and 7 deletions

View File

@ -20,6 +20,7 @@ import java.util.function.Consumer;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.core.ResolvableTypeProvider; import org.springframework.core.ResolvableTypeProvider;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
/** /**
@ -27,6 +28,7 @@ import org.springframework.util.Assert;
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Qimiao Chen
* @since 4.2 * @since 4.2
* @param <T> the payload type of the event * @param <T> the payload type of the event
* @see ApplicationEventPublisher#publishEvent(Object) * @see ApplicationEventPublisher#publishEvent(Object)
@ -37,22 +39,34 @@ public class PayloadApplicationEvent<T> extends ApplicationEvent implements Reso
private final T payload; private final T payload;
private final ResolvableType payloadType;
/** /**
* Create a new PayloadApplicationEvent. * Create a new PayloadApplicationEvent.
* @param source the object on which the event initially occurred (never {@code null}) * @param source the object on which the event initially occurred (never {@code null})
* @param payload the payload object (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); super(source);
Assert.notNull(payload, "Payload must not be null"); Assert.notNull(payload, "Payload must not be null");
this.payload = payload; 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 @Override
public ResolvableType getResolvableType() { public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getPayload())); return ResolvableType.forClassWithGenerics(getClass(), this.payloadType);
} }
/** /**

View File

@ -407,7 +407,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
applicationEvent = (ApplicationEvent) event; applicationEvent = (ApplicationEvent) event;
} }
else { else {
applicationEvent = new PayloadApplicationEvent<>(this, event); applicationEvent = new PayloadApplicationEvent<>(this, event, eventType);
if (eventType == null) { if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType(); eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
} }

View File

@ -27,6 +27,7 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.PayloadApplicationEvent; import org.springframework.context.PayloadApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext; import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -34,10 +35,40 @@ import static org.assertj.core.api.Assertions.assertThat;
/** /**
* @author Juergen Hoeller * @author Juergen Hoeller
*/ */
public class PayloadApplicationEventTests { class PayloadApplicationEventTests {
@Test @Test
public void testEventClassWithInterface() { void payloadApplicationEventWithNoTypeUsesInstance() {
NumberHolder<Integer> payload = new NumberHolder<>(42);
PayloadApplicationEvent<NumberHolder<Integer>> 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<Integer> payload = new NumberHolder<>(42);
ResolvableType payloadType = ResolvableType.forClassWithGenerics(NumberHolder.class, Integer.class);
PayloadApplicationEvent<NumberHolder<Integer>> 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); ApplicationContext ac = new AnnotationConfigApplicationContext(AuditableListener.class);
AuditablePayloadEvent<String> event = new AuditablePayloadEvent<>(this, "xyz"); AuditablePayloadEvent<String> event = new AuditablePayloadEvent<>(this, "xyz");
@ -46,7 +77,7 @@ public class PayloadApplicationEventTests {
} }
@Test @Test
public void testProgrammaticEventListener() { void testProgrammaticEventListener() {
List<Auditable> events = new ArrayList<>(); List<Auditable> events = new ArrayList<>();
ApplicationListener<AuditablePayloadEvent<String>> listener = events::add; ApplicationListener<AuditablePayloadEvent<String>> listener = events::add;
ApplicationListener<AuditablePayloadEvent<Integer>> mismatch = (event -> event.getPayload().intValue()); ApplicationListener<AuditablePayloadEvent<Integer>> mismatch = (event -> event.getPayload().intValue());
@ -62,7 +93,7 @@ public class PayloadApplicationEventTests {
} }
@Test @Test
public void testProgrammaticPayloadListener() { void testProgrammaticPayloadListener() {
List<String> events = new ArrayList<>(); List<String> events = new ArrayList<>();
ApplicationListener<PayloadApplicationEvent<String>> listener = ApplicationListener.forPayload(events::add); ApplicationListener<PayloadApplicationEvent<String>> listener = ApplicationListener.forPayload(events::add);
ApplicationListener<PayloadApplicationEvent<Integer>> mismatch = ApplicationListener.forPayload(payload -> payload.intValue()); ApplicationListener<PayloadApplicationEvent<Integer>> mismatch = ApplicationListener.forPayload(payload -> payload.intValue());
@ -102,4 +133,14 @@ public class PayloadApplicationEventTests {
} }
} }
static class NumberHolder<T extends Number> {
private T number;
public NumberHolder(T number) {
this.number = number;
}
}
} }