Improve GenericTypeResolver to resolve type variable recursively

Fix GH-24963
This commit is contained in:
Yanming Zhou 2023-03-08 12:55:49 +08:00 committed by Juergen Hoeller
parent 4b2e40153a
commit e788aeb25b
5 changed files with 45 additions and 8 deletions

View File

@ -67,6 +67,7 @@ import org.springframework.util.StringUtils;
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen * @author Sam Brannen
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Yanming Zhou
* @since 4.2 * @since 4.2
*/ */
public class ApplicationListenerMethodAdapter implements GenericApplicationListener { public class ApplicationListenerMethodAdapter implements GenericApplicationListener {
@ -177,13 +178,14 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
return true; return true;
} }
if (PayloadApplicationEvent.class.isAssignableFrom(eventType.toClass())) { if (PayloadApplicationEvent.class.isAssignableFrom(eventType.toClass())) {
if (eventType.hasUnresolvableGenerics()) {
return true;
}
ResolvableType payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric(); ResolvableType payloadType = eventType.as(PayloadApplicationEvent.class).getGeneric();
if (declaredEventType.isAssignableFrom(payloadType)) { if (declaredEventType.isAssignableFrom(payloadType)) {
return true; return true;
} }
if (payloadType.resolve() == null) {
// Always accept such event when the type is erased
return true;
}
} }
} }
return false; return false;

View File

@ -39,6 +39,7 @@ import org.springframework.util.ConcurrentReferenceHashMap;
* @author Rob Harrop * @author Rob Harrop
* @author Sam Brannen * @author Sam Brannen
* @author Phillip Webb * @author Phillip Webb
* @author Yanming Zhou
* @since 2.5.2 * @since 2.5.2
*/ */
public final class GenericTypeResolver { public final class GenericTypeResolver {
@ -167,7 +168,7 @@ public final class GenericTypeResolver {
else if (genericType instanceof ParameterizedType parameterizedType) { else if (genericType instanceof ParameterizedType parameterizedType) {
ResolvableType resolvedType = ResolvableType.forType(genericType); ResolvableType resolvedType = ResolvableType.forType(genericType);
if (resolvedType.hasUnresolvableGenerics()) { if (resolvedType.hasUnresolvableGenerics()) {
Class<?>[] generics = new Class<?>[parameterizedType.getActualTypeArguments().length]; ResolvableType[] generics = new ResolvableType[parameterizedType.getActualTypeArguments().length];
Type[] typeArguments = parameterizedType.getActualTypeArguments(); Type[] typeArguments = parameterizedType.getActualTypeArguments();
ResolvableType contextType = ResolvableType.forClass(contextClass); ResolvableType contextType = ResolvableType.forClass(contextClass);
for (int i = 0; i < typeArguments.length; i++) { for (int i = 0; i < typeArguments.length; i++) {
@ -175,14 +176,17 @@ public final class GenericTypeResolver {
if (typeArgument instanceof TypeVariable<?> typeVariable) { if (typeArgument instanceof TypeVariable<?> typeVariable) {
ResolvableType resolvedTypeArgument = resolveVariable(typeVariable, contextType); ResolvableType resolvedTypeArgument = resolveVariable(typeVariable, contextType);
if (resolvedTypeArgument != ResolvableType.NONE) { if (resolvedTypeArgument != ResolvableType.NONE) {
generics[i] = resolvedTypeArgument.resolve(); generics[i] = resolvedTypeArgument;
} }
else { else {
generics[i] = ResolvableType.forType(typeArgument).resolve(); generics[i] = ResolvableType.forType(typeArgument);
} }
} }
else if (typeArgument instanceof ParameterizedType) {
generics[i] = ResolvableType.forType(resolveType(typeArgument, contextClass));
}
else { else {
generics[i] = ResolvableType.forType(typeArgument).resolve(); generics[i] = ResolvableType.forType(typeArgument);
} }
} }
Class<?> rawClass = resolvedType.getRawClass(); Class<?> rawClass = resolvedType.getRawClass();

View File

@ -70,6 +70,7 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb * @author Phillip Webb
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Yanming Zhou
* @since 4.0 * @since 4.0
* @see #forField(Field) * @see #forField(Field)
* @see #forMethodParameter(Method, int) * @see #forMethodParameter(Method, int)
@ -572,7 +573,7 @@ public class ResolvableType implements Serializable {
private boolean determineUnresolvableGenerics() { private boolean determineUnresolvableGenerics() {
ResolvableType[] generics = getGenerics(); ResolvableType[] generics = getGenerics();
for (ResolvableType generic : generics) { for (ResolvableType generic : generics) {
if (generic.isUnresolvableTypeVariable() || generic.isWildcardWithoutBounds()) { if (generic.isUnresolvableTypeVariable() || generic.isWildcardWithoutBounds() || generic.hasUnresolvableGenerics()) {
return true; return true;
} }
} }

View File

@ -39,6 +39,7 @@ import static org.springframework.util.ReflectionUtils.findMethod;
* @author Sam Brannen * @author Sam Brannen
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Yanming Zhou
*/ */
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
class GenericTypeResolverTests { class GenericTypeResolverTests {
@ -203,6 +204,13 @@ class GenericTypeResolverTests {
assertThat(resolvedType).isEqualTo(reference.getType()); assertThat(resolvedType).isEqualTo(reference.getType());
} }
@Test
void resolveNestedTypeVariable() throws Exception {
Type resolved = resolveType(ListOfListSupplier.class.getMethod("get").getGenericReturnType(),
StringListOfListSupplier.class);
assertThat(ResolvableType.forType(resolved).getGeneric(0).getGeneric(0).resolve()).isEqualTo(String.class);
}
private static Method method(Class<?> target, String methodName, Class<?>... parameterTypes) { private static Method method(Class<?> target, String methodName, Class<?>... parameterTypes) {
Method method = findMethod(target, methodName, parameterTypes); Method method = findMethod(target, methodName, parameterTypes);
assertThat(method).describedAs(target.getName() + "#" + methodName).isNotNull(); assertThat(method).describedAs(target.getName() + "#" + methodName).isNotNull();
@ -382,5 +390,14 @@ class GenericTypeResolverTests {
} }
} }
public interface ListOfListSupplier<T> {
List<List<T>> get();
}
public interface StringListOfListSupplier extends ListOfListSupplier<String> {
}
} }

View File

@ -66,6 +66,7 @@ import static org.mockito.Mockito.verify;
* @author Phillip Webb * @author Phillip Webb
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Yanming Zhou
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@ -1314,6 +1315,12 @@ class ResolvableTypeTests {
assertThat(type.hasUnresolvableGenerics()).isTrue(); assertThat(type.hasUnresolvableGenerics()).isTrue();
} }
@Test
void hasUnresolvableGenericsWhenNested() throws Exception {
ResolvableType type = ResolvableType.forMethodReturnType(ListOfListSupplier.class.getMethod("get"));
assertThat(type.hasUnresolvableGenerics()).isTrue();
}
@Test @Test
void spr11219() throws Exception { void spr11219() throws Exception {
ResolvableType type = ResolvableType.forField(BaseProvider.class.getField("stuff"), BaseProvider.class); ResolvableType type = ResolvableType.forField(BaseProvider.class.getField("stuff"), BaseProvider.class);
@ -1617,6 +1624,12 @@ class ResolvableTypeTests {
} }
public interface ListOfListSupplier<T> {
List<List<T>> get();
}
static class EnclosedInParameterizedType<T> { static class EnclosedInParameterizedType<T> {
static class InnerRaw { static class InnerRaw {