21
21
import java .lang .reflect .Type ;
22
22
import java .lang .reflect .TypeVariable ;
23
23
import java .lang .reflect .WildcardType ;
24
- import java .util .Collections ;
25
- import java .util .HashMap ;
26
- import java .util .Map ;
27
24
28
25
import org .springframework .util .Assert ;
29
- import org .springframework .util .ConcurrentReferenceHashMap ;
30
26
31
27
/**
32
28
* Helper class for resolving generic types against type variables.
42
38
*/
43
39
public abstract class GenericTypeResolver {
44
40
45
- /** Cache from Class to TypeVariable Map */
46
- @ SuppressWarnings ("rawtypes" )
47
- private static final Map <Class <?>, Map <TypeVariable , Type >> typeVariableCache =
48
- new ConcurrentReferenceHashMap <>();
49
-
50
-
51
- /**
52
- * Determine the target type for the given parameter specification.
53
- * @param methodParameter the method parameter specification
54
- * @return the corresponding generic parameter type
55
- * @deprecated as of Spring 4.0, use {@link MethodParameter#getGenericParameterType()}
56
- */
57
- @ Deprecated
58
- public static Type getTargetType (MethodParameter methodParameter ) {
59
- Assert .notNull (methodParameter , "MethodParameter must not be null" );
60
- return methodParameter .getGenericParameterType ();
61
- }
62
-
63
41
/**
64
42
* Determine the target type for the given generic parameter type.
65
43
* @param methodParameter the method parameter specification
@@ -80,114 +58,13 @@ public static Class<?> resolveParameterType(MethodParameter methodParameter, Cla
80
58
* @param method the method to introspect
81
59
* @param clazz the class to resolve type variables against
82
60
* @return the corresponding generic parameter or return type
83
- * @see #resolveReturnTypeForGenericMethod
84
61
*/
85
62
public static Class <?> resolveReturnType (Method method , Class <?> clazz ) {
86
63
Assert .notNull (method , "Method must not be null" );
87
64
Assert .notNull (clazz , "Class must not be null" );
88
65
return ResolvableType .forMethodReturnType (method , clazz ).resolve (method .getReturnType ());
89
66
}
90
67
91
- /**
92
- * Determine the target type for the generic return type of the given
93
- * <em>generic method</em>, where formal type variables are declared on
94
- * the given method itself.
95
- * <p>For example, given a factory method with the following signature,
96
- * if {@code resolveReturnTypeForGenericMethod()} is invoked with the reflected
97
- * method for {@code creatProxy()} and an {@code Object[]} array containing
98
- * {@code MyService.class}, {@code resolveReturnTypeForGenericMethod()} will
99
- * infer that the target return type is {@code MyService}.
100
- * <pre class="code">{@code public static <T> T createProxy(Class<T> clazz)}</pre>
101
- * <h4>Possible Return Values</h4>
102
- * <ul>
103
- * <li>the target return type, if it can be inferred</li>
104
- * <li>the {@linkplain Method#getReturnType() standard return type}, if
105
- * the given {@code method} does not declare any {@linkplain
106
- * Method#getTypeParameters() formal type variables}</li>
107
- * <li>the {@linkplain Method#getReturnType() standard return type}, if the
108
- * target return type cannot be inferred (e.g., due to type erasure)</li>
109
- * <li>{@code null}, if the length of the given arguments array is shorter
110
- * than the length of the {@linkplain
111
- * Method#getGenericParameterTypes() formal argument list} for the given
112
- * method</li>
113
- * </ul>
114
- * @param method the method to introspect, never {@code null}
115
- * @param args the arguments that will be supplied to the method when it is
116
- * invoked (never {@code null})
117
- * @param classLoader the ClassLoader to resolve class names against, if necessary
118
- * (may be {@code null})
119
- * @return the resolved target return type, the standard return type, or {@code null}
120
- * @since 3.2.5
121
- * @see #resolveReturnType
122
- */
123
- public static Class <?> resolveReturnTypeForGenericMethod (Method method , Object [] args , ClassLoader classLoader ) {
124
- Assert .notNull (method , "Method must not be null" );
125
- Assert .notNull (args , "Argument array must not be null" );
126
-
127
- TypeVariable <Method >[] declaredTypeVariables = method .getTypeParameters ();
128
- Type genericReturnType = method .getGenericReturnType ();
129
- Type [] methodArgumentTypes = method .getGenericParameterTypes ();
130
-
131
- // No declared type variables to inspect, so just return the standard return type.
132
- if (declaredTypeVariables .length == 0 ) {
133
- return method .getReturnType ();
134
- }
135
-
136
- // The supplied argument list is too short for the method's signature, so
137
- // return null, since such a method invocation would fail.
138
- if (args .length < methodArgumentTypes .length ) {
139
- return null ;
140
- }
141
-
142
- // Ensure that the type variable (e.g., T) is declared directly on the method
143
- // itself (e.g., via <T>), not on the enclosing class or interface.
144
- boolean locallyDeclaredTypeVariableMatchesReturnType = false ;
145
- for (TypeVariable <Method > currentTypeVariable : declaredTypeVariables ) {
146
- if (currentTypeVariable .equals (genericReturnType )) {
147
- locallyDeclaredTypeVariableMatchesReturnType = true ;
148
- break ;
149
- }
150
- }
151
-
152
- if (locallyDeclaredTypeVariableMatchesReturnType ) {
153
- for (int i = 0 ; i < methodArgumentTypes .length ; i ++) {
154
- Type currentMethodArgumentType = methodArgumentTypes [i ];
155
- if (currentMethodArgumentType .equals (genericReturnType )) {
156
- return args [i ].getClass ();
157
- }
158
- if (currentMethodArgumentType instanceof ParameterizedType ) {
159
- ParameterizedType parameterizedType = (ParameterizedType ) currentMethodArgumentType ;
160
- Type [] actualTypeArguments = parameterizedType .getActualTypeArguments ();
161
- for (Type typeArg : actualTypeArguments ) {
162
- if (typeArg .equals (genericReturnType )) {
163
- Object arg = args [i ];
164
- if (arg instanceof Class ) {
165
- return (Class <?>) arg ;
166
- }
167
- else if (arg instanceof String && classLoader != null ) {
168
- try {
169
- return classLoader .loadClass ((String ) arg );
170
- }
171
- catch (ClassNotFoundException ex ) {
172
- throw new IllegalStateException (
173
- "Could not resolve specific class name argument [" + arg + "]" , ex );
174
- }
175
- }
176
- else {
177
- // Consider adding logic to determine the class of the typeArg, if possible.
178
- // For now, just fall back...
179
- return method .getReturnType ();
180
- }
181
- }
182
- }
183
- }
184
- }
185
- }
186
-
187
- // Fall back...
188
- return method .getReturnType ();
189
- }
190
-
191
68
/**
192
69
* Resolve the single type argument of the given generic interface against the given
193
70
* target method which is assumed to return the given interface or an implementation
@@ -248,81 +125,75 @@ public static Class<?>[] resolveTypeArguments(Class<?> clazz, Class<?> genericIf
248
125
}
249
126
250
127
/**
251
- * Resolve the specified generic type against the given TypeVariable map.
252
- * @param genericType the generic type to resolve
253
- * @param map the TypeVariable Map to resolved against
254
- * @return the type if it resolves to a Class, or {@code Object.class} otherwise
255
- * @deprecated as of Spring 4.0 in favor of {@link ResolvableType}
128
+ * Resolve the given generic type against the given context class,
129
+ * substituting type variables as far as possible.
130
+ * @param genericType the (potentially) generic type
131
+ * @param contextClass a context class for the target type, for example a class
132
+ * in which the target type appears in a method signature (can be {@code null})
133
+ * @return the resolved type (possibly the given generic type as-is)
134
+ * @since 5.0
256
135
*/
257
- @ Deprecated
258
- @ SuppressWarnings ("rawtypes" )
259
- public static Class <?> resolveType (Type genericType , Map <TypeVariable , Type > map ) {
260
- return ResolvableType .forType (genericType , new TypeVariableMapVariableResolver (map )).resolve (Object .class );
261
- }
262
-
263
- /**
264
- * Build a mapping of {@link TypeVariable#getName TypeVariable names} to
265
- * {@link Class concrete classes} for the specified {@link Class}. Searches
266
- * all super types, enclosing types and interfaces.
267
- * @deprecated as of Spring 4.0 in favor of {@link ResolvableType}
268
- */
269
- @ Deprecated
270
- @ SuppressWarnings ("rawtypes" )
271
- public static Map <TypeVariable , Type > getTypeVariableMap (Class <?> clazz ) {
272
- Map <TypeVariable , Type > typeVariableMap = typeVariableCache .get (clazz );
273
- if (typeVariableMap == null ) {
274
- typeVariableMap = new HashMap <>();
275
- buildTypeVariableMap (ResolvableType .forClass (clazz ), typeVariableMap );
276
- typeVariableCache .put (clazz , Collections .unmodifiableMap (typeVariableMap ));
277
- }
278
- return typeVariableMap ;
279
- }
280
-
281
- @ SuppressWarnings ("rawtypes" )
282
- private static void buildTypeVariableMap (ResolvableType type , Map <TypeVariable , Type > typeVariableMap ) {
283
- if (type != ResolvableType .NONE ) {
284
- if (type .getType () instanceof ParameterizedType ) {
285
- TypeVariable <?>[] variables = type .resolve ().getTypeParameters ();
286
- for (int i = 0 ; i < variables .length ; i ++) {
287
- ResolvableType generic = type .getGeneric (i );
288
- while (generic .getType () instanceof TypeVariable <?>) {
289
- generic = generic .resolveType ();
290
- }
291
- if (generic != ResolvableType .NONE ) {
292
- typeVariableMap .put (variables [i ], generic .getType ());
293
- }
136
+ public static Type resolveType (Type genericType , Class <?> contextClass ) {
137
+ if (contextClass != null ) {
138
+ if (genericType instanceof TypeVariable ) {
139
+ ResolvableType resolvedTypeVariable = resolveVariable (
140
+ (TypeVariable <?>) genericType , ResolvableType .forClass (contextClass ));
141
+ if (resolvedTypeVariable != ResolvableType .NONE ) {
142
+ return resolvedTypeVariable .resolve ();
294
143
}
295
144
}
296
- buildTypeVariableMap (type .getSuperType (), typeVariableMap );
297
- for (ResolvableType interfaceType : type .getInterfaces ()) {
298
- buildTypeVariableMap (interfaceType , typeVariableMap );
299
- }
300
- if (type .resolve ().isMemberClass ()) {
301
- buildTypeVariableMap (ResolvableType .forClass (type .resolve ().getEnclosingClass ()), typeVariableMap );
145
+ else if (genericType instanceof ParameterizedType ) {
146
+ ResolvableType resolvedType = ResolvableType .forType (genericType );
147
+ if (resolvedType .hasUnresolvableGenerics ()) {
148
+ ParameterizedType parameterizedType = (ParameterizedType ) genericType ;
149
+ Class <?>[] generics = new Class <?>[parameterizedType .getActualTypeArguments ().length ];
150
+ Type [] typeArguments = parameterizedType .getActualTypeArguments ();
151
+ for (int i = 0 ; i < typeArguments .length ; i ++) {
152
+ Type typeArgument = typeArguments [i ];
153
+ if (typeArgument instanceof TypeVariable ) {
154
+ ResolvableType resolvedTypeArgument = resolveVariable (
155
+ (TypeVariable <?>) typeArgument , ResolvableType .forClass (contextClass ));
156
+ if (resolvedTypeArgument != ResolvableType .NONE ) {
157
+ generics [i ] = resolvedTypeArgument .resolve ();
158
+ }
159
+ else {
160
+ generics [i ] = ResolvableType .forType (typeArgument ).resolve ();
161
+ }
162
+ }
163
+ else {
164
+ generics [i ] = ResolvableType .forType (typeArgument ).resolve ();
165
+ }
166
+ }
167
+ return ResolvableType .forClassWithGenerics (resolvedType .getRawClass (), generics ).getType ();
168
+ }
302
169
}
303
170
}
171
+ return genericType ;
304
172
}
305
173
306
-
307
- @ SuppressWarnings ({"serial" , "rawtypes" })
308
- private static class TypeVariableMapVariableResolver implements ResolvableType .VariableResolver {
309
-
310
- private final Map <TypeVariable , Type > typeVariableMap ;
311
-
312
- public TypeVariableMapVariableResolver (Map <TypeVariable , Type > typeVariableMap ) {
313
- this .typeVariableMap = typeVariableMap ;
174
+ private static ResolvableType resolveVariable (TypeVariable <?> typeVariable , ResolvableType contextType ) {
175
+ ResolvableType resolvedType ;
176
+ if (contextType .hasGenerics ()) {
177
+ resolvedType = ResolvableType .forType (typeVariable , contextType );
178
+ if (resolvedType .resolve () != null ) {
179
+ return resolvedType ;
180
+ }
314
181
}
315
182
316
- @ Override
317
- public ResolvableType resolveVariable (TypeVariable <?> variable ) {
318
- Type type = this .typeVariableMap .get (variable );
319
- return (type != null ? ResolvableType .forType (type ) : null );
183
+ ResolvableType superType = contextType .getSuperType ();
184
+ if (superType != ResolvableType .NONE ) {
185
+ resolvedType = resolveVariable (typeVariable , superType );
186
+ if (resolvedType .resolve () != null ) {
187
+ return resolvedType ;
188
+ }
320
189
}
321
-
322
- @ Override
323
- public Object getSource () {
324
- return this .typeVariableMap ;
190
+ for (ResolvableType ifc : contextType .getInterfaces ()) {
191
+ resolvedType = resolveVariable (typeVariable , ifc );
192
+ if (resolvedType .resolve () != null ) {
193
+ return resolvedType ;
194
+ }
325
195
}
196
+ return ResolvableType .NONE ;
326
197
}
327
198
328
199
}
0 commit comments