SpringMVC枚举类型字段处理

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

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

  • 对于@RequestParam,Spring是通过ConverterFactory来处理的,大致处理策略是根据枚举名称或枚举下标来转换枚举。

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

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

RequestParam处理

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

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

    java 复制代码
    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

    java 复制代码
    @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

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

    java 复制代码
    //枚举类
    @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内的值如何转换为枚举

    java 复制代码
    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)
    java 复制代码
       //可以直接加到刚刚定义的BaseEnum接口上,这样所有枚举就自动继承了
       @JsonDeserialize(using = EnumDeserializer.class)
       public interface BaseEnum<T> {
       	......
       }
    1. 这样配置后@RequestBody就能够自动转换枚举了
    java 复制代码
    @PostMapping("/save")
    public User save(@RequestBody User user) {
       studentService.save(user);
       return user;
    }

枚举字段返回序列化

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

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

}

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

java 复制代码
@JsonFormat(shape= JsonFormat.Shape.OBJECT)
@JsonDeserialize(using = EnumDeserializer.class)
public interface BaseEnum<T> {
	......
}

MybatisPlus对枚举的处理

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

java 复制代码
@AllArgsConstructor
@Getter
public enum Gender implements BaseEnum<Integer> {
    MALE(1,"男"),
    FEMALE(2,"女"),
        ;
    //指定code作为入库时的值
    @EnumValue
    private Integer code;
    private String value;

}
相关推荐
bobz9652 分钟前
ovs patch port 对比 veth pair
后端
Asthenia041212 分钟前
Java受检异常与非受检异常分析
后端
uhakadotcom26 分钟前
快速开始使用 n8n
后端·面试·github
JavaGuide33 分钟前
公司来的新人用字符串存储日期,被组长怒怼了...
后端·mysql
bobz96543 分钟前
qemu 网络使用基础
后端
Asthenia04121 小时前
面试攻略:如何应对 Spring 启动流程的层层追问
后端
Asthenia04121 小时前
Spring 启动流程:比喻表达
后端
Asthenia04122 小时前
Spring 启动流程分析-含时序图
后端
ONE_Gua2 小时前
chromium魔改——CDP(Chrome DevTools Protocol)检测01
前端·后端·爬虫
致心2 小时前
记一次debian安装mariadb(带有迁移数据)
后端