In Spring Boot project, the configuration in the YML file can be automatically converted to an @ConfigurationProperties
annotated bean, I found from the official documents and source code in ApplicationConversionService#addApplicationConverters()
method to add A default LenientStringToEnumConverterFactory
to handle all the String conversion to Enum, it through the Enum.valueOf()
implementation,But I want to use other rules to turn strings into examples of enum,Just like the fromAlias()
method below,
@ConfigurationProperties(prefix = "head")
@Component
@Data
public class HeadProperties {
private PayType payType;
private Integer cast;
@Getter
@RequiredArgsConstructor
enum PayType {
GOLD("GOLD", "金币"), DIAMOND("DIAMOND", "钻石"), VIP_FREE("VIP_FREE", "会员免费");
private final String val;
private final String alias;
static PayType fromAlias(String alias) {
return Arrays.stream(values())
.filter(type -> alias.equals(type.getAlias()))
.findAny()
.orElse(null);
}
}
}
The following is the YML file configuration
head:
payType: "金币"
cast: 10
I don't know where the entry is, so I get an error as soon as the program runs code:
@SpringBootApplication
@Slf4j
public class DemoApplication{
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
ApplicationRunner runner(HeadProperties headConfig) {
return arg -> log.info("head config:{}", headConfig);
}
}
the following is error message:
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'head.pay-type' to com.example.demo.HeadProperties$PayType:
Property: head.pay-type
Value: 金币
Origin: class path resource [application.yml] - 2:12
Reason: failed to convert java.lang.String to com.example.demo.HeadProperties$PayType (caused by java.lang.IllegalArgumentException: No enum constant com.example.demo.HeadProperties.PayType.金币)
Action:
Update your application's configuration. The following values are valid:
DIAMOND
GOLD
VIP_FREE
I tried injecting various converters like the one below into the container,but it still didn't work.
@Component
public class PayTypeConverter implements Converter<String, HeadProperties.PayType> {
@Override
public HeadProperties.PayType convert(String source) {
return HeadProperties.PayType.fromAlias(source);
}
}
@Component
public class PayTypeConverter implements Converter<String, Enum<HeadProperties.PayType>> {
@Override
public Enum<HeadProperties.PayType> convert(String source) {
return HeadProperties.PayType.fromAlias(source);
}
}
How can this requirement be fulfilled?
CodePudding user response:
The converters that are used for @ConfigurationProperties
binding need a special qualifier that tells Spring that they are to be used for that purpose.
An annotation exists for this- @ConfigurationPropertiesBinding
. The Javadoc is as follows:
Qualifier for beans that are needed to configure the binding of @ConfigurationProperties (e.g. Converters).
So all that's needed is to add that annotation to your converter, then Spring will use it during the binding process:
@Component
@ConfigurationPropertiesBinding
public class PayTypeConverter implements Converter<String, HeadProperties.PayType> {
@Override
public HeadProperties.PayType convert(String source) {
return HeadProperties.PayType.fromAlias(source);
}
}
That then produces the expected output:
head config:HeadProperties(payType=GOLD, cast=10)
And a minor note, when writing custom converters, be aware that returning null will not trigger an error (assuming there are no other measures configured to prevent that). That means that unlike the out-of-the-box enum converter, your custom one does not produce an error if no matching enum can be found. You can remedy this by instead throwing an exception instead of returning null.