From 714c3c59eb23d3eb9969465717a0258ac2f7b6a5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 21 Jun 2023 13:15:35 +0200 Subject: [PATCH 1/2] Accept unresolvable generics as long as raw event class matches Closes gh-30712 --- .../event/ApplicationListenerMethodAdapter.java | 9 +++++++-- .../ApplicationListenerMethodAdapterTests.java | 13 +++++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java b/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java index f61a767bcc8..7079b69d4cd 100644 --- a/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java +++ b/spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java @@ -168,12 +168,17 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe @Override public boolean supportsEventType(ResolvableType eventType) { for (ResolvableType declaredEventType : this.declaredEventTypes) { - if (declaredEventType.isAssignableFrom(eventType)) { + if (eventType.hasUnresolvableGenerics() ? + declaredEventType.toClass().isAssignableFrom(eventType.toClass()) : + declaredEventType.isAssignableFrom(eventType)) { return true; } if (PayloadApplicationEvent.class.isAssignableFrom(eventType.toClass())) { + if (eventType.hasUnresolvableGenerics()) { + return true; + } ResolvableType payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric(); - if (declaredEventType.isAssignableFrom(payloadType) || eventType.hasUnresolvableGenerics()) { + if (declaredEventType.isAssignableFrom(payloadType)) { return true; } } diff --git a/spring-context/src/test/java/org/springframework/context/event/ApplicationListenerMethodAdapterTests.java b/spring-context/src/test/java/org/springframework/context/event/ApplicationListenerMethodAdapterTests.java index edc69829742..858009aabfb 100644 --- a/spring-context/src/test/java/org/springframework/context/event/ApplicationListenerMethodAdapterTests.java +++ b/spring-context/src/test/java/org/springframework/context/event/ApplicationListenerMethodAdapterTests.java @@ -47,6 +47,8 @@ import static org.mockito.Mockito.verify; /** * @author Stephane Nicoll + * @author Juergen Hoeller + * @author Simon Baslé */ public class ApplicationListenerMethodAdapterTests extends AbstractApplicationEventListenerTests { @@ -81,6 +83,13 @@ public class ApplicationListenerMethodAdapterTests extends AbstractApplicationEv supportsEventType(false, method, ResolvableType.forClassWithGenerics(GenericTestEvent.class, Long.class)); } + @Test + public void genericListenerWithUnresolvedGenerics() { + Method method = ReflectionUtils.findMethod( + SampleEvents.class, "handleGenericString", GenericTestEvent.class); + supportsEventType(true, method, ResolvableType.forClass(GenericTestEvent.class)); + } + @Test public void listenerWithPayloadAndGenericInformation() { Method method = ReflectionUtils.findMethod(SampleEvents.class, "handleString", String.class); @@ -347,7 +356,7 @@ public class ApplicationListenerMethodAdapterTests extends AbstractApplicationEv var adapter = new ApplicationListenerMethodAdapter(null, ApplicationListenerMethodAdapterTests.class, method); assertThat(adapter.supportsEventType(ResolvableType.forClass(EntityWrapper.class))) - .as("handleGenericStringPayload(EntityWrapper) with EntityWrapper").isFalse(); + .as("handleGenericStringPayload(EntityWrapper) with EntityWrapper").isTrue(); assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(EntityWrapper.class, Integer.class))) .as("handleGenericStringPayload(EntityWrapper) with EntityWrapper").isFalse(); assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(EntityWrapper.class, String.class))) @@ -378,7 +387,7 @@ public class ApplicationListenerMethodAdapterTests extends AbstractApplicationEv var adapter = new ApplicationListenerMethodAdapter(null, ApplicationListenerMethodAdapterTests.class, method); assertThat(adapter.supportsEventType(ResolvableType.forClass(GenericTestEvent.class))) - .as("handleGenericString(GenericTestEvent) with GenericTestEvent").isFalse(); + .as("handleGenericString(GenericTestEvent) with GenericTestEvent").isTrue(); assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(GenericTestEvent.class, Integer.class))) .as("handleGenericString(GenericTestEvent) with GenericTestEvent").isFalse(); assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(GenericTestEvent.class, String.class))) From 93218a06baa528401f2baeeb5e02b0582ae44d43 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 21 Jun 2023 13:16:04 +0200 Subject: [PATCH 2/2] Cache hasUnresolvableGenerics result for repeated checks Closes gh-30713 --- .../org/springframework/core/ResolvableType.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index d4ae15af06b..2562ffb9fd5 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -133,6 +133,9 @@ public class ResolvableType implements Serializable { @Nullable private volatile ResolvableType[] generics; + @Nullable + private volatile Boolean unresolvableGenerics; + /** * Private constructor used to create a new {@link ResolvableType} for cache key purposes, @@ -544,6 +547,15 @@ public class ResolvableType implements Serializable { if (this == NONE) { return false; } + Boolean unresolvableGenerics = this.unresolvableGenerics; + if (unresolvableGenerics == null) { + unresolvableGenerics = determineUnresolvableGenerics(); + this.unresolvableGenerics = unresolvableGenerics; + } + return unresolvableGenerics; + } + + private boolean determineUnresolvableGenerics() { ResolvableType[] generics = getGenerics(); for (ResolvableType generic : generics) { if (generic.isUnresolvableTypeVariable() || generic.isWildcardWithoutBounds()) {