Spring枚举传参转换

原创
2020/05/14 11:55
阅读数 6.1K

Spring boot枚举传参转换

在实际web开发项目中会用到枚举,本文章主要分析SpringBoot传参转换枚举, 主要包括SpringBoot传参转换枚举使用实例、应用技巧、基本知识点总结和需要注意事项,该技术对于Spring MVC同样适用。具体来讲可以归纳两种实现方式

  • 可以基于Jackson自带枚举序列化方式进行转换;
  • 基于Spring类型转换器方式来实现枚举传参转换;

以上两种方式都可以实现,只是方法一对于简单处理是可行,但是对于复杂或者全局处理就有局限性,推荐方法二, 本文主要解剖第二种实现方式

有时候,我们传参的时候,希望使用枚举类来当作参数,如下:

e.g :

     public enum VipEnum {
     YELLOW(1, "黄钻"),
     RED(2, "红钻");
     private Integer index;
     private String value;
     
     VipEnum(Integer index, String value) {
     this.index = index;
     this.value = value;
     }
     
     public Integer getIndex() {
     return index;
     }
     
     public String getValue() {
     return value;
     }
     }

我们期望的是这样,参数中传一个1 后台就自动转换为枚举类的VipEnum.YELLOW

     @PostMapping("/vip")
     public VipEnum convert(VipEnum vipEnum) {
     return vipEnum;
     }

实现Converter接口

基于需求,我们可以考虑适用Spring的类型转换器,来实现此功能需求。 Spring为我们提供了一个类型自动转换接口Converter<S, T>,可以实现从一个Object转为另一个Object的功能。除了Converter接口之外。

  1. 接口

     public interface Converter<S, T> {
     T convert(S source);
     }
    
  2. 实现

     @Component
     @Slf4j
     public class PersonConverter implements Converter<String, VipEnum> {
     
     @Override
     public VipEnum convert(String value) {
     log.info("参数是: {}", value);
     return (VipEnum) PersonConverter.getEnum(VipEnum.class, value);
     }
     
     
     public static <T extends VipEnum> Object getEnum(Class<T> targerType, String source) {
     for (T enumObj : targerType.getEnumConstants()) {
     if (source.equals(String.valueOf(enumObj.getId()))) {
       return enumObj;
     }
     }
       return null;
     }   
     }
    

至此,基本上完成功能需求。

注意:在Spring MVC和Spring Boot中,由于从客户端接收到的请求都被视为String类型,所以只能用String转枚举的converter

拓展

对于上述实现,存在如下局限性: 1.只能把所有类型都转换成字符串类型处理,这对于GET请求来说可以,但是对于POST这肯定是不行的; 2.项目中如果比较多的枚举需要转换,上述实现就不可取;

ConverterFactory接口

ConverterFactory的出现可以让我们统一管理一些相关联的Converter。顾名思义,ConverterFactory就是产生Converter的一个工厂,确实ConverterFactory就是用来产生Converter的。ConverterFactory接口的定义

     public interface ConverterFactory<S, R> {
     <T extends R> Converter<S, T> getConverter(Class<T> targetType);
     }

可以看出 总共有 三个泛型S、R、T,其中:

  • S表示原类型
  • R表示目标类型
  • T是类型R的一个子类

ConverterFactory相比converter的好处在于ConverterFactory可以将原类型转换成一组实现了相同接口类型的对象, 而Converter则只能转换成一种类型。

实现全局转换

  1. 声明一个接口,让需要转换的枚举去实现该接口

     public interface IBaseEnum<T> {
         T getIndex();
     }
     
     public enum VipEnum implements IBaseEnum<Integer> {
         HUANG(1, "黄钻"),
         HONG(2, "红钻");
         private Integer index;
         private String value;
              
          VipEnum(Integer index, String value) {
              this.index = index;
              this.value = value;
          }
              
          @Override
          public Integer getIndex() {
              return index;
          }
    
          public String getValue() {
              return value;
          }
          }
     }
    

2.实现转化器工厂

  • 实现索引值为整数类型;

     @Slf4j
     @Component
     public class IntegerCodeToEnumConverterFactory implements ConverterFactory<Integer, IBaseEnum> {
         private static final Map<Class, Converter> CONVERTERS = Maps.newHashMap();
         @Override
         public <T extends IBaseEnum> Converter<Integer, T> getConverter(Class<T> targetType) {
             Converter<Integer, T> converter = CONVERTERS.get(targetType);
             if (Objects.isNull(converter)) {
                 converter = new IntegerToEnumConverter<>(targetType);
                 CONVERTERS.put(targetType, converter);
             }
             return converter;
         }
    
     private static  class  IntegerToEnumConverter<T extends IBaseEnum> implements Converter<Integer, T> {
    
     private final Map<Integer, T> enumMap = Maps.newHashMap();
    
     public IntegerToEnumConverter(Class<T> enumType) {
         T[] enums = enumType.getEnumConstants();
         Arrays.stream(enums).forEach(p -> enumMap.put((Integer) p.getIndex(), p));
     }
    
     @Override
     public T convert(Integer source) {
         T t = enumMap.get(source);
         if (Objects.isNull(t)) log.error("ERROR", new IllegalArgumentException("无法匹配对应的枚举类型"));
         return t;
     }
     }
     }
    
  • 实现索引值为字符串类型;

     @Slf4j
     @Component
     public class StringCodeToEnumConverterFactory implements ConverterFactory<String, IBaseEnum> {
         private static final Map<Class, Converter> CONVERTERS = Maps.newHashMap();
     
         @Override
         public <T extends IBaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
             Converter<String, T> converter = CONVERTERS.get(targetType);
             if (Objects.isNull(converter)) {
                 converter = new StringToEnumConverter<>(targetType);
                 CONVERTERS.put(targetType, converter);
             }
             return converter;
         }
     
         private static class StringToEnumConverter<T extends IBaseEnum> implements Converter<String,
                 T> {
     
             private Map<String, T> enumMap = Maps.newHashMap();
     
             public StringToEnumConverter(Class<T> enumType) {
                 T[] enums = enumType.getEnumConstants();
                 Arrays.stream(enums).forEach(p -> enumMap.put(p.getIndex().toString(), p));
             }
     
             @Override
             public T convert(String source) {
                 T t = enumMap.get(source);
                 if (Objects.isNull(t)) log.error("ERROR", new IllegalArgumentException("无法匹配对应的枚举类型"));
                 return t;
             }
         }
     } 
    

具体可以查看项目中com.zc.travel.converter.factory.StringCodeToEnumConverterFactory

3.配置以及注册转换器工厂类使之生效

    @Configuration
    public class WebConfig  implements WebMvcConfigurer {
    
        @Resource
        private IntegerCodeToEnumConverterFactory integerCodeToEnumConverterFactory;
    
        @Resource
        private StringCodeToEnumConverterFactory stringCodeToEnumConverterFactory;
         
        @Override
        public void addFormatters(FormatterRegistry registry) {
            registry.addConverterFactory(integerCodeToEnumConverterFactory);
            registry.addConverterFactory(stringCodeToEnumConverterFactory);
        }
    }

经过上述实现可以在接口中传index值就可以实现转换。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部