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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -54,9 +54,9 @@ public class GenericApplicationListenerAdapter implements SmartApplicationListen
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
|
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)) {
|
if (typeArg == null || typeArg.equals(ApplicationEvent.class)) {
|
||||||
Class targetClass = AopUtils.getTargetClass(this.delegate);
|
Class<?> targetClass = AopUtils.getTargetClass(this.delegate);
|
||||||
if (targetClass != this.delegate.getClass()) {
|
if (targetClass != this.delegate.getClass()) {
|
||||||
typeArg = GenericTypeResolver.resolveTypeArgument(targetClass, ApplicationListener.class);
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.Array;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.GenericArrayType;
|
import java.lang.reflect.GenericArrayType;
|
||||||
|
import java.lang.reflect.MalformedParameterizedTypeException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
@ -446,8 +447,13 @@ public abstract class GenericCollectionTypeResolver {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (clazz.getSuperclass() != null && isIntrospectionCandidate(clazz.getSuperclass())) {
|
if (clazz.getSuperclass() != null && isIntrospectionCandidate(clazz.getSuperclass())) {
|
||||||
return extractType(clazz.getGenericSuperclass(), source, typeIndex, typeVariableMap, typeIndexesPerLevel,
|
try {
|
||||||
nestingLevel, currentLevel);
|
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();
|
Type[] ifcs = clazz.getGenericInterfaces();
|
||||||
if (ifcs != null) {
|
if (ifcs != null) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.springframework.core;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.GenericArrayType;
|
import java.lang.reflect.GenericArrayType;
|
||||||
|
import java.lang.reflect.MalformedParameterizedTypeException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
@ -74,12 +75,12 @@ public abstract class GenericTypeResolver {
|
||||||
* @param clazz the class to resolve type variables against
|
* @param clazz the class to resolve type variables against
|
||||||
* @return the corresponding generic parameter or return type
|
* @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);
|
Type genericType = getTargetType(methodParam);
|
||||||
Assert.notNull(clazz, "Class must not be null");
|
Assert.notNull(clazz, "Class must not be null");
|
||||||
Map<TypeVariable, Type> typeVariableMap = getTypeVariableMap(clazz);
|
Map<TypeVariable, Type> typeVariableMap = getTypeVariableMap(clazz);
|
||||||
Type rawType = getRawType(genericType, typeVariableMap);
|
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.setParameterType(result);
|
||||||
methodParam.typeVariableMap = typeVariableMap;
|
methodParam.typeVariableMap = typeVariableMap;
|
||||||
return result;
|
return result;
|
||||||
|
@ -227,7 +228,7 @@ public abstract class GenericTypeResolver {
|
||||||
* @param genericIfc the generic interface or superclass to resolve the type argument from
|
* @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
|
* @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);
|
Class[] typeArgs = resolveTypeArguments(clazz, genericIfc);
|
||||||
if (typeArgs == null) {
|
if (typeArgs == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -248,11 +249,11 @@ public abstract class GenericTypeResolver {
|
||||||
* @return the resolved type of each argument, with the array size matching the
|
* @return the resolved type of each argument, with the array size matching the
|
||||||
* number of actual type arguments, or {@code null} if not resolvable
|
* 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);
|
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) {
|
while (classToIntrospect != null) {
|
||||||
if (genericIfc.isInterface()) {
|
if (genericIfc.isInterface()) {
|
||||||
Type[] ifcs = classToIntrospect.getGenericInterfaces();
|
Type[] ifcs = classToIntrospect.getGenericInterfaces();
|
||||||
|
@ -264,18 +265,23 @@ public abstract class GenericTypeResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Class[] result = doResolveTypeArguments(
|
try {
|
||||||
ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
|
Class[] result = doResolveTypeArguments(ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (MalformedParameterizedTypeException ex) {
|
||||||
|
// from getGenericSuperclass() - return null to skip further superclass traversal
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
classToIntrospect = classToIntrospect.getSuperclass();
|
classToIntrospect = classToIntrospect.getSuperclass();
|
||||||
}
|
}
|
||||||
return null;
|
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) {
|
if (ifc instanceof ParameterizedType) {
|
||||||
ParameterizedType paramIfc = (ParameterizedType) ifc;
|
ParameterizedType paramIfc = (ParameterizedType) ifc;
|
||||||
Type rawType = paramIfc.getRawType();
|
Type rawType = paramIfc.getRawType();
|
||||||
|
@ -301,7 +307,7 @@ public abstract class GenericTypeResolver {
|
||||||
/**
|
/**
|
||||||
* Extract a class instance from given Type.
|
* 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) {
|
if (arg instanceof ParameterizedType) {
|
||||||
return extractClass(ownerClass, ((ParameterizedType) arg).getRawType());
|
return extractClass(ownerClass, ((ParameterizedType) arg).getRawType());
|
||||||
}
|
}
|
||||||
|
@ -368,7 +374,7 @@ public abstract class GenericTypeResolver {
|
||||||
* {@link Class concrete classes} for the specified {@link Class}. Searches
|
* {@link Class concrete classes} for the specified {@link Class}. Searches
|
||||||
* all super types, enclosing types and interfaces.
|
* 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);
|
Map<TypeVariable, Type> typeVariableMap = typeVariableCache.get(clazz);
|
||||||
|
|
||||||
if (typeVariableMap == null) {
|
if (typeVariableMap == null) {
|
||||||
|
@ -377,29 +383,38 @@ public abstract class GenericTypeResolver {
|
||||||
// interfaces
|
// interfaces
|
||||||
extractTypeVariablesFromGenericInterfaces(clazz.getGenericInterfaces(), typeVariableMap);
|
extractTypeVariablesFromGenericInterfaces(clazz.getGenericInterfaces(), typeVariableMap);
|
||||||
|
|
||||||
|
try {
|
||||||
// super class
|
// super class
|
||||||
Type genericType = clazz.getGenericSuperclass();
|
Class<?> type = clazz;
|
||||||
Class type = clazz.getSuperclass();
|
while (type.getSuperclass() != null && !Object.class.equals(type.getSuperclass())) {
|
||||||
while (type != null && !Object.class.equals(type)) {
|
Type genericType = type.getGenericSuperclass();
|
||||||
if (genericType instanceof ParameterizedType) {
|
if (genericType instanceof ParameterizedType) {
|
||||||
ParameterizedType pt = (ParameterizedType) genericType;
|
ParameterizedType pt = (ParameterizedType) genericType;
|
||||||
populateTypeMapFromParameterizedType(pt, typeVariableMap);
|
populateTypeMapFromParameterizedType(pt, typeVariableMap);
|
||||||
}
|
}
|
||||||
extractTypeVariablesFromGenericInterfaces(type.getGenericInterfaces(), typeVariableMap);
|
extractTypeVariablesFromGenericInterfaces(type.getSuperclass().getGenericInterfaces(), typeVariableMap);
|
||||||
genericType = type.getGenericSuperclass();
|
|
||||||
type = type.getSuperclass();
|
type = type.getSuperclass();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (MalformedParameterizedTypeException ex) {
|
||||||
|
// from getGenericSuperclass() - ignore and continue with member class check
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
// enclosing class
|
// enclosing class
|
||||||
type = clazz;
|
Class<?> type = clazz;
|
||||||
while (type.isMemberClass()) {
|
while (type.isMemberClass()) {
|
||||||
genericType = type.getGenericSuperclass();
|
Type genericType = type.getGenericSuperclass();
|
||||||
if (genericType instanceof ParameterizedType) {
|
if (genericType instanceof ParameterizedType) {
|
||||||
ParameterizedType pt = (ParameterizedType) genericType;
|
ParameterizedType pt = (ParameterizedType) genericType;
|
||||||
populateTypeMapFromParameterizedType(pt, typeVariableMap);
|
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);
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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
|
* 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,
|
* {@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">
|
* <pre class="code">
|
||||||
* ParameterizedTypeReference<List<String>> typeRef = new ParameterizedTypeReference<List<String>>() {};
|
* ParameterizedTypeReference<List<String>> typeRef = new ParameterizedTypeReference<List<String>>() {};
|
||||||
|
@ -37,54 +37,31 @@ import org.springframework.util.Assert;
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
*
|
|
||||||
* @see <a href="http://gafter.blogspot.nl/2006/12/super-type-tokens.html">Neal Gafter on Super Type Tokens</a>
|
* @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> {
|
public abstract class ParameterizedTypeReference<T> {
|
||||||
|
|
||||||
private final Type type;
|
private final Type type;
|
||||||
|
|
||||||
|
|
||||||
protected ParameterizedTypeReference() {
|
protected ParameterizedTypeReference() {
|
||||||
Class<?> parameterizedTypeReferenceSubClass = findParameterizedTypeReferenceSubClass(getClass());
|
Class<?> parameterizedTypeReferenceSubclass = findParameterizedTypeReferenceSubclass(getClass());
|
||||||
|
Type type = parameterizedTypeReferenceSubclass.getGenericSuperclass();
|
||||||
Type type = parameterizedTypeReferenceSubClass.getGenericSuperclass();
|
|
||||||
Assert.isInstanceOf(ParameterizedType.class, type);
|
Assert.isInstanceOf(ParameterizedType.class, type);
|
||||||
|
|
||||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||||
Assert.isTrue(parameterizedType.getActualTypeArguments().length == 1);
|
Assert.isTrue(parameterizedType.getActualTypeArguments().length == 1);
|
||||||
|
|
||||||
this.type = parameterizedType.getActualTypeArguments()[0];
|
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() {
|
public Type getType() {
|
||||||
return this.type;
|
return this.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object obj) {
|
||||||
if (this == o) {
|
return (this == obj || (obj instanceof ParameterizedTypeReference &&
|
||||||
return true;
|
this.type.equals(((ParameterizedTypeReference) obj).type)));
|
||||||
}
|
|
||||||
if (o instanceof ParameterizedTypeReference) {
|
|
||||||
ParameterizedTypeReference<?> other = (ParameterizedTypeReference<?>) o;
|
|
||||||
return this.type.equals(other.type);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -96,4 +73,19 @@ public abstract class ParameterizedTypeReference<T> {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ParameterizedTypeReference<" + this.type + ">";
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test fixture for {@link ParameterizedTypeReference}.
|
* Test fixture for {@link ParameterizedTypeReference}.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
public class ParameterizedTypeReferenceTest {
|
public class ParameterizedTypeReferenceTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void map() throws NoSuchMethodException {
|
public void map() throws NoSuchMethodException {
|
||||||
|
@ -58,4 +59,5 @@ public class ParameterizedTypeReferenceTest {
|
||||||
public static List<String> listMethod() {
|
public static List<String> listMethod() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue