All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.core.GenericTypeResolver Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.core;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.springframework.util.Assert;
import org.springframework.util.ConcurrentReferenceHashMap;

/**
 * Helper class for resolving generic types against type variables.
 *
 * 

Mainly intended for usage within the framework, resolving method * parameter types even when they are declared generically. * * @author Juergen Hoeller * @author Rob Harrop * @author Sam Brannen * @author Phillip Webb * @since 2.5.2 */ public abstract class GenericTypeResolver { /** Cache from Class to TypeVariable Map */ @SuppressWarnings("rawtypes") private static final Map, Map> typeVariableCache = new ConcurrentReferenceHashMap, Map>(); /** * Determine the target type for the given parameter specification. * @param methodParameter the method parameter specification * @return the corresponding generic parameter type * @deprecated as of Spring 4.0, use {@link MethodParameter#getGenericParameterType()} */ @Deprecated public static Type getTargetType(MethodParameter methodParameter) { Assert.notNull(methodParameter, "MethodParameter must not be null"); return methodParameter.getGenericParameterType(); } /** * Determine the target type for the given generic parameter type. * @param methodParameter the method parameter specification * @param implementationClass the class to resolve type variables against * @return the corresponding generic parameter or return type */ public static Class resolveParameterType(MethodParameter methodParameter, Class implementationClass) { Assert.notNull(methodParameter, "MethodParameter must not be null"); Assert.notNull(implementationClass, "Class must not be null"); methodParameter.setContainingClass(implementationClass); ResolvableType.resolveMethodParameter(methodParameter); return methodParameter.getParameterType(); } /** * Determine the target type for the generic return type of the given method, * where formal type variables are declared on the given class. * @param method the method to introspect * @param clazz the class to resolve type variables against * @return the corresponding generic parameter or return type */ public static Class resolveReturnType(Method method, Class clazz) { Assert.notNull(method, "Method must not be null"); Assert.notNull(clazz, "Class must not be null"); return ResolvableType.forMethodReturnType(method, clazz).resolve(method.getReturnType()); } /** * Determine the target type for the generic return type of the given * generic method, where formal type variables are declared on * the given method itself. *

For example, given a factory method with the following signature, * if {@code resolveReturnTypeForGenericMethod()} is invoked with the reflected * method for {@code creatProxy()} and an {@code Object[]} array containing * {@code MyService.class}, {@code resolveReturnTypeForGenericMethod()} will * infer that the target return type is {@code MyService}. *

{@code public static  T createProxy(Class clazz)}
*

Possible Return Values

*
    *
  • the target return type, if it can be inferred
  • *
  • the {@linkplain Method#getReturnType() standard return type}, if * the given {@code method} does not declare any {@linkplain * Method#getTypeParameters() formal type variables}
  • *
  • the {@linkplain Method#getReturnType() standard return type}, if the * target return type cannot be inferred (e.g., due to type erasure)
  • *
  • {@code null}, if the length of the given arguments array is shorter * than the length of the {@linkplain * Method#getGenericParameterTypes() formal argument list} for the given * method
  • *
* @param method the method to introspect, never {@code null} * @param args the arguments that will be supplied to the method when it is * invoked (never {@code null}) * @param classLoader the ClassLoader to resolve class names against, if necessary * (may be {@code null}) * @return the resolved target return type, the standard return type, or {@code null} * @since 3.2.5 * @deprecated as of Spring Framework 4.3.8, superseded by {@link ResolvableType} usage */ @Deprecated public static Class resolveReturnTypeForGenericMethod(Method method, Object[] args, ClassLoader classLoader) { Assert.notNull(method, "Method must not be null"); Assert.notNull(args, "Argument array must not be null"); TypeVariable[] declaredTypeVariables = method.getTypeParameters(); Type genericReturnType = method.getGenericReturnType(); Type[] methodArgumentTypes = method.getGenericParameterTypes(); // No declared type variables to inspect, so just return the standard return type. if (declaredTypeVariables.length == 0) { return method.getReturnType(); } // The supplied argument list is too short for the method's signature, so // return null, since such a method invocation would fail. if (args.length < methodArgumentTypes.length) { return null; } // Ensure that the type variable (e.g., T) is declared directly on the method // itself (e.g., via ), not on the enclosing class or interface. boolean locallyDeclaredTypeVariableMatchesReturnType = false; for (TypeVariable currentTypeVariable : declaredTypeVariables) { if (currentTypeVariable.equals(genericReturnType)) { locallyDeclaredTypeVariableMatchesReturnType = true; break; } } if (locallyDeclaredTypeVariableMatchesReturnType) { for (int i = 0; i < methodArgumentTypes.length; i++) { Type currentMethodArgumentType = methodArgumentTypes[i]; if (currentMethodArgumentType.equals(genericReturnType)) { return args[i].getClass(); } if (currentMethodArgumentType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) currentMethodArgumentType; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type typeArg : actualTypeArguments) { if (typeArg.equals(genericReturnType)) { Object arg = args[i]; if (arg instanceof Class) { return (Class) arg; } else if (arg instanceof String && classLoader != null) { try { return classLoader.loadClass((String) arg); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Could not resolve specific class name argument [" + arg + "]", ex); } } else { // Consider adding logic to determine the class of the typeArg, if possible. // For now, just fall back... return method.getReturnType(); } } } } } } // Fall back... return method.getReturnType(); } /** * Resolve the single type argument of the given generic interface against the given * target method which is assumed to return the given interface or an implementation * of it. * @param method the target method to check the return type of * @param genericIfc the generic interface or superclass to resolve the type argument from * @return the resolved parameter type of the method return type, or {@code null} * if not resolvable or if the single argument is of type {@link WildcardType}. */ public static Class resolveReturnTypeArgument(Method method, Class genericIfc) { Assert.notNull(method, "Method must not be null"); ResolvableType resolvableType = ResolvableType.forMethodReturnType(method).as(genericIfc); if (!resolvableType.hasGenerics() || resolvableType.getType() instanceof WildcardType) { return null; } return getSingleGeneric(resolvableType); } /** * Resolve the single type argument of the given generic interface against * the given target class which is assumed to implement the generic interface * and possibly declare a concrete type for its type variable. * @param clazz the target class to check against * @param genericIfc the generic interface or superclass to resolve the type argument from * @return the resolved type of the argument, or {@code null} if not resolvable */ public static Class resolveTypeArgument(Class clazz, Class genericIfc) { ResolvableType resolvableType = ResolvableType.forClass(clazz).as(genericIfc); if (!resolvableType.hasGenerics()) { return null; } return getSingleGeneric(resolvableType); } private static Class getSingleGeneric(ResolvableType resolvableType) { if (resolvableType.getGenerics().length > 1) { throw new IllegalArgumentException("Expected 1 type argument on generic interface [" + resolvableType + "] but found " + resolvableType.getGenerics().length); } return resolvableType.getGeneric().resolve(); } /** * Resolve the type arguments of the given generic interface against the given * target class which is assumed to implement the generic interface and possibly * declare concrete types for its type variables. * @param clazz the target class to check against * @param genericIfc the generic interface or superclass to resolve the type argument from * @return the resolved type of each argument, with the array size matching the * number of actual type arguments, or {@code null} if not resolvable */ public static Class[] resolveTypeArguments(Class clazz, Class genericIfc) { ResolvableType type = ResolvableType.forClass(clazz).as(genericIfc); if (!type.hasGenerics() || type.isEntirelyUnresolvable()) { return null; } return type.resolveGenerics(Object.class); } /** * Resolve the specified generic type against the given TypeVariable map. *

Used by Spring Data. * @param genericType the generic type to resolve * @param map the TypeVariable Map to resolved against * @return the type if it resolves to a Class, or {@code Object.class} otherwise */ @SuppressWarnings("rawtypes") public static Class resolveType(Type genericType, Map map) { return ResolvableType.forType(genericType, new TypeVariableMapVariableResolver(map)).resolve(Object.class); } /** * Build a mapping of {@link TypeVariable#getName TypeVariable names} to * {@link Class concrete classes} for the specified {@link Class}. * Searches all super types, enclosing types and interfaces. * @see #resolveType(Type, Map) */ @SuppressWarnings("rawtypes") public static Map getTypeVariableMap(Class clazz) { Map typeVariableMap = typeVariableCache.get(clazz); if (typeVariableMap == null) { typeVariableMap = new HashMap(); buildTypeVariableMap(ResolvableType.forClass(clazz), typeVariableMap); typeVariableCache.put(clazz, Collections.unmodifiableMap(typeVariableMap)); } return typeVariableMap; } @SuppressWarnings("rawtypes") private static void buildTypeVariableMap(ResolvableType type, Map typeVariableMap) { if (type != ResolvableType.NONE) { if (type.getType() instanceof ParameterizedType) { TypeVariable[] variables = type.resolve().getTypeParameters(); for (int i = 0; i < variables.length; i++) { ResolvableType generic = type.getGeneric(i); while (generic.getType() instanceof TypeVariable) { generic = generic.resolveType(); } if (generic != ResolvableType.NONE) { typeVariableMap.put(variables[i], generic.getType()); } } } buildTypeVariableMap(type.getSuperType(), typeVariableMap); for (ResolvableType interfaceType : type.getInterfaces()) { buildTypeVariableMap(interfaceType, typeVariableMap); } if (type.resolve().isMemberClass()) { buildTypeVariableMap(ResolvableType.forClass(type.resolve().getEnclosingClass()), typeVariableMap); } } } @SuppressWarnings({"serial", "rawtypes"}) private static class TypeVariableMapVariableResolver implements ResolvableType.VariableResolver { private final Map typeVariableMap; public TypeVariableMapVariableResolver(Map typeVariableMap) { this.typeVariableMap = typeVariableMap; } @Override public ResolvableType resolveVariable(TypeVariable variable) { Type type = this.typeVariableMap.get(variable); return (type != null ? ResolvableType.forType(type) : null); } @Override public Object getSource() { return this.typeVariableMap; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy