Fix binding of classpath*: to resource arrays and collections
Fixes gh-15835
This commit is contained in:
		
							parent
							
								
									339f75d309
								
							
						
					
					
						commit
						0e3a196af5
					
				|  | @ -1,5 +1,5 @@ | ||||||
| /* | /* | ||||||
|  * Copyright 2012-2022 the original author or authors. |  * Copyright 2012-2023 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. | ||||||
|  | @ -42,6 +42,7 @@ import org.springframework.core.convert.ConverterNotFoundException; | ||||||
| import org.springframework.core.convert.TypeDescriptor; | import org.springframework.core.convert.TypeDescriptor; | ||||||
| import org.springframework.core.convert.converter.ConditionalGenericConverter; | import org.springframework.core.convert.converter.ConditionalGenericConverter; | ||||||
| import org.springframework.core.convert.support.GenericConversionService; | import org.springframework.core.convert.support.GenericConversionService; | ||||||
|  | import org.springframework.core.io.Resource; | ||||||
| import org.springframework.util.CollectionUtils; | import org.springframework.util.CollectionUtils; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -154,8 +155,8 @@ final class BindConverter { | ||||||
| 	private static class TypeConverterConversionService extends GenericConversionService { | 	private static class TypeConverterConversionService extends GenericConversionService { | ||||||
| 
 | 
 | ||||||
| 		TypeConverterConversionService(Consumer<PropertyEditorRegistry> initializer) { | 		TypeConverterConversionService(Consumer<PropertyEditorRegistry> initializer) { | ||||||
| 			addConverter(new TypeConverterConverter(initializer)); |  | ||||||
| 			ApplicationConversionService.addDelimitedStringConverters(this); | 			ApplicationConversionService.addDelimitedStringConverters(this); | ||||||
|  | 			addConverter(new TypeConverterConverter(initializer)); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		@Override | 		@Override | ||||||
|  | @ -196,16 +197,23 @@ final class BindConverter { | ||||||
| 
 | 
 | ||||||
| 		@Override | 		@Override | ||||||
| 		public Set<ConvertiblePair> getConvertibleTypes() { | 		public Set<ConvertiblePair> getConvertibleTypes() { | ||||||
| 			return Collections.singleton(new ConvertiblePair(String.class, Object.class)); | 			return Set.of(new ConvertiblePair(String.class, Object.class), | ||||||
|  | 					new ConvertiblePair(String.class, Object[].class), | ||||||
|  | 					new ConvertiblePair(String.class, Collection.class)); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		@Override | 		@Override | ||||||
| 		public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { | 		public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { | ||||||
| 			Class<?> type = targetType.getType(); | 			Class<?> type = targetType.getType(); | ||||||
| 			if (type == null || type == Object.class || Collection.class.isAssignableFrom(type) | 			if (type == null || type == Object.class || Map.class.isAssignableFrom(type)) { | ||||||
| 					|| Map.class.isAssignableFrom(type)) { |  | ||||||
| 				return false; | 				return false; | ||||||
| 			} | 			} | ||||||
|  | 			if (Collection.class.isAssignableFrom(type)) { | ||||||
|  | 				TypeDescriptor elementType = targetType.getElementTypeDescriptor(); | ||||||
|  | 				if (elementType == null || (!Resource.class.isAssignableFrom(elementType.getType()))) { | ||||||
|  | 					return false; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 			PropertyEditor editor = this.matchesOnlyTypeConverter.getDefaultEditor(type); | 			PropertyEditor editor = this.matchesOnlyTypeConverter.getDefaultEditor(type); | ||||||
| 			if (editor == null) { | 			if (editor == null) { | ||||||
| 				editor = this.matchesOnlyTypeConverter.findCustomEditor(type, null); | 				editor = this.matchesOnlyTypeConverter.findCustomEditor(type, null); | ||||||
|  | @ -218,7 +226,7 @@ final class BindConverter { | ||||||
| 
 | 
 | ||||||
| 		@Override | 		@Override | ||||||
| 		public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { | 		public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { | ||||||
| 			return createTypeConverter().convertIfNecessary(source, targetType.getType()); | 			return createTypeConverter().convertIfNecessary(source, targetType.getType(), targetType); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		private SimpleTypeConverter createTypeConverter() { | 		private SimpleTypeConverter createTypeConverter() { | ||||||
|  |  | ||||||
|  | @ -23,6 +23,7 @@ import java.time.Duration; | ||||||
| import java.time.Period; | import java.time.Period; | ||||||
| import java.time.temporal.ChronoUnit; | import java.time.temporal.ChronoUnit; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
|  | import java.util.Collection; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||||
|  | @ -1173,6 +1174,22 @@ class ConfigurationPropertiesTests { | ||||||
| 		assertThat(properties.getProp()).isEqualTo("alpha"); | 		assertThat(properties.getProp()).isEqualTo("alpha"); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@Test | ||||||
|  | 	void loadWhenBindingClasspathPatternToResourceArrayShouldBindMultipleValues() { | ||||||
|  | 		load(ResourceArrayPropertiesConfiguration.class, | ||||||
|  | 				"test.resources=classpath*:org/springframework/boot/context/properties/*.class"); | ||||||
|  | 		ResourceArrayProperties properties = this.context.getBean(ResourceArrayProperties.class); | ||||||
|  | 		assertThat(properties.getResources()).hasSizeGreaterThan(1); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	void loadWhenBindingClasspathPatternToResourceCollectionShouldBindMultipleValues() { | ||||||
|  | 		load(ResourceCollectionPropertiesConfiguration.class, | ||||||
|  | 				"test.resources=classpath*:org/springframework/boot/context/properties/*.class"); | ||||||
|  | 		ResourceCollectionProperties properties = this.context.getBean(ResourceCollectionProperties.class); | ||||||
|  | 		assertThat(properties.getResources()).hasSizeGreaterThan(1); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	private AnnotationConfigApplicationContext load(Class<?> configuration, String... inlinedProperties) { | 	private AnnotationConfigApplicationContext load(Class<?> configuration, String... inlinedProperties) { | ||||||
| 		return load(new Class<?>[] { configuration }, inlinedProperties); | 		return load(new Class<?>[] { configuration }, inlinedProperties); | ||||||
| 	} | 	} | ||||||
|  | @ -3058,4 +3075,44 @@ class ConfigurationPropertiesTests { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	@EnableConfigurationProperties(ResourceArrayProperties.class) | ||||||
|  | 	static class ResourceArrayPropertiesConfiguration { | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@ConfigurationProperties("test") | ||||||
|  | 	static class ResourceArrayProperties { | ||||||
|  | 
 | ||||||
|  | 		private Resource[] resources; | ||||||
|  | 
 | ||||||
|  | 		Resource[] getResources() { | ||||||
|  | 			return this.resources; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		void setResources(Resource[] resources) { | ||||||
|  | 			this.resources = resources; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@EnableConfigurationProperties(ResourceCollectionProperties.class) | ||||||
|  | 	static class ResourceCollectionPropertiesConfiguration { | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@ConfigurationProperties("test") | ||||||
|  | 	static class ResourceCollectionProperties { | ||||||
|  | 
 | ||||||
|  | 		private Collection<Resource> resources; | ||||||
|  | 
 | ||||||
|  | 		Collection<Resource> getResources() { | ||||||
|  | 			return this.resources; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		void setResources(Collection<Resource> resources) { | ||||||
|  | 			this.resources = resources; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue