Skip to content

Commit 1db2f5f

Browse files
committed
Support Formatter conversion service beans
Update `ConversionServiceDeducer` to also include `Formatter` beans when they are qualified with `@ConfigurationPropertiesBinding`. Fixes gh-23576
1 parent 2063567 commit 1db2f5f

File tree

2 files changed

+58
-5
lines changed

2 files changed

+58
-5
lines changed

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.core.convert.ConversionService;
3131
import org.springframework.core.convert.converter.Converter;
3232
import org.springframework.core.convert.converter.GenericConverter;
33+
import org.springframework.format.Formatter;
3334

3435
/**
3536
* Utility to deduce the {@link ConversionService} to use for configuration properties
@@ -62,9 +63,13 @@ private static class Factory {
6263

6364
private final List<GenericConverter> genericConverters;
6465

66+
@SuppressWarnings("rawtypes")
67+
private final List<Formatter> formatters;
68+
6569
Factory(BeanFactory beanFactory) {
6670
this.converters = beans(beanFactory, Converter.class, ConfigurationPropertiesBinding.VALUE);
6771
this.genericConverters = beans(beanFactory, GenericConverter.class, ConfigurationPropertiesBinding.VALUE);
72+
this.formatters = beans(beanFactory, Formatter.class, ConfigurationPropertiesBinding.VALUE);
6873
}
6974

7075
private <T> List<T> beans(BeanFactory beanFactory, Class<T> type, String qualifier) {
@@ -80,7 +85,7 @@ private <T> List<T> beans(Class<T> type, String qualifier, ListableBeanFactory b
8085
}
8186

8287
public ConversionService create() {
83-
if (this.converters.isEmpty() && this.genericConverters.isEmpty()) {
88+
if (this.converters.isEmpty() && this.genericConverters.isEmpty() && this.formatters.isEmpty()) {
8489
return ApplicationConversionService.getSharedInstance();
8590
}
8691
ApplicationConversionService conversionService = new ApplicationConversionService();
@@ -90,6 +95,9 @@ public ConversionService create() {
9095
for (GenericConverter genericConverter : this.genericConverters) {
9196
conversionService.addConverter(genericConverter);
9297
}
98+
for (Formatter<?> formatter : this.formatters) {
99+
conversionService.addFormatter(formatter);
100+
}
93101
return conversionService;
94102
}
95103

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

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818

1919
import java.beans.PropertyEditorSupport;
2020
import java.io.File;
21+
import java.text.ParseException;
2122
import java.time.Duration;
2223
import java.util.ArrayList;
2324
import java.util.Collections;
2425
import java.util.HashMap;
2526
import java.util.LinkedHashMap;
2627
import java.util.List;
28+
import java.util.Locale;
2729
import java.util.Map;
2830
import java.util.Properties;
2931
import java.util.Set;
@@ -66,6 +68,7 @@
6668
import org.springframework.core.io.ProtocolResolver;
6769
import org.springframework.core.io.Resource;
6870
import org.springframework.core.io.ResourceLoader;
71+
import org.springframework.format.Formatter;
6972
import org.springframework.mock.env.MockEnvironment;
7073
import org.springframework.stereotype.Component;
7174
import org.springframework.test.context.support.TestPropertySourceUtils;
@@ -597,7 +600,7 @@ public void customProtocolResolver() {
597600
}
598601

599602
@Test
600-
public void loadShouldUseConfigurationConverter() {
603+
public void loadShouldUseConverterBean() {
601604
prepareConverterContext(ConverterConfiguration.class, PersonProperties.class);
602605
Person person = this.context.getBean(PersonProperties.class).getPerson();
603606
assertThat(person.firstName).isEqualTo("John");
@@ -613,13 +616,21 @@ public void loadWhenConfigurationConverterIsNotQualifiedShouldNotConvert() {
613616
}
614617

615618
@Test
616-
public void loadShouldUseGenericConfigurationConverter() {
619+
public void loadShouldUseGenericConverterBean() {
617620
prepareConverterContext(GenericConverterConfiguration.class, PersonProperties.class);
618621
Person person = this.context.getBean(PersonProperties.class).getPerson();
619622
assertThat(person.firstName).isEqualTo("John");
620623
assertThat(person.lastName).isEqualTo("Smith");
621624
}
622625

626+
@Test
627+
public void loadShouldUseFormatterBean() {
628+
prepareConverterContext(FormatterConfiguration.class, PersonProperties.class);
629+
Person person = this.context.getBean(PersonProperties.class).getPerson();
630+
assertThat(person.firstName).isEqualTo("John");
631+
assertThat(person.lastName).isEqualTo("Smith");
632+
}
633+
623634
@Test
624635
public void loadWhenGenericConfigurationConverterIsNotQualifiedShouldNotConvert() {
625636
assertThatExceptionOfType(BeanCreationException.class).isThrownBy(
@@ -1043,6 +1054,17 @@ public GenericConverter genericPersonConverter() {
10431054

10441055
}
10451056

1057+
@Configuration
1058+
static class FormatterConfiguration {
1059+
1060+
@Bean
1061+
@ConfigurationPropertiesBinding
1062+
Formatter<Person> personFormatter() {
1063+
return new PersonFormatter();
1064+
}
1065+
1066+
}
1067+
10461068
@Configuration
10471069
static class NonQualifiedGenericConverterConfiguration {
10481070

@@ -1731,12 +1753,27 @@ public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor t
17311753

17321754
}
17331755

1756+
static class PersonFormatter implements Formatter<Person> {
1757+
1758+
@Override
1759+
public String print(Person person, Locale locale) {
1760+
return person.getFirstName() + " " + person.getLastName();
1761+
}
1762+
1763+
@Override
1764+
public Person parse(String text, Locale locale) throws ParseException {
1765+
String[] content = text.split(" ");
1766+
return new Person(content[0], content[1]);
1767+
}
1768+
1769+
}
1770+
17341771
static class PersonPropertyEditor extends PropertyEditorSupport {
17351772

17361773
@Override
17371774
public void setAsText(String text) throws IllegalArgumentException {
1738-
String[] split = text.split(",");
1739-
setValue(new Person(split[1], split[0]));
1775+
String[] content = text.split(",");
1776+
setValue(new Person(content[1], content[0]));
17401777
}
17411778

17421779
}
@@ -1752,6 +1789,14 @@ static class Person {
17521789
this.lastName = lastName;
17531790
}
17541791

1792+
String getFirstName() {
1793+
return this.firstName;
1794+
}
1795+
1796+
String getLastName() {
1797+
return this.lastName;
1798+
}
1799+
17551800
}
17561801

17571802
static class Foo {

0 commit comments

Comments
 (0)