From 1f78ab8aa088b8647544fbe3e10c747e775ea291 Mon Sep 17 00:00:00 2001 From: guanchengang <1620522809@qq.com> Date: Sat, 9 Aug 2025 14:57:46 +0800 Subject: [PATCH] optimize TypeParameterResolver --- .../reflection/TypeParameterResolver.java | 74 +++++++++++++++---- .../reflection/TypeParameterResolverTest.java | 37 +++++++++- 2 files changed, 97 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/apache/ibatis/reflection/TypeParameterResolver.java b/src/main/java/org/apache/ibatis/reflection/TypeParameterResolver.java index aba6a7f3e4d..f9d56b60136 100644 --- a/src/main/java/org/apache/ibatis/reflection/TypeParameterResolver.java +++ b/src/main/java/org/apache/ibatis/reflection/TypeParameterResolver.java @@ -129,9 +129,10 @@ private static Type resolveGenericArrayType(GenericArrayType genericArrayType, T private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType, Class declaringClass) { Class rawType = (Class) parameterizedType.getRawType(); + Type ownerType = parameterizedType.getOwnerType(); Type[] typeArgs = parameterizedType.getActualTypeArguments(); Type[] args = resolveTypes(typeArgs, srcType, declaringClass); - return new ParameterizedTypeImpl(rawType, null, args); + return new ParameterizedTypeImpl(rawType, ownerType, args); } private static Type resolveWildcardType(WildcardType wildcardType, Type srcType, Class declaringClass) { @@ -158,7 +159,7 @@ private static Type resolveTypeVar(TypeVariable typeVar, Type srcType, Class< } } else { throw new IllegalArgumentException( - "The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass()); + "The srcType(2nd arg) must be Class or ParameterizedType, but was: " + srcType.getClass()); } if (clazz == declaringClass) { @@ -198,7 +199,7 @@ private static Type scanSuperTypes(TypeVariable typeVar, Type srcType, Class< for (int i = 0; i < parentTypeVars.length; i++) { if (typeVar.equals(parentTypeVars[i])) { Type actualType = parentAsType.getActualTypeArguments()[i]; - return actualType instanceof TypeVariable ? Object.class : actualType; + return actualType instanceof TypeVariable ? ((TypeVariable) actualType).getBounds()[0] : actualType; } } } @@ -230,7 +231,40 @@ private static ParameterizedType translateParentTypeVars(ParameterizedType srcTy newParentArgs[i] = parentTypeArgs[i]; } } - return noChange ? parentType : new ParameterizedTypeImpl((Class) parentType.getRawType(), null, newParentArgs); + if (noChange && !(parentType instanceof ParameterizedTypeImpl)) noChange = false; + return noChange + ? parentType + : new ParameterizedTypeImpl((Class) parentType.getRawType(), parentType.getOwnerType(), newParentArgs); + } + + private static Type canonicalize(Type type) { + if (type instanceof ParameterizedType) { + ParameterizedType p = (ParameterizedType) type; + return new ParameterizedTypeImpl((Class) p.getRawType(), p.getOwnerType(), p.getActualTypeArguments()); + } + else if (type instanceof GenericArrayType) { + GenericArrayType g = (GenericArrayType) type; + return new GenericArrayTypeImpl(g.getGenericComponentType()); + } + else if (type instanceof WildcardType) { + WildcardType w = (WildcardType) type; + return new WildcardTypeImpl(w.getLowerBounds(), w.getUpperBounds()); + } + else { + return type; + } + } + + private static Type[] canonicalizeTypes(Type[] types) { + if (types == null || types.length == 0) { + return new Type[0]; + } + int length = types.length; + Type[] canonicalizedTypes = new Type[length]; + for (int i = 0; i < length; i++) { + canonicalizedTypes[i] = canonicalize(types[i]); + } + return canonicalizedTypes; } private TypeParameterResolver() { @@ -244,11 +278,11 @@ static class ParameterizedTypeImpl implements ParameterizedType { private final Type[] actualTypeArguments; - public ParameterizedTypeImpl(Class rawType, Type ownerType, Type[] actualTypeArguments) { + ParameterizedTypeImpl(Class rawType, Type ownerType, Type[] actualTypeArguments) { super(); - this.rawType = rawType; - this.ownerType = ownerType; - this.actualTypeArguments = actualTypeArguments; + this.rawType = (Class) canonicalize(rawType); + this.ownerType = canonicalize(ownerType); + this.actualTypeArguments = canonicalizeTypes(actualTypeArguments); } @Override @@ -283,7 +317,21 @@ public boolean equals(Object obj) { @Override public String toString() { - StringBuilder s = new StringBuilder().append(rawType.getName()).append("<"); + StringBuilder s = new StringBuilder(); + if (ownerType != null) { + s.append(ownerType.getTypeName()).append("$"); + if (ownerType instanceof ParameterizedTypeImpl) { + // remove prefixes that do not contain generic information + s.append( + rawType.getName().replace(((ParameterizedTypeImpl) ownerType).rawType.getName() + "$", "")); + } + else { + s.append(rawType.getSimpleName()); + } + } else { + s.append(rawType.getName()); + } + s.append("<"); for (int i = 0; i < actualTypeArguments.length; i++) { if (i > 0) { s.append(", "); @@ -301,8 +349,8 @@ static class WildcardTypeImpl implements WildcardType { WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) { super(); - this.lowerBounds = lowerBounds; - this.upperBounds = upperBounds; + this.lowerBounds = canonicalizeTypes(lowerBounds); + this.upperBounds = canonicalizeTypes(upperBounds); } @Override @@ -353,7 +401,7 @@ static class GenericArrayTypeImpl implements GenericArrayType { GenericArrayTypeImpl(Type genericComponentType) { super(); - this.genericComponentType = genericComponentType; + this.genericComponentType = canonicalize(genericComponentType); } @Override @@ -380,7 +428,7 @@ public boolean equals(Object obj) { @Override public String toString() { - return new StringBuilder().append(genericComponentType.toString()).append("[]").toString(); + return new StringBuilder().append(genericComponentType.getTypeName()).append("[]").toString(); } } } diff --git a/src/test/java/org/apache/ibatis/reflection/TypeParameterResolverTest.java b/src/test/java/org/apache/ibatis/reflection/TypeParameterResolverTest.java index ca4bb535570..d0599aa335b 100644 --- a/src/test/java/org/apache/ibatis/reflection/TypeParameterResolverTest.java +++ b/src/test/java/org/apache/ibatis/reflection/TypeParameterResolverTest.java @@ -18,6 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; @@ -230,7 +231,6 @@ void returnLv2ArrayOfArray() throws Exception { Type result = TypeParameterResolver.resolveReturnType(method, clazz); assertTrue(result instanceof Class); Class resultClass = (Class) result; - assertTrue(result instanceof Class); assertTrue(resultClass.isArray()); assertTrue(resultClass.getComponentType().isArray()); assertEquals(String.class, resultClass.getComponentType().getComponentType()); @@ -513,6 +513,41 @@ void shouldResolveInterfaceTypeParamsDeeper() { assertEquals(String.class, types[0]); } + @Test + void shouldResolveWildcardTypeInClassDefinition() throws Exception { + class A { + public T foo(T t) { + return t; + } + } + + class B extends A { + + } + + class C1 extends B { + + } + + class C2 extends B { + + } + + class C3 extends B { + + } + + Method m = A.class.getMethod("foo", Serializable.class); + + assertEquals(Integer.class, TypeParameterResolver.resolveReturnType(m, C1.class)); + assertEquals(Integer.class, TypeParameterResolver.resolveReturnType(m, C2.class)); + assertEquals(Number.class, TypeParameterResolver.resolveReturnType(m, C3.class)); + + assertEquals(Integer.class, TypeParameterResolver.resolveParamTypes(m, C1.class)[0]); + assertEquals(Integer.class, TypeParameterResolver.resolveParamTypes(m, C2.class)[0]); + assertEquals(Number.class, TypeParameterResolver.resolveParamTypes(m, C3.class)[0]); + } + class AA { }