Spring Boot 和 Feign Client 解析 Enum參數(shù)

SpringBoot中Enum解析默認使用的是EnumToStringConverter,默認轉(zhuǎn)成枚舉的名稱。
響應(yīng)返回的JSON,Enum也默認解析為name。
有時候不使用枚舉的name,而是value來進行返回,參數(shù)解析。

  1. JSON序列化
    可以通過在枚舉字段上加上@JsonValue來實現(xiàn),這樣生成的json就會是code值了。
    @JsonCreator指定反序列化時使用的構(gòu)造方法,不指定會使用name解析。
    例如:
public enum GenderEnum {
    UNKNOWN("0", "未知"),
    MALE("1", "男"),
    FEMALE("2", "女");

    @JsonValue
    @Getter
    private final String code;

    @Getter
    private final String description;

    GenderEnum(String code, String description) {
        this.code = code;
        this.description = description;
    }

    private static final Map<String, GenderEnum> VALUES = new HashMap<>();

    static {
        for (final GenderEnum type : GenderEnum.values()) {
            GenderEnum.VALUES.put(type.getCode(), type);
        }
    }

    @JsonCreator
    public static GenderEnum of(String code) {
        return GenderEnum.VALUES.get(code);
    }
}
  1. 反序列化
    如果是PostJson提交的數(shù)據(jù),jackson會自動調(diào)用@JsonCreator來進行構(gòu)造,數(shù)據(jù)是可以正常提交的。
    如果是Form表單提交,參數(shù)里只有一個Enum格式的參數(shù)。
@GetMappting("/listByGender")
public void listByGender(@RequestParam GenderEnum gender) {
}

這時候提交gender=1會解析失敗,可以通過配置MessageConvertFactory實現(xiàn)解析。
定義StringToEnumConverterFactory:

@Slf4j
public class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {

    private static final Map<Class, Converter> CONVERTER_MAP = new ConcurrentHashMap<>();

    @Override
    public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
        Converter converter = CONVERTER_MAP.get(targetType);
        if (converter == null) {
            converter = new StringToEnumConverter<>(targetType);
            CONVERTER_MAP.put(targetType, converter);
        }
        return converter;
    }

    class StringToEnumConverter<T extends Enum> implements Converter<String, T> {

        private Map<String, T> enumMap = new HashMap<>();

        private StringToEnumConverter(Class<T> enumType) {
            Optional<Field> optional = AnnotationUtils.getAnnotationField(enumType, JsonValue.class);
            //Enum有JsonValue注解時使用該值解析,否則使用默認的name解析
            if (optional.isPresent()) {
                Field field = optional.get();
                try {
                    field.setAccessible(true);
                    T[] enums = enumType.getEnumConstants();
                    for (T e : enums) {
                        enumMap.put(String.valueOf(field.get(e)), e);
                    }
                } catch (IllegalAccessException e) {
                    log.error("enum map construct failed:", e);
                }
            } else {
                T[] enums = enumType.getEnumConstants();
                for (T e : enums) {
                    enumMap.put(e.name(), e);
                }
            }
        }

        @Override
        public T convert(String source) {
            return enumMap.get(source);
        }
    }
}

配置mvc

@Configuration
public class CustomWebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(new StringToEnumConverterFactory());
    }
}
  1. 使用FeignClient
    當使用FeignClient進行服務(wù)訪問時,F(xiàn)eign會自動構(gòu)造請求參數(shù)。Feign的接口的定義為:
@GetMappting("/listByGender")
public void listByGender(@RequestParam GenderEnum gender);

這時候調(diào)用訪問,F(xiàn)eign會把gender轉(zhuǎn)成name來進行訪問?gender=MALE,這個情況下服務(wù)端肯定報錯,因為我們的Enum解析已經(jīng)不是name了。
所以要配置下FeignClient,支持Enum也轉(zhuǎn)成JsonValue注解的值進行訪問。
定義Converter:

@Slf4j
public class EnumToStringConverter implements Converter<Enum<?>, String> {

    @Override
    public String convert(Enum<?> source) {
        Class<?> enumType = source.getClass();
        Optional<Field> optional = AnnotationUtils.getAnnotationField(enumType, JsonValue.class);
        if (optional.isPresent()) {
            Field field = optional.get();
            field.setAccessible(true);
            return String.valueOf(field.get(source));
        } else {
            return source.name();
        }
    }
}

配置FeignClient:

@Component
public class EnumFeignFormatterRegistrar implements FeignFormatterRegistrar {

    @Override
    public void registerFormatters(FormatterRegistry registry) {
        registry.addConverter(new EnumToStringConverter());
    }

}

通過以上三個步驟,就可以實現(xiàn)Enum參數(shù)的請求和響應(yīng)了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容