diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java index f2b33f22dc..fe75220ac4 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/BeanFactoryGenericsTests.java @@ -769,6 +769,21 @@ public class BeanFactoryGenericsTests { 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") public static class NamedUrlList extends LinkedList { @@ -831,4 +846,29 @@ public class BeanFactoryGenericsTests { } } + + public static class NumberStore { + } + + + public static class NumberBean { + + private final NumberStore doubleStore; + + private final NumberStore floatStore; + + public NumberBean(NumberStore doubleStore, NumberStore floatStore) { + this.doubleStore = doubleStore; + this.floatStore = floatStore; + } + + public NumberStore getDoubleStore() { + return this.doubleStore; + } + + public NumberStore getFloatStore() { + return this.floatStore; + } + } + } diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index 7812ceca0d..60344942ce 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -394,8 +394,12 @@ public final class ResolvableType implements Serializable { * The result will be {@code true} only in those two scenarios. */ public boolean hasUnresolvableGenerics() { - for (Class generic : resolveGenerics()) { - if (generic == null) { + if (this == NONE) { + return false; + } + ResolvableType[] generics = getGenerics(); + for (ResolvableType generic : generics) { + if (generic.isUnresolvableTypeVariable()) { 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; @@ -486,11 +506,11 @@ public final class ResolvableType implements Serializable { if (indexes == null || indexes.length == 0) { return getGenerics()[0]; } - ResolvableType rtn = this; + ResolvableType generic = this; for (int index : indexes) { - rtn = rtn.getGenerics()[index]; + generic = generic.getGenerics()[index]; } - return rtn; + return generic; } catch (IndexOutOfBoundsException ex) { return NONE; diff --git a/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java index 1dc41e1b79..6dc363b8a2 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java +++ b/spring-core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java @@ -268,10 +268,16 @@ public class GenericConversionService implements ConfigurableConversionService { private GenericConverter.ConvertiblePair getRequiredTypeInfo(Object converter, Class genericIfc) { ResolvableType resolvableType = ResolvableType.forClass(converter.getClass()).as(genericIfc); - if(resolvableType.hasUnresolvableGenerics()) { + ResolvableType[] generics = resolvableType.getGenerics(); + if (generics.length < 2) { 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() { @@ -328,7 +334,7 @@ public class GenericConversionService implements ConfigurableConversionService { @Override public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { - if(!this.typeInfo.getTargetType().equals(targetType.getObjectType())) { + if (!this.typeInfo.getTargetType().equals(targetType.getObjectType())) { return false; } if (this.converter instanceof ConditionalConverter) { @@ -382,9 +388,9 @@ public class GenericConversionService implements ConfigurableConversionService { if (this.converterFactory instanceof ConditionalConverter) { matches = ((ConditionalConverter) this.converterFactory).matches(sourceType, targetType); } - if(matches) { + if (matches) { Converter converter = this.converterFactory.getConverter(targetType.getType()); - if(converter instanceof ConditionalConverter) { + if (converter instanceof ConditionalConverter) { matches = ((ConditionalConverter) converter).matches(sourceType, targetType); } } @@ -455,11 +461,10 @@ public class GenericConversionService implements ConfigurableConversionService { */ private static class Converters { - private final Set globalConverters = - new LinkedHashSet(); + private final Set globalConverters = new LinkedHashSet(); private final Map converters = - new LinkedHashMap(36); + new LinkedHashMap(36); public void add(GenericConverter converter) { Set convertibleTypes = converter.getConvertibleTypes(); @@ -486,7 +491,7 @@ public class GenericConversionService implements ConfigurableConversionService { } 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 targetCandidate : targetCandidates) { ConvertiblePair convertiblePair = new ConvertiblePair(sourceCandidate, targetCandidate); - GenericConverter converter = getRegisteredConverter( - sourceType, targetType, convertiblePair); - if(converter != null) { + GenericConverter converter = getRegisteredConverter(sourceType, targetType, convertiblePair); + if (converter != null) { return converter; } } @@ -518,17 +522,17 @@ public class GenericConversionService implements ConfigurableConversionService { TypeDescriptor targetType, ConvertiblePair convertiblePair) { // Check specifically registered converters - ConvertersForPair convertersForPair = converters.get(convertiblePair); - GenericConverter converter = convertersForPair == null ? null - : convertersForPair.getConverter(sourceType, targetType); - if (converter != null) { - return converter; + ConvertersForPair convertersForPair = this.converters.get(convertiblePair); + if (convertersForPair != null) { + GenericConverter converter = convertersForPair.getConverter(sourceType, targetType); + if (converter != null) { + return converter; + } } // Check ConditionalGenericConverter that match all types for (GenericConverter globalConverter : this.globalConverters) { - if (((ConditionalConverter)globalConverter).matches( - sourceType, targetType)) { + if (((ConditionalConverter)globalConverter).matches(sourceType, targetType)) { return globalConverter; } } @@ -568,10 +572,10 @@ public class GenericConversionService implements ConfigurableConversionService { private void addToClassHierarchy(int index, Class type, boolean asArray, List> hierarchy, Set> visited) { - if(asArray) { + if (asArray) { type = Array.newInstance(type, 0).getClass(); } - if(visited.add(type)) { + if (visited.add(type)) { hierarchy.add(index, type); } } @@ -634,28 +638,26 @@ public class GenericConversionService implements ConfigurableConversionService { */ private static class NoOpConverter implements GenericConverter { - private String name; - + private final String name; public NoOpConverter(String name) { this.name = name; } - @Override public Set getConvertibleTypes() { return null; } @Override - public Object convert(Object source, TypeDescriptor sourceType, - TypeDescriptor targetType) { + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { return source; } @Override public String toString() { - return name; + return this.name; } } + }