GenericTypeResolver defensively calls Class.getGenericSuperclass() and consistently uses Class<?>
Issue: SPR-10559
This commit is contained in:
parent
23737a4516
commit
25e29b851d
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
@ -54,9 +54,9 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen
|
|||
|
||||
@Override
|
||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
||||
Class typeArg = GenericTypeResolver.resolveTypeArgument(this.delegate.getClass(), ApplicationListener.class);
|
||||
Class<?> typeArg = GenericTypeResolver.resolveTypeArgument(this.delegate.getClass(), ApplicationListener.class);
|
||||
if (typeArg == null || typeArg.equals(ApplicationEvent.class)) {
|
||||
Class targetClass = AopUtils.getTargetClass(this.delegate);
|
||||
Class<?> targetClass = AopUtils.getTargetClass(this.delegate);
|
||||
if (targetClass != this.delegate.getClass()) {
|
||||
typeArg = GenericTypeResolver.resolveTypeArgument(targetClass, ApplicationListener.class);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
@ -19,6 +19,7 @@ package org.springframework.core;
|
|||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.MalformedParameterizedTypeException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
@ -446,8 +447,13 @@ public abstract class GenericCollectionTypeResolver {
|
|||
return null;
|
||||
}
|
||||
if (clazz.getSuperclass() != null && isIntrospectionCandidate(clazz.getSuperclass())) {
|
||||
return extractType(clazz.getGenericSuperclass(), source, typeIndex, typeVariableMap, typeIndexesPerLevel,
|
||||
nestingLevel, currentLevel);
|
||||
try {
|
||||
return extractType(clazz.getGenericSuperclass(), source, typeIndex, typeVariableMap,
|
||||
typeIndexesPerLevel, nestingLevel, currentLevel);
|
||||
}
|
||||
catch (MalformedParameterizedTypeException ex) {
|
||||
// from getGenericSuperclass() - ignore and continue with interface introspection
|
||||
}
|
||||
}
|
||||
Type[] ifcs = clazz.getGenericInterfaces();
|
||||
if (ifcs != null) {
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.springframework.core;
|
|||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.MalformedParameterizedTypeException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
@ -74,12 +75,12 @@ public abstract class GenericTypeResolver {
|
|||
* @param clazz the class to resolve type variables against
|
||||
* @return the corresponding generic parameter or return type
|
||||
*/
|
||||
public static Class<?> resolveParameterType(MethodParameter methodParam, Class clazz) {
|
||||
public static Class<?> resolveParameterType(MethodParameter methodParam, Class<?> clazz) {
|
||||
Type genericType = getTargetType(methodParam);
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
Map<TypeVariable, Type> typeVariableMap = getTypeVariableMap(clazz);
|
||||
Type rawType = getRawType(genericType, typeVariableMap);
|
||||
Class result = (rawType instanceof Class ? (Class) rawType : methodParam.getParameterType());
|
||||
Class<?> result = (rawType instanceof Class ? (Class) rawType : methodParam.getParameterType());
|
||||
methodParam.setParameterType(result);
|
||||
methodParam.typeVariableMap = typeVariableMap;
|
||||
return result;
|
||||
|
@ -227,7 +228,7 @@ public abstract class GenericTypeResolver {
|
|||
* @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) {
|
||||
public static Class<?> resolveTypeArgument(Class<?> clazz, Class<?> genericIfc) {
|
||||
Class[] typeArgs = resolveTypeArguments(clazz, genericIfc);
|
||||
if (typeArgs == null) {
|
||||
return null;
|
||||
|
@ -248,11 +249,11 @@ public abstract class GenericTypeResolver {
|
|||
* @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) {
|
||||
public static Class[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) {
|
||||
return doResolveTypeArguments(clazz, clazz, genericIfc);
|
||||
}
|
||||
|
||||
private static Class[] doResolveTypeArguments(Class ownerClass, Class classToIntrospect, Class genericIfc) {
|
||||
private static Class[] doResolveTypeArguments(Class<?> ownerClass, Class<?> classToIntrospect, Class<?> genericIfc) {
|
||||
while (classToIntrospect != null) {
|
||||
if (genericIfc.isInterface()) {
|
||||
Type[] ifcs = classToIntrospect.getGenericInterfaces();
|
||||
|
@ -264,10 +265,15 @@ public abstract class GenericTypeResolver {
|
|||
}
|
||||
}
|
||||
else {
|
||||
Class[] result = doResolveTypeArguments(
|
||||
ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
|
||||
if (result != null) {
|
||||
return result;
|
||||
try {
|
||||
Class[] result = doResolveTypeArguments(ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
catch (MalformedParameterizedTypeException ex) {
|
||||
// from getGenericSuperclass() - return null to skip further superclass traversal
|
||||
return null;
|
||||
}
|
||||
}
|
||||
classToIntrospect = classToIntrospect.getSuperclass();
|
||||
|
@ -275,7 +281,7 @@ public abstract class GenericTypeResolver {
|
|||
return null;
|
||||
}
|
||||
|
||||
private static Class[] doResolveTypeArguments(Class ownerClass, Type ifc, Class genericIfc) {
|
||||
private static Class[] doResolveTypeArguments(Class<?> ownerClass, Type ifc, Class<?> genericIfc) {
|
||||
if (ifc instanceof ParameterizedType) {
|
||||
ParameterizedType paramIfc = (ParameterizedType) ifc;
|
||||
Type rawType = paramIfc.getRawType();
|
||||
|
@ -301,7 +307,7 @@ public abstract class GenericTypeResolver {
|
|||
/**
|
||||
* Extract a class instance from given Type.
|
||||
*/
|
||||
private static Class extractClass(Class ownerClass, Type arg) {
|
||||
private static Class<?> extractClass(Class<?> ownerClass, Type arg) {
|
||||
if (arg instanceof ParameterizedType) {
|
||||
return extractClass(ownerClass, ((ParameterizedType) arg).getRawType());
|
||||
}
|
||||
|
@ -368,7 +374,7 @@ public abstract class GenericTypeResolver {
|
|||
* {@link Class concrete classes} for the specified {@link Class}. Searches
|
||||
* all super types, enclosing types and interfaces.
|
||||
*/
|
||||
public static Map<TypeVariable, Type> getTypeVariableMap(Class clazz) {
|
||||
public static Map<TypeVariable, Type> getTypeVariableMap(Class<?> clazz) {
|
||||
Map<TypeVariable, Type> typeVariableMap = typeVariableCache.get(clazz);
|
||||
|
||||
if (typeVariableMap == null) {
|
||||
|
@ -377,28 +383,37 @@ public abstract class GenericTypeResolver {
|
|||
// interfaces
|
||||
extractTypeVariablesFromGenericInterfaces(clazz.getGenericInterfaces(), typeVariableMap);
|
||||
|
||||
// super class
|
||||
Type genericType = clazz.getGenericSuperclass();
|
||||
Class type = clazz.getSuperclass();
|
||||
while (type != null && !Object.class.equals(type)) {
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
populateTypeMapFromParameterizedType(pt, typeVariableMap);
|
||||
try {
|
||||
// super class
|
||||
Class<?> type = clazz;
|
||||
while (type.getSuperclass() != null && !Object.class.equals(type.getSuperclass())) {
|
||||
Type genericType = type.getGenericSuperclass();
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
populateTypeMapFromParameterizedType(pt, typeVariableMap);
|
||||
}
|
||||
extractTypeVariablesFromGenericInterfaces(type.getSuperclass().getGenericInterfaces(), typeVariableMap);
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
extractTypeVariablesFromGenericInterfaces(type.getGenericInterfaces(), typeVariableMap);
|
||||
genericType = type.getGenericSuperclass();
|
||||
type = type.getSuperclass();
|
||||
}
|
||||
catch (MalformedParameterizedTypeException ex) {
|
||||
// from getGenericSuperclass() - ignore and continue with member class check
|
||||
}
|
||||
|
||||
// enclosing class
|
||||
type = clazz;
|
||||
while (type.isMemberClass()) {
|
||||
genericType = type.getGenericSuperclass();
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
populateTypeMapFromParameterizedType(pt, typeVariableMap);
|
||||
try {
|
||||
// enclosing class
|
||||
Class<?> type = clazz;
|
||||
while (type.isMemberClass()) {
|
||||
Type genericType = type.getGenericSuperclass();
|
||||
if (genericType instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) genericType;
|
||||
populateTypeMapFromParameterizedType(pt, typeVariableMap);
|
||||
}
|
||||
type = type.getEnclosingClass();
|
||||
}
|
||||
type = type.getEnclosingClass();
|
||||
}
|
||||
catch (MalformedParameterizedTypeException ex) {
|
||||
// from getGenericSuperclass() - ignore and preserve previously accumulated type variables
|
||||
}
|
||||
|
||||
typeVariableCache.put(clazz, typeVariableMap);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
@ -24,7 +24,7 @@ import org.springframework.util.Assert;
|
|||
/**
|
||||
* The purpose of this class is to enable capturing and passing a generic
|
||||
* {@link Type}. In order to capture the generic type and retain it at runtime,
|
||||
* you need to create a sub-class as follows:
|
||||
* you need to create a subclass as follows:
|
||||
*
|
||||
* <pre class="code">
|
||||
* ParameterizedTypeReference<List<String>> typeRef = new ParameterizedTypeReference<List<String>>() {};
|
||||
|
@ -37,54 +37,31 @@ import org.springframework.util.Assert;
|
|||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.2
|
||||
*
|
||||
* @see <a href="http://gafter.blogspot.nl/2006/12/super-type-tokens.html">Neal Gafter on Super Type Tokens</a>
|
||||
*/
|
||||
public abstract class ParameterizedTypeReference<T> {
|
||||
|
||||
private final Type type;
|
||||
|
||||
|
||||
protected ParameterizedTypeReference() {
|
||||
Class<?> parameterizedTypeReferenceSubClass = findParameterizedTypeReferenceSubClass(getClass());
|
||||
|
||||
Type type = parameterizedTypeReferenceSubClass.getGenericSuperclass();
|
||||
Class<?> parameterizedTypeReferenceSubclass = findParameterizedTypeReferenceSubclass(getClass());
|
||||
Type type = parameterizedTypeReferenceSubclass.getGenericSuperclass();
|
||||
Assert.isInstanceOf(ParameterizedType.class, type);
|
||||
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
Assert.isTrue(parameterizedType.getActualTypeArguments().length == 1);
|
||||
|
||||
this.type = parameterizedType.getActualTypeArguments()[0];
|
||||
}
|
||||
|
||||
private static Class<?> findParameterizedTypeReferenceSubClass(Class<?> child) {
|
||||
|
||||
Class<?> parent = child.getSuperclass();
|
||||
|
||||
if (Object.class.equals(parent)) {
|
||||
throw new IllegalStateException("Expected ParameterizedTypeReference superclass");
|
||||
}
|
||||
else if (ParameterizedTypeReference.class.equals(parent)) {
|
||||
return child;
|
||||
}
|
||||
else {
|
||||
return findParameterizedTypeReferenceSubClass(parent);
|
||||
}
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o instanceof ParameterizedTypeReference) {
|
||||
ParameterizedTypeReference<?> other = (ParameterizedTypeReference<?>) o;
|
||||
return this.type.equals(other.type);
|
||||
}
|
||||
return false;
|
||||
public boolean equals(Object obj) {
|
||||
return (this == obj || (obj instanceof ParameterizedTypeReference &&
|
||||
this.type.equals(((ParameterizedTypeReference) obj).type)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,4 +73,19 @@ public abstract class ParameterizedTypeReference<T> {
|
|||
public String toString() {
|
||||
return "ParameterizedTypeReference<" + this.type + ">";
|
||||
}
|
||||
|
||||
|
||||
private static Class<?> findParameterizedTypeReferenceSubclass(Class<?> child) {
|
||||
Class<?> parent = child.getSuperclass();
|
||||
if (Object.class.equals(parent)) {
|
||||
throw new IllegalStateException("Expected ParameterizedTypeReference superclass");
|
||||
}
|
||||
else if (ParameterizedTypeReference.class.equals(parent)) {
|
||||
return child;
|
||||
}
|
||||
else {
|
||||
return findParameterizedTypeReferenceSubclass(parent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
@ -20,16 +20,17 @@ import java.lang.reflect.Type;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link ParameterizedTypeReference}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ParameterizedTypeReferenceTest {
|
||||
public class ParameterizedTypeReferenceTests {
|
||||
|
||||
@Test
|
||||
public void map() throws NoSuchMethodException {
|
||||
|
@ -58,4 +59,5 @@ public class ParameterizedTypeReferenceTest {
|
|||
public static List<String> listMethod() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue