spring-projects/spring-boot

Custom binding converters are ignored when working with collection types

kascaks opened this issue · 2 comments

The code below works correctly in 2.7.18, 3.0.13 and 3.1.6, but when used with 3.2.0, it fails with exception.

Failed to bind properties under 'config.list' to java.util.List<foo.bar.Application$CustomType>:

    Property: config.list
    Value: "key2-value2,key3-value3"
    Origin: class path resource [application.properties] - 2:13
    Reason: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.lang.String] to type [java.util.List<foo.bar.Application$CustomType>]

Application.java

package foo.bar;

import java.util.List;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

import foo.bar.Application.Config;

@Configuration
@EnableAutoConfiguration
@ComponentScan
@EnableConfigurationProperties(Config.class)
public class Application {

	public static void main(String[] args) throws Exception {
		ApplicationContext ctx = SpringApplication.run(Application.class, args);
		Config c = ctx.getBean(Config.class);
		System.out.println(c.scalar().key() + " - " + c.scalar().value());
		for (CustomType ct : c.list()) {
			System.out.println(ct.key() + " - " + ct.value());
		}
	}

	@ConfigurationProperties(prefix = "config")
	public static record Config(CustomType scalar, List<CustomType> list) {
	}

	public static record CustomType(String key, String value) {
	}

	@Component
	@ConfigurationPropertiesBinding
	public static class CustomTypeConverter implements Converter<String, CustomType> {

		@Override
		public CustomType convert(String source) {
			String[] kv = source.split("-");
			return new CustomType(kv[0], kv[1]);
		}
	}
}

application.properties

config.scalar=key1-value1
config.list=key2-value2,key3-value3

Minimal working sample project is in attachment.
converter-failure.zip

I think this may be due to #34631. We add the converter to an empty FormattingConversionService which will do String -> CustomType conversion but not String -> List<CustomType> conversion.