Fixed hasUnresolvableGenerics() to return false in case of an unresolvable bounded variable as well

Issue: SPR-11250
This commit is contained in:
Juergen Hoeller
2013-12-20 16:44:25 +01:00
parent bc3064cbb7
commit f9e8eb59e1
3 changed files with 96 additions and 34 deletions

View File

@@ -769,6 +769,21 @@ public class BeanFactoryGenericsTests {
assertEquals(1, beans.size()); assertEquals(1, beans.size());
} }
@Test
public void testSpr11250() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new GenericTypeAwareAutowireCandidateResolver());
bf.registerBeanDefinition("doubleStore", new RootBeanDefinition(NumberStore.class));
bf.registerBeanDefinition("floatStore", new RootBeanDefinition(NumberStore.class));
bf.registerBeanDefinition("numberBean",
new RootBeanDefinition(NumberBean.class, RootBeanDefinition.AUTOWIRE_CONSTRUCTOR, false));
NumberBean nb = bf.getBean(NumberBean.class);
assertNotNull(nb.getDoubleStore());
assertNotNull(nb.getFloatStore());
}
@SuppressWarnings("serial") @SuppressWarnings("serial")
public static class NamedUrlList extends LinkedList<URL> { public static class NamedUrlList extends LinkedList<URL> {
@@ -831,4 +846,29 @@ public class BeanFactoryGenericsTests {
} }
} }
public static class NumberStore<T extends Number> {
}
public static class NumberBean {
private final NumberStore<Double> doubleStore;
private final NumberStore<Float> floatStore;
public NumberBean(NumberStore<Double> doubleStore, NumberStore<Float> floatStore) {
this.doubleStore = doubleStore;
this.floatStore = floatStore;
}
public NumberStore<Double> getDoubleStore() {
return this.doubleStore;
}
public NumberStore<Float> getFloatStore() {
return this.floatStore;
}
}
} }

View File

@@ -394,8 +394,12 @@ public final class ResolvableType implements Serializable {
* The result will be {@code true} only in those two scenarios. * The result will be {@code true} only in those two scenarios.
*/ */
public boolean hasUnresolvableGenerics() { public boolean hasUnresolvableGenerics() {
for (Class<?> generic : resolveGenerics()) { if (this == NONE) {
if (generic == null) { return false;
}
ResolvableType[] generics = getGenerics();
for (ResolvableType generic : generics) {
if (generic.isUnresolvableTypeVariable()) {
return true; return true;
} }
} }
@@ -408,8 +412,24 @@ public final class ResolvableType implements Serializable {
} }
} }
} }
if (resolved.getGenericSuperclass() != null) { return getSuperType().hasUnresolvableGenerics();
return getSuperType().hasUnresolvableGenerics(); }
return false;
}
/**
* Determine whether the underlying type is a type variable that
* cannot be resolved through the associated variable resolver.
*/
private boolean isUnresolvableTypeVariable() {
if (this.type instanceof TypeVariable) {
if (this.variableResolver == null) {
return true;
}
TypeVariable<?> variable = (TypeVariable<?>) this.type;
ResolvableType resolved = this.variableResolver.resolveVariable(variable);
if (resolved == null || resolved.isUnresolvableTypeVariable()) {
return true;
} }
} }
return false; return false;
@@ -486,11 +506,11 @@ public final class ResolvableType implements Serializable {
if (indexes == null || indexes.length == 0) { if (indexes == null || indexes.length == 0) {
return getGenerics()[0]; return getGenerics()[0];
} }
ResolvableType rtn = this; ResolvableType generic = this;
for (int index : indexes) { for (int index : indexes) {
rtn = rtn.getGenerics()[index]; generic = generic.getGenerics()[index];
} }
return rtn; return generic;
} }
catch (IndexOutOfBoundsException ex) { catch (IndexOutOfBoundsException ex) {
return NONE; return NONE;

View File

@@ -268,10 +268,16 @@ public class GenericConversionService implements ConfigurableConversionService {
private GenericConverter.ConvertiblePair getRequiredTypeInfo(Object converter, Class<?> genericIfc) { private GenericConverter.ConvertiblePair getRequiredTypeInfo(Object converter, Class<?> genericIfc) {
ResolvableType resolvableType = ResolvableType.forClass(converter.getClass()).as(genericIfc); ResolvableType resolvableType = ResolvableType.forClass(converter.getClass()).as(genericIfc);
if(resolvableType.hasUnresolvableGenerics()) { ResolvableType[] generics = resolvableType.getGenerics();
if (generics.length < 2) {
return null; return null;
} }
return new GenericConverter.ConvertiblePair(resolvableType.resolveGeneric(0), resolvableType.resolveGeneric(1)); Class<?> sourceType = generics[0].resolve();
Class<?> targetType = generics[1].resolve();
if (sourceType == null || targetType == null) {
return null;
}
return new GenericConverter.ConvertiblePair(sourceType, targetType);
} }
private void invalidateCache() { private void invalidateCache() {
@@ -328,7 +334,7 @@ public class GenericConversionService implements ConfigurableConversionService {
@Override @Override
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
if(!this.typeInfo.getTargetType().equals(targetType.getObjectType())) { if (!this.typeInfo.getTargetType().equals(targetType.getObjectType())) {
return false; return false;
} }
if (this.converter instanceof ConditionalConverter) { if (this.converter instanceof ConditionalConverter) {
@@ -382,9 +388,9 @@ public class GenericConversionService implements ConfigurableConversionService {
if (this.converterFactory instanceof ConditionalConverter) { if (this.converterFactory instanceof ConditionalConverter) {
matches = ((ConditionalConverter) this.converterFactory).matches(sourceType, targetType); matches = ((ConditionalConverter) this.converterFactory).matches(sourceType, targetType);
} }
if(matches) { if (matches) {
Converter<?, ?> converter = this.converterFactory.getConverter(targetType.getType()); Converter<?, ?> converter = this.converterFactory.getConverter(targetType.getType());
if(converter instanceof ConditionalConverter) { if (converter instanceof ConditionalConverter) {
matches = ((ConditionalConverter) converter).matches(sourceType, targetType); matches = ((ConditionalConverter) converter).matches(sourceType, targetType);
} }
} }
@@ -455,11 +461,10 @@ public class GenericConversionService implements ConfigurableConversionService {
*/ */
private static class Converters { private static class Converters {
private final Set<GenericConverter> globalConverters = private final Set<GenericConverter> globalConverters = new LinkedHashSet<GenericConverter>();
new LinkedHashSet<GenericConverter>();
private final Map<ConvertiblePair, ConvertersForPair> converters = private final Map<ConvertiblePair, ConvertersForPair> converters =
new LinkedHashMap<ConvertiblePair, ConvertersForPair>(36); new LinkedHashMap<ConvertiblePair, ConvertersForPair>(36);
public void add(GenericConverter converter) { public void add(GenericConverter converter) {
Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes(); Set<ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
@@ -486,7 +491,7 @@ public class GenericConversionService implements ConfigurableConversionService {
} }
public void remove(Class<?> sourceType, Class<?> targetType) { public void remove(Class<?> sourceType, Class<?> targetType) {
converters.remove(new ConvertiblePair(sourceType, targetType)); this.converters.remove(new ConvertiblePair(sourceType, targetType));
} }
/** /**
@@ -504,9 +509,8 @@ public class GenericConversionService implements ConfigurableConversionService {
for (Class<?> sourceCandidate : sourceCandidates) { for (Class<?> sourceCandidate : sourceCandidates) {
for (Class<?> targetCandidate : targetCandidates) { for (Class<?> targetCandidate : targetCandidates) {
ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate); ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate);
GenericConverter converter = getRegisteredConverter( GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair);
sourceType, targetType, convertiblePair); if (converter != null) {
if(converter != null) {
return converter; return converter;
} }
} }
@@ -518,17 +522,17 @@ public class GenericConversionService implements ConfigurableConversionService {
TypeDescriptor targetType, ConvertiblePair convertiblePair) { TypeDescriptor targetType, ConvertiblePair convertiblePair) {
// Check specifically registered converters // Check specifically registered converters
ConvertersForPair convertersForPair = converters.get(convertiblePair); ConvertersForPair convertersForPair = this.converters.get(convertiblePair);
GenericConverter converter = convertersForPair == null ? null if (convertersForPair != null) {
: convertersForPair.getConverter(sourceType, targetType); GenericConverter converter = convertersForPair.getConverter(sourceType, targetType);
if (converter != null) { if (converter != null) {
return converter; return converter;
}
} }
// Check ConditionalGenericConverter that match all types // Check ConditionalGenericConverter that match all types
for (GenericConverter globalConverter : this.globalConverters) { for (GenericConverter globalConverter : this.globalConverters) {
if (((ConditionalConverter)globalConverter).matches( if (((ConditionalConverter)globalConverter).matches(sourceType, targetType)) {
sourceType, targetType)) {
return globalConverter; return globalConverter;
} }
} }
@@ -568,10 +572,10 @@ public class GenericConversionService implements ConfigurableConversionService {
private void addToClassHierarchy(int index, Class<?> type, boolean asArray, private void addToClassHierarchy(int index, Class<?> type, boolean asArray,
List<Class<?>> hierarchy, Set<Class<?>> visited) { List<Class<?>> hierarchy, Set<Class<?>> visited) {
if(asArray) { if (asArray) {
type = Array.newInstance(type, 0).getClass(); type = Array.newInstance(type, 0).getClass();
} }
if(visited.add(type)) { if (visited.add(type)) {
hierarchy.add(index, type); hierarchy.add(index, type);
} }
} }
@@ -634,28 +638,26 @@ public class GenericConversionService implements ConfigurableConversionService {
*/ */
private static class NoOpConverter implements GenericConverter { private static class NoOpConverter implements GenericConverter {
private String name; private final String name;
public NoOpConverter(String name) { public NoOpConverter(String name) {
this.name = name; this.name = name;
} }
@Override @Override
public Set<ConvertiblePair> getConvertibleTypes() { public Set<ConvertiblePair> getConvertibleTypes() {
return null; return null;
} }
@Override @Override
public Object convert(Object source, TypeDescriptor sourceType, public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
TypeDescriptor targetType) {
return source; return source;
} }
@Override @Override
public String toString() { public String toString() {
return name; return this.name;
} }
} }
} }