• SpringMVC枚举类型字段处理


    在日常的项目开发中经常会遇到一些取值范围固定的字段,例如性别、证件类型、会员等级等,此时我们可以利用枚举来最大程度减少字段的乱定义,统一管理枚举的值。

    SpringMVC中对于枚举也有默认的处理策略:

    • 对于@RequestParam,Spring是通过ConverterFactory来处理的,大致处理策略是根据枚举名称或枚举下标来转换枚举。
      在这里插入图片描述

    • 对于@RequestBody,Spring是通过Jackson配置将json内的枚举值转换为对象的,大致处理策略同样是根据枚举名称或枚举下标来转换枚举。

    在SpringMVC内对枚举的默认处理逻辑是根据枚举的类名或枚举下标来将请求参数转化为枚举对象,这显然不太灵活,因此我们需要调整枚举字段的处理逻辑。

    RequestParam处理

    我们可以自定义ConvertFactory来自定义枚举字段的转化策略。

    1. 定义BaseEnum接口,规定所有枚举都应该实现此接口

      public interface BaseEnum<T> {
      
          /**
           * 获取枚举值
           */
          T getCode();
      
          /**
           * 根据值获取对应的枚举
           * @param enumTypeClazz 枚举类型类
           * @param value 值
           */
          static <T extends BaseEnum> T getEnumByCode(Class<T> enumTypeClazz, Object value) {
              if (enumTypeClazz == null || value == null) {
                  return null;
              }
      
              Optional<T> optional = Arrays.stream(enumTypeClazz.getEnumConstants()).filter(e ->{
                  Object enumCode = e.getCode();
                  return Objects.equals(Convert.convert(enumCode.getClass(), value),enumCode);
              }).findFirst();
      
              //如果不存在则抛异常
              return optional.orElseThrow( ()-> new RuntimeException("[" + enumTypeClazz.getSimpleName() + "]参数错误[" + value + "]"));
          }
      }
      
      
    2. 自定义ConverterFactory

      @Component
      public class EnumConverterFactory implements ConverterFactory<String, BaseEnum> {
          @Override
          public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
              return source -> BaseEnum.getEnumByCode(targetType, source);
          }
      }
      
    3. 注册ConverterFactory

      @Configuration
      public class SpringMVCConfig implements WebMvcConfigurer {
      
          @Override
          public void addFormatters(FormatterRegistry registry) {
              registry.addConverterFactory(new EnumConverterFactory());
          }
      
      }
      
    4. 这样配置后请求参数就会自动转换为枚举了。

      //枚举类
      @AllArgsConstructor
      @Getter
      public enum Gender implements BaseEnum<Integer> {
          MALE(1,"男"),
          FEMALE(2,"女"),
              ;
          @EnumValue
          private Integer code;
          private String value;
      
      }
      
      //通过接口接受gender参数,能够根据code自动转换为对应的枚举
      @GetMapping("/test")
      public Gender insert(Gender gender) {
          return gender;
      }
      

    RequestBody处理

    RequestBody是通过Jackson转换对请求参数进行处理的,因此我们只需要自定义反序列化类即可

    1. 自定义序列化规则设置json内的值如何转换为枚举

      public class EnumDeserializer extends JsonDeserializer<BaseEnum> {
      
          /**
           * 根据参数值获取对应的枚举
           * @throws IOException
           * @throws JacksonException
           */
          @Override
          public BaseEnum deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
              // 当前值
              final String paramValue = p.getText();
      
              //获取序列化信息
              final JsonStreamContext parsingContext = p.getParsingContext();
              // 获取当前序列化的类的对象
              final Object currentValue = parsingContext.getCurrentValue();
              //获取当前序列化的字段名
              final String currentName = parsingContext.getCurrentName();
      
              try {
                  // 反射获取当前序列化字段信息
                  final Field declaredField = currentValue.getClass().getDeclaredField(currentName);
                  // 通过字段信息获取对应的枚举的Class
                  final Class<BaseEnum> targetType = (Class<BaseEnum>) declaredField.getType();
      
                  //根据参数值获取对应的枚举
                  BaseEnum baseEnum = BaseEnum.getEnumByCode(targetType, paramValue);
                  if (ObjectUtil.isEmpty(baseEnum)) {
                      throw new RuntimeException("[" + currentName + "]参数错误");
                  }
                  //返回枚举
                  return baseEnum;
              } catch (NoSuchFieldException e) {
                  throw new RuntimeException("[" + currentName + "]参数错误");
              }
          }
      
      }
      
      1. 在枚举类加上 @JsonDeserialize(using = EnumDeserializer.class)
         //可以直接加到刚刚定义的BaseEnum接口上,这样所有枚举就自动继承了
         @JsonDeserialize(using = EnumDeserializer.class)
         public interface BaseEnum<T> {
         	……
         }
      
      1. 这样配置后@RequestBody就能够自动转换枚举了
      @PostMapping("/save")
      public User save(@RequestBody User user) {
         studentService.save(user);
         return user;
      }
      

    枚举字段返回序列化

    如果我们返回的对象内有枚举字段,SpringMVC会默认将枚举的名称作为值返回,如果我们想指定枚举类的某个属性作为值,可以通过@JsonValue指定

    @AllArgsConstructor
    @Getter
    public enum Gender implements BaseEnum<Integer> {
        MALE(1,"男"),
        FEMALE(2,"女"),
            ;
    	//指定转json时使用code作为值
        @JsonValue
        private Integer code;
        private String value;
    
    }
    

    或者直接在枚举类上加@JsonFormat,将枚举转换为对象格式

    @JsonFormat(shape= JsonFormat.Shape.OBJECT)
    @JsonDeserialize(using = EnumDeserializer.class)
    public interface BaseEnum<T> {
    	……
    }
    

    MybatisPlus对枚举的处理

    MybatisPlus直接在枚举类的属性上加@EnumValue即可,并且兼容xml内的动态sql

    @AllArgsConstructor
    @Getter
    public enum Gender implements BaseEnum<Integer> {
        MALE(1,"男"),
        FEMALE(2,"女"),
            ;
        //指定code作为入库时的值
        @EnumValue
        private Integer code;
        private String value;
    
    }
    
  • 相关阅读:
    【数据结构】用堆排序解决TOPK问题
    原则、语言、编译、解释
    【Web前端二级导航栏】
    mysql基础
    [附源码]Python计算机毕业设计Django青栞系统
    反射获取类属性(含父类中的属性)
    如何验证高压放大器的性能好坏呢
    很多应用都是nginx+apache+tomcat
    crondtab定时任务%字符无法识别的处理
    java计算机毕业设计航空售票系统源码+mysql数据库+系统+lw文档+部署
  • 原文地址:https://blog.csdn.net/dndndnnffj/article/details/139262016