Browse Source

Consistent type variable resolution for arrays/collections (in particular at field level)

Deprecating GenericCollectionTypeResolver in favor of direct ResolvableType usage.

Issue: SPR-15160
(cherry picked from commit 5e946c2)
pull/1316/head
Juergen Hoeller 9 years ago
parent
commit
3304efd64d
  1. 33
      spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
  2. 6
      spring-beans/src/main/java/org/springframework/beans/factory/config/ListFactoryBean.java
  3. 9
      spring-beans/src/main/java/org/springframework/beans/factory/config/MapFactoryBean.java
  4. 6
      spring-beans/src/main/java/org/springframework/beans/factory/config/SetFactoryBean.java
  5. 16
      spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
  6. 232
      spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java
  7. 10
      spring-context/src/main/java/org/springframework/jmx/access/MBeanClientInterceptor.java
  8. 10
      spring-core/src/main/java/org/springframework/core/Conventions.java
  9. 4
      spring-core/src/main/java/org/springframework/core/GenericCollectionTypeResolver.java
  10. 3
      spring-core/src/main/java/org/springframework/core/GenericTypeResolver.java
  11. 3
      spring-core/src/main/java/org/springframework/core/MethodParameter.java
  12. 54
      spring-core/src/test/java/org/springframework/core/GenericCollectionTypeResolverTests.java
  13. 6
      spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java

33
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"); * 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.
@ -28,7 +28,6 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.InjectionPoint; import org.springframework.beans.factory.InjectionPoint;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.GenericTypeResolver; import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer;
@ -63,6 +62,8 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
private Class<?> containingClass; private Class<?> containingClass;
private volatile ResolvableType resolvableType;
/** /**
* Create a new descriptor for a method or constructor parameter. * Create a new descriptor for a method or constructor parameter.
@ -214,6 +215,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
*/ */
public void increaseNestingLevel() { public void increaseNestingLevel() {
this.nestingLevel++; this.nestingLevel++;
this.resolvableType = null;
if (this.methodParameter != null) { if (this.methodParameter != null) {
this.methodParameter.increaseNestingLevel(); this.methodParameter.increaseNestingLevel();
} }
@ -227,6 +229,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
*/ */
public void setContainingClass(Class<?> containingClass) { public void setContainingClass(Class<?> containingClass) {
this.containingClass = containingClass; this.containingClass = containingClass;
this.resolvableType = null;
if (this.methodParameter != null) { if (this.methodParameter != null) {
GenericTypeResolver.resolveParameterType(this.methodParameter, containingClass); GenericTypeResolver.resolveParameterType(this.methodParameter, containingClass);
} }
@ -237,8 +240,12 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
* @since 4.0 * @since 4.0
*/ */
public ResolvableType getResolvableType() { public ResolvableType getResolvableType() {
return (this.field != null ? ResolvableType.forField(this.field, this.nestingLevel, this.containingClass) : if (this.resolvableType == null) {
ResolvableType.forMethodParameter(this.methodParameter)); 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. * Determine the generic element type of the wrapped Collection parameter/field, if any.
* @return the generic type, or {@code null} if none * @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() { public Class<?> getCollectionType() {
return (this.field != null ? return (this.field != null ?
GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel) : org.springframework.core.GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel) :
GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter)); org.springframework.core.GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter));
} }
/** /**
* Determine the generic key type of the wrapped Map parameter/field, if any. * Determine the generic key type of the wrapped Map parameter/field, if any.
* @return the generic type, or {@code null} if none * @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() { public Class<?> getMapKeyType() {
return (this.field != null ? return (this.field != null ?
GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel) : org.springframework.core.GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel) :
GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter)); org.springframework.core.GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter));
} }
/** /**
* Determine the generic value type of the wrapped Map parameter/field, if any. * Determine the generic value type of the wrapped Map parameter/field, if any.
* @return the generic type, or {@code null} if none * @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() { public Class<?> getMapValueType() {
return (this.field != null ? return (this.field != null ?
GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel) : org.springframework.core.GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel) :
GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter)); org.springframework.core.GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter));
} }

6
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"); * 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.
@ -21,7 +21,7 @@ import java.util.List;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.TypeConverter; 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 * Simple factory for shared List instances. Allows for central setup
@ -86,7 +86,7 @@ public class ListFactoryBean extends AbstractFactoryBean<List<Object>> {
} }
Class<?> valueType = null; Class<?> valueType = null;
if (this.targetListClass != null) { if (this.targetListClass != null) {
valueType = GenericCollectionTypeResolver.getCollectionType(this.targetListClass); valueType = ResolvableType.forClass(this.targetListClass).asCollection().resolveGeneric();
} }
if (valueType != null) { if (valueType != null) {
TypeConverter converter = getBeanTypeConverter(); TypeConverter converter = getBeanTypeConverter();

9
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"); * 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.
@ -21,7 +21,7 @@ import java.util.Map;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.TypeConverter; 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 * Simple factory for shared Map instances. Allows for central setup
@ -87,8 +87,9 @@ public class MapFactoryBean extends AbstractFactoryBean<Map<Object, Object>> {
Class<?> keyType = null; Class<?> keyType = null;
Class<?> valueType = null; Class<?> valueType = null;
if (this.targetMapClass != null) { if (this.targetMapClass != null) {
keyType = GenericCollectionTypeResolver.getMapKeyType(this.targetMapClass); ResolvableType mapType = ResolvableType.forClass(this.targetMapClass).asMap();
valueType = GenericCollectionTypeResolver.getMapValueType(this.targetMapClass); keyType = mapType.resolveGeneric(0);
valueType = mapType.resolveGeneric(1);
} }
if (keyType != null || valueType != null) { if (keyType != null || valueType != null) {
TypeConverter converter = getBeanTypeConverter(); TypeConverter converter = getBeanTypeConverter();

6
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"); * 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.
@ -21,7 +21,7 @@ import java.util.Set;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.TypeConverter; 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 * Simple factory for shared Set instances. Allows for central setup
@ -86,7 +86,7 @@ public class SetFactoryBean extends AbstractFactoryBean<Set<Object>> {
} }
Class<?> valueType = null; Class<?> valueType = null;
if (this.targetSetClass != null) { if (this.targetSetClass != null) {
valueType = GenericCollectionTypeResolver.getCollectionType(this.targetSetClass); valueType = ResolvableType.forClass(this.targetSetClass).asCollection().resolveGeneric();
} }
if (valueType != null) { if (valueType != null) {
TypeConverter converter = getBeanTypeConverter(); TypeConverter converter = getBeanTypeConverter();

16
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(); Class<?> type = descriptor.getDependencyType();
if (type.isArray()) { if (type.isArray()) {
Class<?> componentType = type.getComponentType(); 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<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType,
new MultiElementDescriptor(descriptor)); new MultiElementDescriptor(descriptor));
if (matchingBeans.isEmpty()) { if (matchingBeans.isEmpty()) {
@ -1164,7 +1173,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return result; return result;
} }
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) { else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
Class<?> elementType = descriptor.getCollectionType(); Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
if (elementType == null) { if (elementType == null) {
return null; return null;
} }
@ -1184,11 +1193,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
return result; return result;
} }
else if (Map.class == type) { else if (Map.class == type) {
Class<?> keyType = descriptor.getMapKeyType(); ResolvableType mapType = descriptor.getResolvableType().asMap();
Class<?> keyType = mapType.resolveGeneric(0);
if (String.class != keyType) { if (String.class != keyType) {
return null; return null;
} }
Class<?> valueType = descriptor.getMapValueType(); Class<?> valueType = mapType.resolveGeneric(1);
if (valueType == null) { if (valueType == null) {
return null; return null;
} }

232
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); RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBean.class);
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd); bf.registerBeanDefinition("annotatedBean", bd);
String sv = "X";
bf.registerSingleton("stringValue", sv);
Integer iv = 1;
bf.registerSingleton("integerValue", iv);
StringRepository sr = new StringRepository(); StringRepository sr = new StringRepository();
bf.registerSingleton("stringRepo", sr); bf.registerSingleton("stringRepo", sr);
IntegerRepository ir = new IntegerRepository(); IntegerRepository ir = new IntegerRepository();
bf.registerSingleton("integerRepo", ir); bf.registerSingleton("integerRepo", ir);
RepositoryFieldInjectionBean bean = (RepositoryFieldInjectionBean) bf.getBean("annotatedBean"); 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(sr, bean.stringRepository);
assertSame(ir, bean.integerRepository); assertSame(ir, bean.integerRepository);
assertSame(1, bean.stringRepositoryArray.length); assertSame(1, bean.stringRepositoryArray.length);
@ -1624,12 +1642,30 @@ public class AutowiredAnnotationBeanPostProcessorTests {
RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBeanWithSubstitutedVariables.class); RootBeanDefinition bd = new RootBeanDefinition(RepositoryFieldInjectionBeanWithSubstitutedVariables.class);
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd); bf.registerBeanDefinition("annotatedBean", bd);
String sv = "X";
bf.registerSingleton("stringValue", sv);
Integer iv = 1;
bf.registerSingleton("integerValue", iv);
StringRepository sr = new StringRepository(); StringRepository sr = new StringRepository();
bf.registerSingleton("stringRepo", sr); bf.registerSingleton("stringRepo", sr);
IntegerRepository ir = new IntegerRepository(); IntegerRepository ir = new IntegerRepository();
bf.registerSingleton("integerRepo", ir); bf.registerSingleton("integerRepo", ir);
RepositoryFieldInjectionBeanWithSubstitutedVariables bean = (RepositoryFieldInjectionBeanWithSubstitutedVariables) bf.getBean("annotatedBean"); 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(sr, bean.stringRepository);
assertSame(ir, bean.integerRepository); assertSame(ir, bean.integerRepository);
assertSame(1, bean.stringRepositoryArray.length); assertSame(1, bean.stringRepositoryArray.length);
@ -1872,12 +1908,30 @@ public class AutowiredAnnotationBeanPostProcessorTests {
RootBeanDefinition bd = new RootBeanDefinition(RepositoryMethodInjectionBean.class); RootBeanDefinition bd = new RootBeanDefinition(RepositoryMethodInjectionBean.class);
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd); bf.registerBeanDefinition("annotatedBean", bd);
String sv = "X";
bf.registerSingleton("stringValue", sv);
Integer iv = 1;
bf.registerSingleton("integerValue", iv);
StringRepository sr = new StringRepository(); StringRepository sr = new StringRepository();
bf.registerSingleton("stringRepo", sr); bf.registerSingleton("stringRepo", sr);
IntegerRepository ir = new IntegerRepository(); IntegerRepository ir = new IntegerRepository();
bf.registerSingleton("integerRepo", ir); bf.registerSingleton("integerRepo", ir);
RepositoryMethodInjectionBean bean = (RepositoryMethodInjectionBean) bf.getBean("annotatedBean"); 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(sr, bean.stringRepository);
assertSame(ir, bean.integerRepository); assertSame(ir, bean.integerRepository);
assertSame(1, bean.stringRepositoryArray.length); assertSame(1, bean.stringRepositoryArray.length);
@ -1904,12 +1958,30 @@ public class AutowiredAnnotationBeanPostProcessorTests {
RootBeanDefinition bd = new RootBeanDefinition(RepositoryMethodInjectionBeanWithSubstitutedVariables.class); RootBeanDefinition bd = new RootBeanDefinition(RepositoryMethodInjectionBeanWithSubstitutedVariables.class);
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
bf.registerBeanDefinition("annotatedBean", bd); bf.registerBeanDefinition("annotatedBean", bd);
String sv = "X";
bf.registerSingleton("stringValue", sv);
Integer iv = 1;
bf.registerSingleton("integerValue", iv);
StringRepository sr = new StringRepository(); StringRepository sr = new StringRepository();
bf.registerSingleton("stringRepo", sr); bf.registerSingleton("stringRepo", sr);
IntegerRepository ir = new IntegerRepository(); IntegerRepository ir = new IntegerRepository();
bf.registerSingleton("integerRepo", ir); bf.registerSingleton("integerRepo", ir);
RepositoryMethodInjectionBeanWithSubstitutedVariables bean = (RepositoryMethodInjectionBeanWithSubstitutedVariables) bf.getBean("annotatedBean"); 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(sr, bean.stringRepository);
assertSame(ir, bean.integerRepository); assertSame(ir, bean.integerRepository);
assertSame(1, bean.stringRepositoryArray.length); assertSame(1, bean.stringRepositoryArray.length);
@ -2968,6 +3040,30 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public static class RepositoryFieldInjectionBean { public static class RepositoryFieldInjectionBean {
@Autowired
public String string;
@Autowired
public Integer integer;
@Autowired
public String[] stringArray;
@Autowired
public Integer[] integerArray;
@Autowired
public List<String> stringList;
@Autowired
public List<Integer> integerList;
@Autowired
public Map<String, String> stringMap;
@Autowired
public Map<String, Integer> integerMap;
@Autowired @Autowired
public Repository<String> stringRepository; public Repository<String> stringRepository;
@ -2996,6 +3092,30 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public static class RepositoryFieldInjectionBeanWithVariables<S, I> { public static class RepositoryFieldInjectionBeanWithVariables<S, I> {
@Autowired
public S string;
@Autowired
public I integer;
@Autowired
public S[] stringArray;
@Autowired
public I[] integerArray;
@Autowired
public List<S> stringList;
@Autowired
public List<I> integerList;
@Autowired
public Map<String, S> stringMap;
@Autowired
public Map<String, I> integerMap;
@Autowired @Autowired
public Repository<S> stringRepository; public Repository<S> stringRepository;
@ -3092,6 +3212,22 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public static class RepositoryMethodInjectionBean { public static class RepositoryMethodInjectionBean {
public String string;
public Integer integer;
public String[] stringArray;
public Integer[] integerArray;
public List<String> stringList;
public List<Integer> integerList;
public Map<String, String> stringMap;
public Map<String, Integer> integerMap;
public Repository<String> stringRepository; public Repository<String> stringRepository;
public Repository<Integer> integerRepository; public Repository<Integer> integerRepository;
@ -3108,6 +3244,46 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public Map<String, Repository<Integer>> integerRepositoryMap; public Map<String, Repository<Integer>> 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<String> stringList) {
this.stringList = stringList;
}
@Autowired
public void setIntegerList(List<Integer> integerList) {
this.integerList = integerList;
}
@Autowired
public void setStringMap(Map<String, String> stringMap) {
this.stringMap = stringMap;
}
@Autowired
public void setIntegerMap(Map<String, Integer> integerMap) {
this.integerMap = integerMap;
}
@Autowired @Autowired
public void setStringRepository(Repository<String> stringRepository) { public void setStringRepository(Repository<String> stringRepository) {
this.stringRepository = stringRepository; this.stringRepository = stringRepository;
@ -3152,6 +3328,22 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public static class RepositoryMethodInjectionBeanWithVariables<S, I> { public static class RepositoryMethodInjectionBeanWithVariables<S, I> {
public S string;
public I integer;
public S[] stringArray;
public I[] integerArray;
public List<S> stringList;
public List<I> integerList;
public Map<String, S> stringMap;
public Map<String, I> integerMap;
public Repository<S> stringRepository; public Repository<S> stringRepository;
public Repository<I> integerRepository; public Repository<I> integerRepository;
@ -3168,6 +3360,46 @@ public class AutowiredAnnotationBeanPostProcessorTests {
public Map<String, Repository<I>> integerRepositoryMap; public Map<String, Repository<I>> 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<S> stringList) {
this.stringList = stringList;
}
@Autowired
public void setIntegerList(List<I> integerList) {
this.integerList = integerList;
}
@Autowired
public void setStringMap(Map<String, S> stringMap) {
this.stringMap = stringMap;
}
@Autowired
public void setIntegerMap(Map<String, I> integerMap) {
this.integerMap = integerMap;
}
@Autowired @Autowired
public void setStringRepository(Repository<S> stringRepository) { public void setStringRepository(Repository<S> stringRepository) {
this.stringRepository = stringRepository; this.stringRepository = stringRepository;

10
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"); * 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.
@ -57,8 +57,8 @@ import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.CollectionFactory; import org.springframework.core.CollectionFactory;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.jmx.support.JmxUtils; import org.springframework.jmx.support.JmxUtils;
import org.springframework.jmx.support.ObjectNameManager; import org.springframework.jmx.support.ObjectNameManager;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
@ -546,7 +546,8 @@ public class MBeanClientInterceptor
return convertDataArrayToTargetArray(array, targetClass); return convertDataArrayToTargetArray(array, targetClass);
} }
else if (Collection.class.isAssignableFrom(targetClass)) { else if (Collection.class.isAssignableFrom(targetClass)) {
Class<?> elementType = GenericCollectionTypeResolver.getCollectionParameterType(parameter); Class<?> elementType =
ResolvableType.forMethodParameter(parameter).asCollection().resolveGeneric();
if (elementType != null) { if (elementType != null) {
return convertDataArrayToTargetCollection(array, targetClass, elementType); return convertDataArrayToTargetCollection(array, targetClass, elementType);
} }
@ -562,7 +563,8 @@ public class MBeanClientInterceptor
return convertDataArrayToTargetArray(array, targetClass); return convertDataArrayToTargetArray(array, targetClass);
} }
else if (Collection.class.isAssignableFrom(targetClass)) { else if (Collection.class.isAssignableFrom(targetClass)) {
Class<?> elementType = GenericCollectionTypeResolver.getCollectionParameterType(parameter); Class<?> elementType =
ResolvableType.forMethodParameter(parameter).asCollection().resolveGeneric();
if (elementType != null) { if (elementType != null) {
return convertDataArrayToTargetCollection(array, targetClass, elementType); return convertDataArrayToTargetCollection(array, targetClass, elementType);
} }

10
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"); * 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.
@ -52,8 +52,8 @@ public abstract class Conventions {
private static final Set<Class<?>> IGNORED_INTERFACES; private static final Set<Class<?>> IGNORED_INTERFACES;
static { static {
IGNORED_INTERFACES = Collections.unmodifiableSet(new HashSet<Class<?>>(Arrays.<Class<?>>asList( IGNORED_INTERFACES = Collections.unmodifiableSet(new HashSet<Class<?>>(
Serializable.class, Externalizable.class, Cloneable.class, Comparable.class))); Arrays.<Class<?>>asList(Serializable.class, Externalizable.class, Cloneable.class, Comparable.class)));
} }
@ -114,7 +114,7 @@ public abstract class Conventions {
pluralize = true; pluralize = true;
} }
else if (Collection.class.isAssignableFrom(parameter.getParameterType())) { else if (Collection.class.isAssignableFrom(parameter.getParameterType())) {
valueClass = GenericCollectionTypeResolver.getCollectionParameterType(parameter); valueClass = ResolvableType.forMethodParameter(parameter).asCollection().resolveGeneric();
if (valueClass == null) { if (valueClass == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Cannot generate variable name for non-typed Collection parameter type"); "Cannot generate variable name for non-typed Collection parameter type");
@ -180,7 +180,7 @@ public abstract class Conventions {
pluralize = true; pluralize = true;
} }
else if (Collection.class.isAssignableFrom(resolvedType)) { else if (Collection.class.isAssignableFrom(resolvedType)) {
valueClass = GenericCollectionTypeResolver.getCollectionReturnType(method); valueClass = ResolvableType.forMethodReturnType(method).asCollection().resolveGeneric();
if (valueClass == null) { if (valueClass == null) {
if (!(value instanceof Collection)) { if (!(value instanceof Collection)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(

4
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"); * 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.
@ -32,7 +32,9 @@ import java.util.Map;
* @author Phillip Webb * @author Phillip Webb
* @since 2.0 * @since 2.0
* @see ResolvableType * @see ResolvableType
* @deprecated as of 4.3.6, in favor of direct {@link ResolvableType} usage
*/ */
@Deprecated
public abstract class GenericCollectionTypeResolver { public abstract class GenericCollectionTypeResolver {
/** /**

3
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"); * 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.
@ -39,7 +39,6 @@ import org.springframework.util.ConcurrentReferenceHashMap;
* @author Sam Brannen * @author Sam Brannen
* @author Phillip Webb * @author Phillip Webb
* @since 2.5.2 * @since 2.5.2
* @see GenericCollectionTypeResolver
*/ */
public abstract class GenericTypeResolver { public abstract class GenericTypeResolver {

3
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"); * 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.
@ -43,7 +43,6 @@ import org.springframework.util.ClassUtils;
* @author Andy Clement * @author Andy Clement
* @author Sam Brannen * @author Sam Brannen
* @since 2.0 * @since 2.0
* @see GenericCollectionTypeResolver
* @see org.springframework.core.annotation.SynthesizingMethodParameter * @see org.springframework.core.annotation.SynthesizingMethodParameter
*/ */
public class MethodParameter { public class MethodParameter {

54
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"); * 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.
@ -38,6 +38,7 @@ import static org.junit.Assert.*;
* @author Juergen Hoeller * @author Juergen Hoeller
* @author Sam Brannen * @author Sam Brannen
*/ */
@SuppressWarnings("deprecation")
public class GenericCollectionTypeResolverTests { public class GenericCollectionTypeResolverTests {
protected Class<?> targetClass; protected Class<?> targetClass;
@ -46,31 +47,15 @@ public class GenericCollectionTypeResolverTests {
protected Type[] expectedResults; protected Type[] expectedResults;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
this.targetClass = Foo.class; this.targetClass = Foo.class;
this.methods = new String[] { "a", "b", "b2", "b3", "c", "d", "d2", "d3", "e", this.methods = new String[] {"a", "b", "b2", "b3", "c", "d", "d2", "d3", "e", "e2", "e3"};
"e2", "e3" }; this.expectedResults = new Class<?>[] {Integer.class, null, Set.class, Set.class, null,
this.expectedResults = new Class[] { Integer.class, null, Set.class, Set.class, Integer.class, Integer.class, Integer.class, Integer.class, Integer.class, Integer.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");
} }
protected Type getType(Method method) {
return GenericCollectionTypeResolver.getMapValueReturnType(method);
}
@Test @Test
public void a() throws Exception { public void a() throws Exception {
@ -130,10 +115,8 @@ public class GenericCollectionTypeResolverTests {
@Test @Test
public void programmaticListIntrospection() throws Exception { public void programmaticListIntrospection() throws Exception {
Method setter = GenericObject.class.getMethod("setResourceList", List.class); Method setter = GenericObject.class.getMethod("setResourceList", List.class);
assertEquals( assertEquals(Resource.class,
Resource.class, GenericCollectionTypeResolver.getCollectionParameterType(new MethodParameter(setter, 0)));
GenericCollectionTypeResolver.getCollectionParameterType(new MethodParameter(
setter, 0)));
Method getter = GenericObject.class.getMethod("getResourceList"); Method getter = GenericObject.class.getMethod("getResourceList");
assertEquals(Resource.class, assertEquals(Resource.class,
@ -150,6 +133,24 @@ public class GenericCollectionTypeResolverTests {
GenericCollectionTypeResolver.getMapValueType(CustomMap.class)); 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<T> extends AbstractSet<String> { private static abstract class CustomSet<T> extends AbstractSet<String> {
} }
@ -159,8 +160,9 @@ public class GenericCollectionTypeResolverTests {
private static abstract class OtherCustomMap<T> implements Map<String, Integer> { private static abstract class OtherCustomMap<T> implements Map<String, Integer> {
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private static interface Foo { private interface Foo {
Map<String, Integer> a(); Map<String, Integer> a();

6
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"); * 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.
@ -22,8 +22,8 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part; import javax.servlet.http.Part;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.web.multipart.MultipartException; import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -155,7 +155,7 @@ public abstract class MultipartResolutionDelegate {
private static Class<?> getCollectionParameterType(MethodParameter methodParam) { private static Class<?> getCollectionParameterType(MethodParameter methodParam) {
Class<?> paramType = methodParam.getNestedParameterType(); Class<?> paramType = methodParam.getNestedParameterType();
if (Collection.class == paramType || List.class.isAssignableFrom(paramType)){ if (Collection.class == paramType || List.class.isAssignableFrom(paramType)){
Class<?> valueType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam); Class<?> valueType = ResolvableType.forMethodParameter(methodParam).asCollection().resolveGeneric();
if (valueType != null) { if (valueType != null) {
return valueType; return valueType;
} }

Loading…
Cancel
Save