Skip to content

Commit 92ef515

Browse files
committed
Fix @DefaultValue PropertyEditor based conversion
Update `ValueObjectBinder` reattempt conversion if the `@DefaultValue` contains a single element. Prior to this commit, single element conversion relied on the `ArrayToObjectConverter` which isn't always available. Fixes gh-21264
1 parent 6051a27 commit 92ef515

File tree

2 files changed

+64
-3
lines changed

2 files changed

+64
-3
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ValueObjectBinder.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.core.MethodParameter;
3737
import org.springframework.core.ParameterNameDiscoverer;
3838
import org.springframework.core.ResolvableType;
39+
import org.springframework.core.convert.ConversionException;
3940
import org.springframework.util.Assert;
4041

4142
/**
@@ -94,16 +95,30 @@ private <T> T getDefaultValue(Binder.Context context, ConstructorParameter param
9495
Annotation[] annotations = parameter.getAnnotations();
9596
for (Annotation annotation : annotations) {
9697
if (annotation instanceof DefaultValue) {
97-
DefaultValue defaultValue = (DefaultValue) annotation;
98-
if (defaultValue.value().length == 0) {
98+
String[] defaultValue = ((DefaultValue) annotation).value();
99+
if (defaultValue.length == 0) {
99100
return getNewInstanceIfPossible(context, type);
100101
}
101-
return context.getConverter().convert(defaultValue.value(), type, annotations);
102+
return convertDefaultValue(context.getConverter(), defaultValue, type, annotations);
102103
}
103104
}
104105
return null;
105106
}
106107

108+
private <T> T convertDefaultValue(BindConverter converter, String[] defaultValue, ResolvableType type,
109+
Annotation[] annotations) {
110+
try {
111+
return converter.convert(defaultValue, type, annotations);
112+
}
113+
catch (ConversionException ex) {
114+
// Try again in case ArrayToObjectConverter is not in play
115+
if (defaultValue.length == 1) {
116+
return converter.convert(defaultValue[0], type, annotations);
117+
}
118+
throw ex;
119+
}
120+
}
121+
107122
@SuppressWarnings("unchecked")
108123
private <T> T getNewInstanceIfPossible(Binder.Context context, ResolvableType type) {
109124
Class<T> resolved = (Class<T>) type.resolve();

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/bind/ValueObjectBinderTests.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
package org.springframework.boot.context.properties.bind;
1717

1818
import java.lang.reflect.Constructor;
19+
import java.nio.file.Path;
20+
import java.nio.file.Paths;
1921
import java.time.LocalDate;
2022
import java.util.ArrayList;
2123
import java.util.List;
@@ -307,6 +309,29 @@ void bindWhenPrimitiveParameterWithEmptyDefaultValueShouldThrowException() {
307309
.withStackTraceContaining("Parameter of type int must have a non-empty default value.");
308310
}
309311

312+
@Test
313+
void bindWhenBindingToPathTypeWithValue() { // gh-21263
314+
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
315+
source.put("test.name", "test");
316+
source.put("test.path", "specific_value");
317+
this.sources.add(source);
318+
Bindable<PathBean> target = Bindable.of(PathBean.class);
319+
PathBean bound = this.binder.bind("test", target).get();
320+
assertThat(bound.getName()).isEqualTo("test");
321+
assertThat(bound.getPath()).isEqualTo(Paths.get("specific_value"));
322+
}
323+
324+
@Test
325+
void bindWhenBindingToPathTypeWithDefaultValue() { // gh-21263
326+
MockConfigurationPropertySource source = new MockConfigurationPropertySource();
327+
source.put("test.name", "test");
328+
this.sources.add(source);
329+
Bindable<PathBean> target = Bindable.of(PathBean.class);
330+
PathBean bound = this.binder.bindOrCreate("test", target);
331+
assertThat(bound.getName()).isEqualTo("test");
332+
assertThat(bound.getPath()).isEqualTo(Paths.get("default_value"));
333+
}
334+
310335
private void noConfigurationProperty(BindException ex) {
311336
assertThat(ex.getProperty()).isNull();
312337
}
@@ -684,4 +709,25 @@ int getIntValue() {
684709

685710
}
686711

712+
static class PathBean {
713+
714+
private final String name;
715+
716+
private final Path path;
717+
718+
PathBean(String name, @DefaultValue("default_value") Path path) {
719+
this.name = name;
720+
this.path = path;
721+
}
722+
723+
String getName() {
724+
return this.name;
725+
}
726+
727+
Path getPath() {
728+
return this.path;
729+
}
730+
731+
}
732+
687733
}

0 commit comments

Comments
 (0)