Ensure conversion service actually converts to the correct type

Update `BindConverter` with a guard to ensure that the resulting object
is the correct type.

Fixes gh-28592
This commit is contained in:
Phillip Webb 2022-06-10 14:18:49 -07:00
parent e02803d341
commit 31b0264d94
3 changed files with 94 additions and 2 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors.
* Copyright 2012-2022 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.
@ -106,7 +106,10 @@ final class BindConverter {
for (ConversionService delegate : this.delegates) {
try {
if (delegate.canConvert(sourceType, targetType)) {
return delegate.convert(source, sourceType, targetType);
Object converted = delegate.convert(source, sourceType, targetType);
if (targetType.getType().isInstance(converted)) {
return converted;
}
}
}
catch (ConversionException ex) {

View File

@ -57,6 +57,7 @@ import org.springframework.boot.context.properties.bind.BindException;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.context.properties.bind.validation.BindValidationException;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.convert.ApplicationConversionService;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.boot.convert.DurationFormat;
import org.springframework.boot.convert.DurationStyle;
@ -1081,6 +1082,15 @@ class ConfigurationPropertiesTests {
assertThat(keys.stream().map(ConfigurationPropertyName::toString)).contains("name", "nested.name");
}
@Test // gh-28592
void loadWhenBindingWithCustomConverterAndObjectToObjectMethod() {
this.context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
load(WithCustomConverterAndObjectToObjectMethodConfiguration.class, "test.item=foo");
WithCustomConverterAndObjectToObjectMethodProperties bean = this.context
.getBean(WithCustomConverterAndObjectToObjectMethodProperties.class);
assertThat(bean.getItem().getValue()).isEqualTo("foo");
}
private AnnotationConfigApplicationContext load(Class<?> configuration, String... inlinedProperties) {
return load(new Class<?>[] { configuration }, inlinedProperties);
}
@ -2710,4 +2720,40 @@ class ConfigurationPropertiesTests {
}
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WithCustomConverterAndObjectToObjectMethodProperties.class)
static class WithCustomConverterAndObjectToObjectMethodConfiguration {
@Bean
@ConfigurationPropertiesBinding
WithObjectToObjectMethodConverter withObjectToObjectMethodConverter() {
return new WithObjectToObjectMethodConverter();
}
}
@ConfigurationProperties("test")
static class WithCustomConverterAndObjectToObjectMethodProperties {
private WithPublicObjectToObjectMethod item;
WithPublicObjectToObjectMethod getItem() {
return this.item;
}
void setItem(WithPublicObjectToObjectMethod item) {
this.item = item;
}
}
static class WithObjectToObjectMethodConverter implements Converter<String, WithPublicObjectToObjectMethod> {
@Override
public WithPublicObjectToObjectMethod convert(String source) {
return new WithPublicObjectToObjectMethod(source);
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2012-2022 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.context.properties;
import java.util.Optional;
/**
* Data object with a pubic method picked up by the {@code ObjectToObjectConverter}. Used
* in {@link ConfigurationPropertiesTests}.
*
* @author Phillip Webb
*/
public class WithPublicObjectToObjectMethod {
private final String value;
WithPublicObjectToObjectMethod(String value) {
this.value = value;
}
String getValue() {
return this.value;
}
public static Optional<WithPublicObjectToObjectMethod> from(String value) {
return Optional.of(new WithPublicObjectToObjectMethod(value));
}
}