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;

}
相关推荐
morris13138 分钟前
【SpringBoot】Xss的常见攻击方式与防御手段
java·spring boot·xss·csp
monkey_meng1 小时前
【Rust中的迭代器】
开发语言·后端·rust
余衫马1 小时前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng1 小时前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
阿伟*rui3 小时前
配置管理,雪崩问题分析,sentinel的使用
java·spring boot·sentinel
paopaokaka_luck5 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
码农小旋风7 小时前
详解K8S--声明式API
后端
Peter_chq7 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml47 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~7 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端