diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java index 6cdf28479cb..fd6203054e1 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java @@ -227,10 +227,8 @@ public class Binder { } private T convert(Object value, Bindable target) { - if (value == null) { - return null; - } - return this.conversionService.convert(value, target); + return ResolvableTypeDescriptor.forBindable(target) + .convert(this.conversionService, value); } private Object bindObject(ConfigurationPropertyName name, Bindable target, @@ -287,7 +285,7 @@ public class Binder { context.setConfigurationProperty(property); Object result = property.getValue(); result = this.placeholdersResolver.resolvePlaceholders(result); - result = this.conversionService.convert(result, target); + result = convert(result, target); return result; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/IndexedElementsBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/IndexedElementsBinder.java index 702a9a36d20..d9e09fe87e7 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/IndexedElementsBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/IndexedElementsBinder.java @@ -122,11 +122,10 @@ abstract class IndexedElementsBinder extends AggregateBinder { } } - @SuppressWarnings("unchecked") private C convert(Object value, ResolvableType type) { value = getContext().getPlaceholdersResolver().resolvePlaceholders(value); BinderConversionService conversionService = getContext().getConversionService(); - return (C) conversionService.convert(value, type); + return ResolvableTypeDescriptor.forType(type).convert(conversionService, value); } /** diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java index c3f3c80a68b..c7bd8160708 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java @@ -20,7 +20,6 @@ import java.util.Collection; import java.util.Map; import java.util.Properties; -import org.springframework.boot.context.properties.bind.convert.BinderConversionService; import org.springframework.boot.context.properties.source.ConfigurationProperty; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertyName.Form; @@ -102,8 +101,7 @@ class MapBinder extends AggregateBinder> { for (ConfigurationPropertyName name : (IterableConfigurationPropertySource) source) { Bindable valueBindable = getValueBindable(name); ConfigurationPropertyName entryName = getEntryName(source, name); - Object key = getContext().getConversionService() - .convert(getKeyName(entryName), this.keyType); + Object key = convert(getKeyName(entryName), this.keyType); map.computeIfAbsent(key, (k) -> this.elementBinder.bind(entryName, valueBindable)); } @@ -159,9 +157,17 @@ class MapBinder extends AggregateBinder> { } Object value = property.getValue(); value = getContext().getPlaceholdersResolver().resolvePlaceholders(value); - BinderConversionService conversionService = getContext() - .getConversionService(); - return conversionService.canConvert(value, this.valueType); + return canConvert(value, this.valueType); + } + + private boolean canConvert(Object source, ResolvableType targetType) { + return ResolvableTypeDescriptor.forType(targetType) + .canConvert(getContext().getConversionService(), source); + } + + private Object convert(Object source, ResolvableType targetType) { + return ResolvableTypeDescriptor.forType(targetType) + .convert(getContext().getConversionService(), source); } private String getKeyName(ConfigurationPropertyName name) { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/convert/ResolvableTypeDescriptor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ResolvableTypeDescriptor.java similarity index 53% rename from spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/convert/ResolvableTypeDescriptor.java rename to spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ResolvableTypeDescriptor.java index 76fe621b91c..cfa4eb68582 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/convert/ResolvableTypeDescriptor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ResolvableTypeDescriptor.java @@ -14,12 +14,13 @@ * limitations under the License. */ -package org.springframework.boot.context.properties.bind.convert; +package org.springframework.boot.context.properties.bind; import java.lang.annotation.Annotation; -import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.core.ResolvableType; +import org.springframework.core.convert.ConversionException; +import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; /** @@ -35,12 +36,40 @@ final class ResolvableTypeDescriptor extends TypeDescriptor { super(resolvableType, null, annotations); } + /** + * Determine if the specified source object can be converted to this type. + * @param conversionService the backing conversion service + * @param source the source to check + * @return {@code true} if conversion can be performed + */ + public boolean canConvert(ConversionService conversionService, Object source) { + TypeDescriptor sourceType = TypeDescriptor.forObject(source); + return conversionService.canConvert(sourceType, this); + } + + /** + * Convert the given source object into this type. + * @param conversionService the source conversion service + * @param value the value to convert + * @param the target type + * @return the converted value + * @throws ConversionException if a conversion exception occurred + */ + @SuppressWarnings("unchecked") + public T convert(ConversionService conversionService, Object value) { + if (value == null) { + return null; + } + TypeDescriptor sourceType = TypeDescriptor.forObject(value); + return (T) conversionService.convert(value, sourceType, this); + } + /** * Create a {@link TypeDescriptor} for the specified {@link Bindable}. * @param bindable the bindable * @return the type descriptor */ - public static TypeDescriptor forBindable(Bindable bindable) { + public static ResolvableTypeDescriptor forBindable(Bindable bindable) { return forType(bindable.getType(), bindable.getAnnotations()); } @@ -50,7 +79,8 @@ final class ResolvableTypeDescriptor extends TypeDescriptor { * @param annotations the annotations to include * @return the type descriptor */ - public static TypeDescriptor forType(ResolvableType type, Annotation... annotations) { + public static ResolvableTypeDescriptor forType(ResolvableType type, + Annotation... annotations) { return new ResolvableTypeDescriptor(type, annotations); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/convert/BinderConversionService.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/convert/BinderConversionService.java index fcdf8be6d51..1ba51bc02f4 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/convert/BinderConversionService.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/convert/BinderConversionService.java @@ -18,9 +18,7 @@ package org.springframework.boot.context.properties.bind.convert; import java.util.function.Function; -import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.core.ResolvableType; import org.springframework.core.convert.ConversionException; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConverterNotFoundException; @@ -54,19 +52,6 @@ public class BinderConversionService implements ConversionService { this.additionalConversionService = createAdditionalConversionService(); } - /** - * Return {@code true} if the given source object can be converted to the - * {@code targetType}. - * @param source the source object - * @param targetType the target type to convert to (required) - * @return {@code true} if a conversion can be performed, {@code false} if not - * @throws IllegalArgumentException if {@code targetType} is {@code null} - */ - public boolean canConvert(Object source, ResolvableType targetType) { - TypeDescriptor sourceType = TypeDescriptor.forObject(source); - return canConvert(sourceType, ResolvableTypeDescriptor.forType(targetType)); - } - @Override public boolean canConvert(Class sourceType, Class targetType) { return (this.conversionService != null @@ -81,20 +66,6 @@ public class BinderConversionService implements ConversionService { || this.additionalConversionService.canConvert(sourceType, targetType); } - @SuppressWarnings("unchecked") - public T convert(Object value, ResolvableType type) { - TypeDescriptor sourceType = TypeDescriptor.forObject(value); - TypeDescriptor targetType = ResolvableTypeDescriptor.forType(type); - return (T) convert(value, sourceType, targetType); - } - - @SuppressWarnings("unchecked") - public T convert(Object value, Bindable bindable) { - TypeDescriptor sourceType = TypeDescriptor.forObject(value); - TypeDescriptor targetType = ResolvableTypeDescriptor.forBindable(bindable); - return (T) convert(value, sourceType, targetType); - } - @Override public T convert(Object source, Class targetType) { return callConversionService((c) -> c.convert(source, targetType)); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/convert/ResolvableTypeDescriptorTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ResolvableTypeDescriptorTests.java similarity index 94% rename from spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/convert/ResolvableTypeDescriptorTests.java rename to spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ResolvableTypeDescriptorTests.java index 12e0a8616e1..17fc48c2d64 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/convert/ResolvableTypeDescriptorTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ResolvableTypeDescriptorTests.java @@ -14,14 +14,13 @@ * limitations under the License. */ -package org.springframework.boot.context.properties.bind.convert; +package org.springframework.boot.context.properties.bind; import java.lang.annotation.Annotation; import java.util.List; import org.junit.Test; -import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.convert.TypeDescriptor;