自定义 YML 配置文件 String 转换 Enum

Custom YML configuration file String conversion Enum

在Springboot工程中,yml文件中的配置可以自动转换为@ConfigurationProperties注解bean,我从官方文档和源码中找到 ApplicationConversionService#addApplicationConverters()方法添加一个默认的LenientStringToEnumConverterFactory来处理所有String到Enum的转换,它通过Enum.valueOf()实现,但是我想使用其他规则将字符串转换为枚举的例子,就像fromAlias() 下面的方法,

@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);
        }

    }

}

以下是yml文件配置

head:
  payType: "金币"
  cast: 10

不知道入口在哪里,所以程序一运行就报错 代码:

@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);
    }
}

以下是错误信息:

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

我尝试将各种转换器(如下图)注入到容器中,但仍然没有用。

@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);
    }
}

如何满足这个要求?

用于 @ConfigurationProperties 绑定的转换器需要一个特殊的限定符来告诉 Spring 它们将用于该目的。

这个注释存在 - @ConfigurationPropertiesBindingJavadoc如下:

Qualifier for beans that are needed to configure the binding of @ConfigurationProperties (e.g. Converters).

所以只需要将该注释添加到您的转换器,然后 Spring 将在绑定过程中使用它:

@Component
@ConfigurationPropertiesBinding
public class PayTypeConverter implements Converter<String, HeadProperties.PayType> {
    @Override
    public HeadProperties.PayType convert(String source) {
        return HeadProperties.PayType.fromAlias(source);
    }
}

然后产生预期的输出:

head config:HeadProperties(payType=GOLD, cast=10)

还有一个小注意事项,在编写自定义转换器时,请注意返回 null 不会触发错误(假设没有配置其他措施来防止这种情况发生)。这意味着与 out-of-the-box 枚举转换器不同,如果找不到匹配的枚举,您的自定义转换器不会产生错误。您可以通过抛出异常而不是返回 null 来解决这个问题。