diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java index 82c6883cc18..36db05b19e6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -28,7 +28,6 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.InjectionPoint; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; -import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; @@ -63,6 +62,8 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable private Class containingClass; + private volatile ResolvableType resolvableType; + /** * Create a new descriptor for a method or constructor parameter. @@ -214,6 +215,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable */ public void increaseNestingLevel() { this.nestingLevel++; + this.resolvableType = null; if (this.methodParameter != null) { this.methodParameter.increaseNestingLevel(); } @@ -227,6 +229,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable */ public void setContainingClass(Class containingClass) { this.containingClass = containingClass; + this.resolvableType = null; if (this.methodParameter != null) { GenericTypeResolver.resolveParameterType(this.methodParameter, containingClass); } @@ -237,8 +240,12 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable * @since 4.0 */ public ResolvableType getResolvableType() { - return (this.field != null ? ResolvableType.forField(this.field, this.nestingLevel, this.containingClass) : - ResolvableType.forMethodParameter(this.methodParameter)); + if (this.resolvableType == null) { + this.resolvableType = (this.field != null ? + ResolvableType.forField(this.field, this.nestingLevel, this.containingClass) : + ResolvableType.forMethodParameter(this.methodParameter)); + } + return this.resolvableType; } /** @@ -324,31 +331,37 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable /** * Determine the generic element type of the wrapped Collection parameter/field, if any. * @return the generic type, or {@code null} if none + * @deprecated as of 4.3.6, in favor of direct {@link ResolvableType} usage */ + @Deprecated public Class getCollectionType() { return (this.field != null ? - GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel) : - GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter)); + org.springframework.core.GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel) : + org.springframework.core.GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter)); } /** * Determine the generic key type of the wrapped Map parameter/field, if any. * @return the generic type, or {@code null} if none + * @deprecated as of 4.3.6, in favor of direct {@link ResolvableType} usage */ + @Deprecated public Class getMapKeyType() { return (this.field != null ? - GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel) : - GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter)); + org.springframework.core.GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel) : + org.springframework.core.GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter)); } /** * Determine the generic value type of the wrapped Map parameter/field, if any. * @return the generic type, or {@code null} if none + * @deprecated as of 4.3.6, in favor of direct {@link ResolvableType} usage */ + @Deprecated public Class getMapValueType() { return (this.field != null ? - GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel) : - GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter)); + org.springframework.core.GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel) : + org.springframework.core.GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter)); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ListFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ListFactoryBean.java index fb251c4243b..3ab96831092 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ListFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ListFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2017 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. @@ -21,7 +21,7 @@ import java.util.List; import org.springframework.beans.BeanUtils; import org.springframework.beans.TypeConverter; -import org.springframework.core.GenericCollectionTypeResolver; +import org.springframework.core.ResolvableType; /** * Simple factory for shared List instances. Allows for central setup @@ -86,7 +86,7 @@ public class ListFactoryBean extends AbstractFactoryBean> { } Class valueType = null; if (this.targetListClass != null) { - valueType = GenericCollectionTypeResolver.getCollectionType(this.targetListClass); + valueType = ResolvableType.forClass(this.targetListClass).asCollection().resolveGeneric(); } if (valueType != null) { TypeConverter converter = getBeanTypeConverter(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/MapFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/MapFactoryBean.java index f1ba9cfd451..2e9ca7378bd 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/MapFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/MapFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2017 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. @@ -21,7 +21,7 @@ import java.util.Map; import org.springframework.beans.BeanUtils; import org.springframework.beans.TypeConverter; -import org.springframework.core.GenericCollectionTypeResolver; +import org.springframework.core.ResolvableType; /** * Simple factory for shared Map instances. Allows for central setup @@ -87,8 +87,9 @@ public class MapFactoryBean extends AbstractFactoryBean> { Class keyType = null; Class valueType = null; if (this.targetMapClass != null) { - keyType = GenericCollectionTypeResolver.getMapKeyType(this.targetMapClass); - valueType = GenericCollectionTypeResolver.getMapValueType(this.targetMapClass); + ResolvableType mapType = ResolvableType.forClass(this.targetMapClass).asMap(); + keyType = mapType.resolveGeneric(0); + valueType = mapType.resolveGeneric(1); } if (keyType != null || valueType != null) { TypeConverter converter = getBeanTypeConverter(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/SetFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/SetFactoryBean.java index c6b573d3dbe..cd05acd2382 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/SetFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/SetFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2017 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. @@ -21,7 +21,7 @@ import java.util.Set; import org.springframework.beans.BeanUtils; import org.springframework.beans.TypeConverter; -import org.springframework.core.GenericCollectionTypeResolver; +import org.springframework.core.ResolvableType; /** * Simple factory for shared Set instances. Allows for central setup @@ -86,7 +86,7 @@ public class SetFactoryBean extends AbstractFactoryBean> { } Class valueType = null; if (this.targetSetClass != null) { - valueType = GenericCollectionTypeResolver.getCollectionType(this.targetSetClass); + valueType = ResolvableType.forClass(this.targetSetClass).asCollection().resolveGeneric(); } if (valueType != null) { TypeConverter converter = getBeanTypeConverter(); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 7b04c45e3c0..c36b7196e1d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1148,6 +1148,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto Class type = descriptor.getDependencyType(); if (type.isArray()) { Class componentType = type.getComponentType(); + ResolvableType resolvableType = descriptor.getResolvableType(); + Class resolvedArrayType = resolvableType.resolve(); + if (resolvedArrayType != null && resolvedArrayType != type) { + type = resolvedArrayType; + componentType = resolvableType.getComponentType().resolve(); + } + if (componentType == null) { + return null; + } Map matchingBeans = findAutowireCandidates(beanName, componentType, new MultiElementDescriptor(descriptor)); if (matchingBeans.isEmpty()) { @@ -1164,7 +1173,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return result; } else if (Collection.class.isAssignableFrom(type) && type.isInterface()) { - Class elementType = descriptor.getCollectionType(); + Class elementType = descriptor.getResolvableType().asCollection().resolveGeneric(); if (elementType == null) { return null; } @@ -1184,11 +1193,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return result; } else if (Map.class == type) { - Class keyType = descriptor.getMapKeyType(); + ResolvableType mapType = descriptor.getResolvableType().asMap(); + Class keyType = mapType.resolveGeneric(0); if (String.class != keyType) { return null; } - Class valueType = descriptor.getMapValueType(); + Class valueType = mapType.resolveGeneric(1); if (valueType == null) { return null; } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index 37f8be79310..920975b0923 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -1592,12 +1592,30 @@ public class AutowiredAnnotationBeanPostProcessorTests { RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBean.class); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); + String sv = "X"; + bf.registerSingleton("stringValue", sv); + Integer iv = 1; + bf.registerSingleton("integerValue", iv); StringRepository sr = new StringRepository(); bf.registerSingleton("stringRepo", sr); IntegerRepository ir = new IntegerRepository(); bf.registerSingleton("integerRepo", ir); RepositoryFieldInjectionBean bean = (RepositoryFieldInjectionBean) bf.getBean("annotatedBean"); + assertSame(sv, bean.string); + assertSame(iv, bean.integer); + assertSame(1, bean.stringArray.length); + assertSame(1, bean.integerArray.length); + assertSame(sv, bean.stringArray[0]); + assertSame(iv, bean.integerArray[0]); + assertSame(1, bean.stringList.size()); + assertSame(1, bean.integerList.size()); + assertSame(sv, bean.stringList.get(0)); + assertSame(iv, bean.integerList.get(0)); + assertSame(1, bean.stringMap.size()); + assertSame(1, bean.integerMap.size()); + assertSame(sv, bean.stringMap.get("stringValue")); + assertSame(iv, bean.integerMap.get("integerValue")); assertSame(sr, bean.stringRepository); assertSame(ir, bean.integerRepository); assertSame(1, bean.stringRepositoryArray.length); @@ -1624,12 +1642,30 @@ public class AutowiredAnnotationBeanPostProcessorTests { RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBeanWithSubstitutedVariables.class); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); + String sv = "X"; + bf.registerSingleton("stringValue", sv); + Integer iv = 1; + bf.registerSingleton("integerValue", iv); StringRepository sr = new StringRepository(); bf.registerSingleton("stringRepo", sr); IntegerRepository ir = new IntegerRepository(); bf.registerSingleton("integerRepo", ir); RepositoryFieldInjectionBeanWithSubstitutedVariables bean = (RepositoryFieldInjectionBeanWithSubstitutedVariables) bf.getBean("annotatedBean"); + assertSame(sv, bean.string); + assertSame(iv, bean.integer); + assertSame(1, bean.stringArray.length); + assertSame(1, bean.integerArray.length); + assertSame(sv, bean.stringArray[0]); + assertSame(iv, bean.integerArray[0]); + assertSame(1, bean.stringList.size()); + assertSame(1, bean.integerList.size()); + assertSame(sv, bean.stringList.get(0)); + assertSame(iv, bean.integerList.get(0)); + assertSame(1, bean.stringMap.size()); + assertSame(1, bean.integerMap.size()); + assertSame(sv, bean.stringMap.get("stringValue")); + assertSame(iv, bean.integerMap.get("integerValue")); assertSame(sr, bean.stringRepository); assertSame(ir, bean.integerRepository); assertSame(1, bean.stringRepositoryArray.length); @@ -1872,12 +1908,30 @@ public class AutowiredAnnotationBeanPostProcessorTests { RootBeanDefinition bd = new RootBeanDefinition(RepositoryMethodInjectionBean.class); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); + String sv = "X"; + bf.registerSingleton("stringValue", sv); + Integer iv = 1; + bf.registerSingleton("integerValue", iv); StringRepository sr = new StringRepository(); bf.registerSingleton("stringRepo", sr); IntegerRepository ir = new IntegerRepository(); bf.registerSingleton("integerRepo", ir); RepositoryMethodInjectionBean bean = (RepositoryMethodInjectionBean) bf.getBean("annotatedBean"); + assertSame(sv, bean.string); + assertSame(iv, bean.integer); + assertSame(1, bean.stringArray.length); + assertSame(1, bean.integerArray.length); + assertSame(sv, bean.stringArray[0]); + assertSame(iv, bean.integerArray[0]); + assertSame(1, bean.stringList.size()); + assertSame(1, bean.integerList.size()); + assertSame(sv, bean.stringList.get(0)); + assertSame(iv, bean.integerList.get(0)); + assertSame(1, bean.stringMap.size()); + assertSame(1, bean.integerMap.size()); + assertSame(sv, bean.stringMap.get("stringValue")); + assertSame(iv, bean.integerMap.get("integerValue")); assertSame(sr, bean.stringRepository); assertSame(ir, bean.integerRepository); assertSame(1, bean.stringRepositoryArray.length); @@ -1904,12 +1958,30 @@ public class AutowiredAnnotationBeanPostProcessorTests { RootBeanDefinition bd = new RootBeanDefinition(RepositoryMethodInjectionBeanWithSubstitutedVariables.class); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); + String sv = "X"; + bf.registerSingleton("stringValue", sv); + Integer iv = 1; + bf.registerSingleton("integerValue", iv); StringRepository sr = new StringRepository(); bf.registerSingleton("stringRepo", sr); IntegerRepository ir = new IntegerRepository(); bf.registerSingleton("integerRepo", ir); RepositoryMethodInjectionBeanWithSubstitutedVariables bean = (RepositoryMethodInjectionBeanWithSubstitutedVariables) bf.getBean("annotatedBean"); + assertSame(sv, bean.string); + assertSame(iv, bean.integer); + assertSame(1, bean.stringArray.length); + assertSame(1, bean.integerArray.length); + assertSame(sv, bean.stringArray[0]); + assertSame(iv, bean.integerArray[0]); + assertSame(1, bean.stringList.size()); + assertSame(1, bean.integerList.size()); + assertSame(sv, bean.stringList.get(0)); + assertSame(iv, bean.integerList.get(0)); + assertSame(1, bean.stringMap.size()); + assertSame(1, bean.integerMap.size()); + assertSame(sv, bean.stringMap.get("stringValue")); + assertSame(iv, bean.integerMap.get("integerValue")); assertSame(sr, bean.stringRepository); assertSame(ir, bean.integerRepository); assertSame(1, bean.stringRepositoryArray.length); @@ -2968,6 +3040,30 @@ public class AutowiredAnnotationBeanPostProcessorTests { public static class RepositoryFieldInjectionBean { + @Autowired + public String string; + + @Autowired + public Integer integer; + + @Autowired + public String[] stringArray; + + @Autowired + public Integer[] integerArray; + + @Autowired + public List stringList; + + @Autowired + public List integerList; + + @Autowired + public Map stringMap; + + @Autowired + public Map integerMap; + @Autowired public Repository stringRepository; @@ -2996,6 +3092,30 @@ public class AutowiredAnnotationBeanPostProcessorTests { public static class RepositoryFieldInjectionBeanWithVariables { + @Autowired + public S string; + + @Autowired + public I integer; + + @Autowired + public S[] stringArray; + + @Autowired + public I[] integerArray; + + @Autowired + public List stringList; + + @Autowired + public List integerList; + + @Autowired + public Map stringMap; + + @Autowired + public Map integerMap; + @Autowired public Repository stringRepository; @@ -3092,6 +3212,22 @@ public class AutowiredAnnotationBeanPostProcessorTests { public static class RepositoryMethodInjectionBean { + public String string; + + public Integer integer; + + public String[] stringArray; + + public Integer[] integerArray; + + public List stringList; + + public List integerList; + + public Map stringMap; + + public Map integerMap; + public Repository stringRepository; public Repository integerRepository; @@ -3108,6 +3244,46 @@ public class AutowiredAnnotationBeanPostProcessorTests { public Map> integerRepositoryMap; + @Autowired + public void setString(String string) { + this.string = string; + } + + @Autowired + public void setInteger(Integer integer) { + this.integer = integer; + } + + @Autowired + public void setStringArray(String[] stringArray) { + this.stringArray = stringArray; + } + + @Autowired + public void setIntegerArray(Integer[] integerArray) { + this.integerArray = integerArray; + } + + @Autowired + public void setStringList(List stringList) { + this.stringList = stringList; + } + + @Autowired + public void setIntegerList(List integerList) { + this.integerList = integerList; + } + + @Autowired + public void setStringMap(Map stringMap) { + this.stringMap = stringMap; + } + + @Autowired + public void setIntegerMap(Map integerMap) { + this.integerMap = integerMap; + } + @Autowired public void setStringRepository(Repository stringRepository) { this.stringRepository = stringRepository; @@ -3152,6 +3328,22 @@ public class AutowiredAnnotationBeanPostProcessorTests { public static class RepositoryMethodInjectionBeanWithVariables { + public S string; + + public I integer; + + public S[] stringArray; + + public I[] integerArray; + + public List stringList; + + public List integerList; + + public Map stringMap; + + public Map integerMap; + public Repository stringRepository; public Repository integerRepository; @@ -3168,6 +3360,46 @@ public class AutowiredAnnotationBeanPostProcessorTests { public Map> integerRepositoryMap; + @Autowired + public void setString(S string) { + this.string = string; + } + + @Autowired + public void setInteger(I integer) { + this.integer = integer; + } + + @Autowired + public void setStringArray(S[] stringArray) { + this.stringArray = stringArray; + } + + @Autowired + public void setIntegerArray(I[] integerArray) { + this.integerArray = integerArray; + } + + @Autowired + public void setStringList(List stringList) { + this.stringList = stringList; + } + + @Autowired + public void setIntegerList(List integerList) { + this.integerList = integerList; + } + + @Autowired + public void setStringMap(Map stringMap) { + this.stringMap = stringMap; + } + + @Autowired + public void setIntegerMap(Map integerMap) { + this.integerMap = integerMap; + } + @Autowired public void setStringRepository(Repository stringRepository) { this.stringRepository = stringRepository; diff --git a/spring-context/src/main/java/org/springframework/jmx/access/MBeanClientInterceptor.java b/spring-context/src/main/java/org/springframework/jmx/access/MBeanClientInterceptor.java index 9d7c677731c..cbb95cc9d16 100644 --- a/spring-context/src/main/java/org/springframework/jmx/access/MBeanClientInterceptor.java +++ b/spring-context/src/main/java/org/springframework/jmx/access/MBeanClientInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -57,8 +57,8 @@ import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.CollectionFactory; -import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.MethodParameter; +import org.springframework.core.ResolvableType; import org.springframework.jmx.support.JmxUtils; import org.springframework.jmx.support.ObjectNameManager; import org.springframework.util.ClassUtils; @@ -546,7 +546,8 @@ public class MBeanClientInterceptor return convertDataArrayToTargetArray(array, targetClass); } else if (Collection.class.isAssignableFrom(targetClass)) { - Class elementType = GenericCollectionTypeResolver.getCollectionParameterType(parameter); + Class elementType = + ResolvableType.forMethodParameter(parameter).asCollection().resolveGeneric(); if (elementType != null) { return convertDataArrayToTargetCollection(array, targetClass, elementType); } @@ -562,7 +563,8 @@ public class MBeanClientInterceptor return convertDataArrayToTargetArray(array, targetClass); } else if (Collection.class.isAssignableFrom(targetClass)) { - Class elementType = GenericCollectionTypeResolver.getCollectionParameterType(parameter); + Class elementType = + ResolvableType.forMethodParameter(parameter).asCollection().resolveGeneric(); if (elementType != null) { return convertDataArrayToTargetCollection(array, targetClass, elementType); } diff --git a/spring-core/src/main/java/org/springframework/core/Conventions.java b/spring-core/src/main/java/org/springframework/core/Conventions.java index 2f84c97631e..e325561e8d4 100644 --- a/spring-core/src/main/java/org/springframework/core/Conventions.java +++ b/spring-core/src/main/java/org/springframework/core/Conventions.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -52,8 +52,8 @@ public abstract class Conventions { private static final Set> IGNORED_INTERFACES; static { - IGNORED_INTERFACES = Collections.unmodifiableSet(new HashSet>(Arrays.>asList( - Serializable.class, Externalizable.class, Cloneable.class, Comparable.class))); + IGNORED_INTERFACES = Collections.unmodifiableSet(new HashSet>( + Arrays.>asList(Serializable.class, Externalizable.class, Cloneable.class, Comparable.class))); } @@ -114,7 +114,7 @@ public abstract class Conventions { pluralize = true; } else if (Collection.class.isAssignableFrom(parameter.getParameterType())) { - valueClass = GenericCollectionTypeResolver.getCollectionParameterType(parameter); + valueClass = ResolvableType.forMethodParameter(parameter).asCollection().resolveGeneric(); if (valueClass == null) { throw new IllegalArgumentException( "Cannot generate variable name for non-typed Collection parameter type"); @@ -180,7 +180,7 @@ public abstract class Conventions { pluralize = true; } else if (Collection.class.isAssignableFrom(resolvedType)) { - valueClass = GenericCollectionTypeResolver.getCollectionReturnType(method); + valueClass = ResolvableType.forMethodReturnType(method).asCollection().resolveGeneric(); if (valueClass == null) { if (!(value instanceof Collection)) { throw new IllegalArgumentException( diff --git a/spring-core/src/main/java/org/springframework/core/GenericCollectionTypeResolver.java b/spring-core/src/main/java/org/springframework/core/GenericCollectionTypeResolver.java index 22d2a873fdb..3b6e0531959 100644 --- a/spring-core/src/main/java/org/springframework/core/GenericCollectionTypeResolver.java +++ b/spring-core/src/main/java/org/springframework/core/GenericCollectionTypeResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2017 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. @@ -32,7 +32,9 @@ import java.util.Map; * @author Phillip Webb * @since 2.0 * @see ResolvableType + * @deprecated as of 4.3.6, in favor of direct {@link ResolvableType} usage */ +@Deprecated public abstract class GenericCollectionTypeResolver { /** diff --git a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java index 74cfb0122cd..94032e48dfb 100644 --- a/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java +++ b/spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2017 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. @@ -39,7 +39,6 @@ import org.springframework.util.ConcurrentReferenceHashMap; * @author Sam Brannen * @author Phillip Webb * @since 2.5.2 - * @see GenericCollectionTypeResolver */ public abstract class GenericTypeResolver { diff --git a/spring-core/src/main/java/org/springframework/core/MethodParameter.java b/spring-core/src/main/java/org/springframework/core/MethodParameter.java index 48344a91ad9..51738dc593b 100644 --- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java +++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -43,7 +43,6 @@ import org.springframework.util.ClassUtils; * @author Andy Clement * @author Sam Brannen * @since 2.0 - * @see GenericCollectionTypeResolver * @see org.springframework.core.annotation.SynthesizingMethodParameter */ public class MethodParameter { diff --git a/spring-core/src/test/java/org/springframework/core/GenericCollectionTypeResolverTests.java b/spring-core/src/test/java/org/springframework/core/GenericCollectionTypeResolverTests.java index 59fb22ea113..973733e236d 100644 --- a/spring-core/src/test/java/org/springframework/core/GenericCollectionTypeResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/GenericCollectionTypeResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2017 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. @@ -38,6 +38,7 @@ import static org.junit.Assert.*; * @author Juergen Hoeller * @author Sam Brannen */ +@SuppressWarnings("deprecation") public class GenericCollectionTypeResolverTests { protected Class targetClass; @@ -46,31 +47,15 @@ public class GenericCollectionTypeResolverTests { protected Type[] expectedResults; + @Before public void setUp() throws Exception { this.targetClass = Foo.class; - this.methods = new String[] { "a", "b", "b2", "b3", "c", "d", "d2", "d3", "e", - "e2", "e3" }; - this.expectedResults = new Class[] { Integer.class, null, Set.class, Set.class, - null, Integer.class, Integer.class, Integer.class, Integer.class, - Integer.class, Integer.class }; - } - - protected void executeTest(String methodName) throws NoSuchMethodException { - for (int i = 0; i < this.methods.length; i++) { - if (methodName.equals(this.methods[i])) { - Method method = this.targetClass.getMethod(methodName); - Type type = getType(method); - assertEquals(this.expectedResults[i], type); - return; - } - } - throw new IllegalStateException("Bad test data"); + this.methods = new String[] {"a", "b", "b2", "b3", "c", "d", "d2", "d3", "e", "e2", "e3"}; + this.expectedResults = new Class[] {Integer.class, null, Set.class, Set.class, null, + Integer.class, Integer.class, Integer.class, Integer.class, Integer.class, Integer.class}; } - protected Type getType(Method method) { - return GenericCollectionTypeResolver.getMapValueReturnType(method); - } @Test public void a() throws Exception { @@ -130,10 +115,8 @@ public class GenericCollectionTypeResolverTests { @Test public void programmaticListIntrospection() throws Exception { Method setter = GenericObject.class.getMethod("setResourceList", List.class); - assertEquals( - Resource.class, - GenericCollectionTypeResolver.getCollectionParameterType(new MethodParameter( - setter, 0))); + assertEquals(Resource.class, + GenericCollectionTypeResolver.getCollectionParameterType(new MethodParameter(setter, 0))); Method getter = GenericObject.class.getMethod("getResourceList"); assertEquals(Resource.class, @@ -150,6 +133,24 @@ public class GenericCollectionTypeResolverTests { GenericCollectionTypeResolver.getMapValueType(CustomMap.class)); } + + protected void executeTest(String methodName) throws NoSuchMethodException { + for (int i = 0; i < this.methods.length; i++) { + if (methodName.equals(this.methods[i])) { + Method method = this.targetClass.getMethod(methodName); + Type type = getType(method); + assertEquals(this.expectedResults[i], type); + return; + } + } + throw new IllegalStateException("Bad test data"); + } + + protected Type getType(Method method) { + return GenericCollectionTypeResolver.getMapValueReturnType(method); + } + + private static abstract class CustomSet extends AbstractSet { } @@ -159,8 +160,9 @@ public class GenericCollectionTypeResolverTests { private static abstract class OtherCustomMap implements Map { } + @SuppressWarnings("rawtypes") - private static interface Foo { + private interface Foo { Map a(); diff --git a/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java b/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java index 42248276181..87aa34a1915 100644 --- a/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java +++ b/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -22,8 +22,8 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.Part; -import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.MethodParameter; +import org.springframework.core.ResolvableType; import org.springframework.util.ClassUtils; import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartFile; @@ -155,7 +155,7 @@ public abstract class MultipartResolutionDelegate { private static Class getCollectionParameterType(MethodParameter methodParam) { Class paramType = methodParam.getNestedParameterType(); if (Collection.class == paramType || List.class.isAssignableFrom(paramType)){ - Class valueType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam); + Class valueType = ResolvableType.forMethodParameter(methodParam).asCollection().resolveGeneric(); if (valueType != null) { return valueType; }