Fix binding of classpath*: to resource arrays and collections

Fixes gh-15835
This commit is contained in:
Andy Wilkinson 2023-10-16 11:27:01 +01:00
parent 339f75d309
commit 0e3a196af5
2 changed files with 71 additions and 6 deletions

View File

@ -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");
* 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.converter.ConditionalGenericConverter;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.io.Resource;
import org.springframework.util.CollectionUtils;
/**
@ -154,8 +155,8 @@ final class BindConverter {
private static class TypeConverterConversionService extends GenericConversionService {
TypeConverterConversionService(Consumer<PropertyEditorRegistry> initializer) {
addConverter(new TypeConverterConverter(initializer));
ApplicationConversionService.addDelimitedStringConverters(this);
addConverter(new TypeConverterConverter(initializer));
}
@Override
@ -196,16 +197,23 @@ final class BindConverter {
@Override
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
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
Class<?> type = targetType.getType();
if (type == null || type == Object.class || Collection.class.isAssignableFrom(type)
|| Map.class.isAssignableFrom(type)) {
if (type == null || type == Object.class || Map.class.isAssignableFrom(type)) {
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);
if (editor == null) {
editor = this.matchesOnlyTypeConverter.findCustomEditor(type, null);
@ -218,7 +226,7 @@ final class BindConverter {
@Override
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() {

View File

@ -23,6 +23,7 @@ import java.time.Duration;
import java.time.Period;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
@ -1173,6 +1174,22 @@ class ConfigurationPropertiesTests {
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) {
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;
}
}
}