diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java index 1f8fb61cd8..e4b445751c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java @@ -35,6 +35,7 @@ import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; +import org.springframework.util.CollectionUtils; import org.springframework.util.NumberUtils; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; @@ -139,13 +140,17 @@ class TypeConverterDelegate { // Value not of required type? if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) { - if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) && - convertedValue instanceof String text) { + if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType)) { TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor(); if (elementTypeDesc != null) { Class elementType = elementTypeDesc.getType(); - if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) { - convertedValue = StringUtils.commaDelimitedListToStringArray(text); + if (convertedValue instanceof String text) { + if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) { + convertedValue = StringUtils.commaDelimitedListToStringArray(text); + } + if (editor == null && String.class != elementType) { + editor = findDefaultEditor(Array.newInstance(elementType, 0).getClass()); + } } } } @@ -166,11 +171,23 @@ class TypeConverterDelegate { } else if (requiredType.isArray()) { // Array required -> apply appropriate conversion of elements. - if (convertedValue instanceof String text && Enum.class.isAssignableFrom(requiredType.getComponentType())) { + if (convertedValue instanceof String text && + Enum.class.isAssignableFrom(requiredType.getComponentType())) { convertedValue = StringUtils.commaDelimitedListToStringArray(text); } return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType()); } + else if (convertedValue.getClass().isArray()) { + if (Array.getLength(convertedValue) == 1) { + convertedValue = Array.get(convertedValue, 0); + standardConversion = true; + } + else if (Collection.class.isAssignableFrom(requiredType)) { + convertedValue = convertToTypedCollection(CollectionUtils.arrayToList(convertedValue), + propertyName, requiredType, typeDescriptor); + standardConversion = true; + } + } else if (convertedValue instanceof Collection coll) { // Convert elements to target type, if determined. convertedValue = convertToTypedCollection(coll, propertyName, requiredType, typeDescriptor); @@ -181,10 +198,6 @@ class TypeConverterDelegate { convertedValue = convertToTypedMap(map, propertyName, requiredType, typeDescriptor); standardConversion = true; } - if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) { - convertedValue = Array.get(convertedValue, 0); - standardConversion = true; - } if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) { // We can stringify any primitive value... return (T) convertedValue.toString(); @@ -501,12 +514,11 @@ class TypeConverterDelegate { Collection convertedCopy; try { - if (approximable) { + if (approximable && requiredType.isInstance(original)) { convertedCopy = CollectionFactory.createApproximateCollection(original, original.size()); } else { - convertedCopy = (Collection) - ReflectionUtils.accessibleConstructor(requiredType).newInstance(); + convertedCopy = CollectionFactory.createCollection(requiredType, original.size()); } } catch (Throwable ex) { @@ -576,12 +588,11 @@ class TypeConverterDelegate { Map convertedCopy; try { - if (approximable) { + if (approximable && requiredType.isInstance(original)) { convertedCopy = CollectionFactory.createApproximateMap(original, original.size()); } else { - convertedCopy = (Map) - ReflectionUtils.accessibleConstructor(requiredType).newInstance(); + convertedCopy = CollectionFactory.createMap(requiredType, original.size()); } } catch (Throwable ex) { diff --git a/spring-context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests.java b/spring-context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests.java index 655c91de03..170b1b6561 100644 --- a/spring-context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests.java +++ b/spring-context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests.java @@ -222,11 +222,12 @@ public class ClassPathXmlApplicationContextTests { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(CONTEXT_WILDCARD); Service service = ctx.getBean("service", Service.class); assertThat(service.getResources()).containsExactlyInAnyOrder(contextA, contextB, contextC); + assertThat(service.getResourceSet()).containsExactlyInAnyOrder(contextA, contextB, contextC); ctx.close(); } @Test - void childWithProxy() throws Exception { + void childWithProxy() { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(CONTEXT_WILDCARD); ClassPathXmlApplicationContext child = new ClassPathXmlApplicationContext( new String[] {CHILD_WITH_PROXY_CONTEXT}, ctx); @@ -337,8 +338,7 @@ public class ClassPathXmlApplicationContextTests { }; ResourceTestBean resource1 = (ResourceTestBean) ctx.getBean("resource1"); ResourceTestBean resource2 = (ResourceTestBean) ctx.getBean("resource2"); - boolean condition = resource1.getResource() instanceof ClassPathResource; - assertThat(condition).isTrue(); + assertThat(resource1.getResource()).isInstanceOf(ClassPathResource.class); StringWriter writer = new StringWriter(); FileCopyUtils.copy(new InputStreamReader(resource1.getResource().getInputStream()), writer); assertThat(writer.toString()).isEqualTo("contexttest"); diff --git a/spring-context/src/test/java/org/springframework/context/support/ConversionServiceFactoryBeanTests.java b/spring-context/src/test/java/org/springframework/context/support/ConversionServiceFactoryBeanTests.java index 233ecc98ce..697e4fc834 100644 --- a/spring-context/src/test/java/org/springframework/context/support/ConversionServiceFactoryBeanTests.java +++ b/spring-context/src/test/java/org/springframework/context/support/ConversionServiceFactoryBeanTests.java @@ -118,7 +118,7 @@ class ConversionServiceFactoryBeanTests { ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(fileName, getClass()); ResourceTestBean tb = ctx.getBean("resourceTestBean", ResourceTestBean.class); assertThat(resourceClass.isInstance(tb.getResource())).isTrue(); - assertThat(tb.getResourceArray()).isNotEmpty(); + assertThat(tb.getResourceArray()).hasSize(1); assertThat(resourceClass.isInstance(tb.getResourceArray()[0])).isTrue(); assertThat(tb.getResourceMap()).hasSize(1); assertThat(resourceClass.isInstance(tb.getResourceMap().get("key1"))).isTrue(); diff --git a/spring-context/src/test/java/org/springframework/context/support/Service.java b/spring-context/src/test/java/org/springframework/context/support/Service.java index 680e8091ec..7ea2af3745 100644 --- a/spring-context/src/test/java/org/springframework/context/support/Service.java +++ b/spring-context/src/test/java/org/springframework/context/support/Service.java @@ -16,6 +16,8 @@ package org.springframework.context.support; +import java.util.Set; + import org.springframework.beans.factory.BeanCreationNotAllowedException; import org.springframework.beans.factory.DisposableBean; import org.springframework.context.ApplicationContext; @@ -37,6 +39,8 @@ public class Service implements ApplicationContextAware, MessageSourceAware, Dis private Resource[] resources; + private Set resourceSet; + private boolean properlyDestroyed = false; @@ -65,6 +69,14 @@ public class Service implements ApplicationContextAware, MessageSourceAware, Dis return resources; } + public void setResourceSet(Set resourceSet) { + this.resourceSet = resourceSet; + } + + public Set getResourceSet() { + return resourceSet; + } + @Override public void destroy() { diff --git a/spring-context/src/test/resources/org/springframework/context/support/test/contextA.xml b/spring-context/src/test/resources/org/springframework/context/support/test/contextA.xml index 0ac88a5ffa..d58a2e8258 100644 --- a/spring-context/src/test/resources/org/springframework/context/support/test/contextA.xml +++ b/spring-context/src/test/resources/org/springframework/context/support/test/contextA.xml @@ -17,6 +17,7 @@ +