美文网首页
Spring Boot 和 Feign Client 解析 En

Spring Boot 和 Feign Client 解析 En

作者: xin_5457 | 来源:发表于2021-11-04 16:03 被阅读0次

    SpringBoot中Enum解析默认使用的是EnumToStringConverter,默认转成枚举的名称。
    响应返回的JSON,Enum也默认解析为name。
    有时候不使用枚举的name,而是value来进行返回,参数解析。

    1. JSON序列化
      可以通过在枚举字段上加上@JsonValue来实现,这样生成的json就会是code值了。
      @JsonCreator指定反序列化时使用的构造方法,不指定会使用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提交的数据,jackson会自动调用@JsonCreator来进行构造,数据是可以正常提交的。
      如果是Form表单提交,参数里只有一个Enum格式的参数。
    @GetMappting("/listByGender")
    public void listByGender(@RequestParam GenderEnum gender) {
    }
    

    这时候提交gender=1会解析失败,可以通过配置MessageConvertFactory实现解析。
    定义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进行服务访问时,Feign会自动构造请求参数。Feign的接口的定义为:
    @GetMappting("/listByGender")
    public void listByGender(@RequestParam GenderEnum gender);
    

    这时候调用访问,Feign会把gender转成name来进行访问?gender=MALE,这个情况下服务端肯定报错,因为我们的Enum解析已经不是name了。
    所以要配置下FeignClient,支持Enum也转成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());
        }
    
    }
    

    通过以上三个步骤,就可以实现Enum参数的请求和响应了。

    相关文章

      网友评论

          本文标题:Spring Boot 和 Feign Client 解析 En

          本文链接:https://www.haomeiwen.com/subject/ifdkzltx.html