Skip to content

Commit c733ae0

Browse files
committed
Fix ApplicationListenerMethodAdapter#supportsEventType check
This commit fixes the check by avoiding a fallback to eventType's hasUnresolvableGenerics(). This could previously lead to checking a generic event type `A<T>` against a listener which accepts unrelated `B` and return `true` despite the inconsistency. Note that this wouldn't necessarily surface to the user because there is a `catch (ClassCastException e)` down the line, which was primarily put in place to deal with lambda-based listeners but happens to catch an exception thrown due to the bad result of `supportsEventType`. The `supportsEventType` now matches generic `PayloadApplicationEvent` types with a raw counterpart, using the above fallback only in that case and otherwise ultimately returning `false`. Closes gh-30399
1 parent aefcb9d commit c733ae0

File tree

2 files changed

+78
-2
lines changed

2 files changed

+78
-2
lines changed

spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,12 @@ public boolean supportsEventType(ResolvableType eventType) {
173173
}
174174
if (PayloadApplicationEvent.class.isAssignableFrom(eventType.toClass())) {
175175
ResolvableType payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric();
176-
if (declaredEventType.isAssignableFrom(payloadType)) {
176+
if (declaredEventType.isAssignableFrom(payloadType) || eventType.hasUnresolvableGenerics()) {
177177
return true;
178178
}
179179
}
180180
}
181-
return eventType.hasUnresolvableGenerics();
181+
return false;
182182
}
183183

184184
@Override

spring-context/src/test/java/org/springframework/context/event/ApplicationListenerMethodAdapterTests.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.IOException;
2020
import java.lang.reflect.Method;
2121
import java.lang.reflect.UndeclaredThrowableException;
22+
import java.util.List;
2223

2324
import org.junit.jupiter.api.Test;
2425

@@ -325,6 +326,77 @@ public void beanInstanceRetrievedAtEveryInvocation() {
325326
verify(this.context, times(2)).getBean("testBean");
326327
}
327328

329+
// see https://github.com/spring-projects/spring-framework/issues/30399
330+
@Test
331+
void simplePayloadDoesNotSupportArbitraryGenericEventType() throws Exception {
332+
var method = SampleEvents.class.getDeclaredMethod("handleString", String.class);
333+
var adapter = new ApplicationListenerMethodAdapter(null, ApplicationListenerMethodAdapterTests.class, method);
334+
335+
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(EntityWrapper.class, Integer.class)))
336+
.as("handleString(String) with EntityWrapper<Integer>").isFalse();
337+
assertThat(adapter.supportsEventType(ResolvableType.forClass(EntityWrapper.class)))
338+
.as("handleString(String) with EntityWrapper<?>").isFalse();
339+
assertThat(adapter.supportsEventType(ResolvableType.forClass(String.class)))
340+
.as("handleString(String) with String").isTrue();
341+
}
342+
343+
// see https://github.com/spring-projects/spring-framework/issues/30399
344+
@Test
345+
void genericPayloadDoesNotSupportArbitraryGenericEventType() throws Exception {
346+
var method = SampleEvents.class.getDeclaredMethod("handleGenericStringPayload", EntityWrapper.class);
347+
var adapter = new ApplicationListenerMethodAdapter(null, ApplicationListenerMethodAdapterTests.class, method);
348+
349+
assertThat(adapter.supportsEventType(ResolvableType.forClass(EntityWrapper.class)))
350+
.as("handleGenericStringPayload(EntityWrapper<String>) with EntityWrapper<?>").isFalse();
351+
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(EntityWrapper.class, Integer.class)))
352+
.as("handleGenericStringPayload(EntityWrapper<String>) with EntityWrapper<Integer>").isFalse();
353+
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(EntityWrapper.class, String.class)))
354+
.as("handleGenericStringPayload(EntityWrapper<String>) with EntityWrapper<String>").isTrue();
355+
}
356+
357+
// see https://github.com/spring-projects/spring-framework/issues/30399
358+
@Test
359+
void rawGenericPayloadDoesNotSupportArbitraryGenericEventType() throws Exception {
360+
var method = SampleEvents.class.getDeclaredMethod("handleGenericAnyPayload", EntityWrapper.class);
361+
var adapter = new ApplicationListenerMethodAdapter(null, ApplicationListenerMethodAdapterTests.class, method);
362+
363+
assertThat(adapter.supportsEventType(ResolvableType.forClass(EntityWrapper.class)))
364+
.as("handleGenericAnyPayload(EntityWrapper<?>) with EntityWrapper<?>").isTrue();
365+
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(EntityWrapper.class, Integer.class)))
366+
.as("handleGenericAnyPayload(EntityWrapper<?>) with EntityWrapper<Integer>").isTrue();
367+
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(EntityWrapper.class, String.class)))
368+
.as("handleGenericAnyPayload(EntityWrapper<?>) with EntityWrapper<String>").isTrue();
369+
assertThat(adapter.supportsEventType(ResolvableType.forClass(List.class)))
370+
.as("handleGenericAnyPayload(EntityWrapper<?>) with List<?>").isFalse();
371+
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(List.class, String.class)))
372+
.as("handleGenericAnyPayload(EntityWrapper<?>) with List<String>").isFalse();
373+
}
374+
375+
@Test
376+
void genericApplicationEventSupportsSpecificType() throws Exception {
377+
var method = SampleEvents.class.getDeclaredMethod("handleGenericString", GenericTestEvent.class);
378+
var adapter = new ApplicationListenerMethodAdapter(null, ApplicationListenerMethodAdapterTests.class, method);
379+
380+
assertThat(adapter.supportsEventType(ResolvableType.forClass(GenericTestEvent.class)))
381+
.as("handleGenericString(GenericTestEvent<String>) with GenericTestEvent<?>").isFalse();
382+
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(GenericTestEvent.class, Integer.class)))
383+
.as("handleGenericString(GenericTestEvent<String>) with GenericTestEvent<Integer>").isFalse();
384+
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(GenericTestEvent.class, String.class)))
385+
.as("handleGenericString(GenericTestEvent<String>) with GenericTestEvent<String>").isTrue();
386+
}
387+
388+
@Test
389+
void genericRawApplicationEventSupportsRawTypeAndAnySpecificType() throws Exception {
390+
var method = SampleEvents.class.getDeclaredMethod("handleGenericRaw", GenericTestEvent.class);
391+
var adapter = new ApplicationListenerMethodAdapter(null, ApplicationListenerMethodAdapterTests.class, method);
392+
393+
assertThat(adapter.supportsEventType(ResolvableType.forClass(GenericTestEvent.class)))
394+
.as("handleGenericRaw(GenericTestEvent<?>) with GenericTestEvent<?>").isTrue();
395+
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(GenericTestEvent.class, String.class)))
396+
.as("handleGenericRaw(GenericTestEvent<?>) with GenericTestEvent<String>").isTrue();
397+
assertThat(adapter.supportsEventType(ResolvableType.forClassWithGenerics(GenericTestEvent.class, Integer.class)))
398+
.as("handleGenericRaw(GenericTestEvent<?>) with GenericTestEvent<Integer>").isTrue();
399+
}
328400

329401
private void supportsEventType(boolean match, Method method, ResolvableType eventType) {
330402
ApplicationListenerMethodAdapter adapter = createTestInstance(method);
@@ -373,6 +445,10 @@ public void handleRaw(ApplicationEvent event) {
373445
public void handleGenericString(GenericTestEvent<String> event) {
374446
}
375447

448+
@EventListener
449+
public void handleGenericRaw(GenericTestEvent<?> event) {
450+
}
451+
376452
@EventListener
377453
public void handleString(String payload) {
378454
}

0 commit comments

Comments
 (0)