在Spring中自定义GenericConverter,实现前端传值自动转换成后端所需枚举类型

目录

问题发现

解决方法

结果验证

注意事项

 


问题发现

        在大多数项目中后端都会用枚举来表示商品的状态(如启用,禁用),然而前端大部分都是以数字形式来代表状态值发送请求。如果我们需要正确的枚举值就需要手动使用枚举内的静态方法进行转换。

/**
     * 根据code获取枚举对象
     * @param code 值
     * @return 根据code获取枚举对象 不存在返回null
     */
    public static CategoryType of(Integer code) {
        if (code == null) {
            return null;
        }
        for (CategoryType value : CategoryType.values()) {
            if (value.code.equals(code)) {
                return value;
            }
        }
        return null;
    }

        项目中存在着许多的枚举如果都一个个转换就太麻烦了,对业务代码的侵入也比较严重,那么有没有办法可以解决呢?当然有我们可以自定义转换器来实现自动转换。


解决方法

  1. 首先,我们需要自定义一个注解,用于标记值到枚举的准换方法。
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface EnumConvert {
    
    }
    
  2.  定义枚举,用注解标注静态方法。
    import com.baomidou.mybatisplus.annotation.EnumValue;
    import com.fasterxml.jackson.annotation.JsonValue;
    import com.sky.annotation.EnumConvert;
    import lombok.Getter;
    
    @Getter
    public enum CategoryType {
        SETMEAL(1, "套餐分类"),
        DISH(2, "菜品分类");
    
        @EnumValue // 这个是Mybaitsplus枚举处理用于将枚举的什么值保存到数据库,
        @JsonValue // 这个是Json转换器用于将枚举的什么值返回前端
        private final Integer code;
        private final String value;
    
        CategoryType(Integer code, String value) {
            this.code = code;
            this.value = value;
        }
    
        /**
         * 根据code获取枚举对象
         * @param code 值
         * @return 根据code获取枚举对象 不存在返回null
         */
        @EnumConvert // 自定义注解
        public static CategoryType of(Integer code) {
            if (code == null) {
                return null;
            }
            for (CategoryType value : CategoryType.values()) {
                if (value.code.equals(code)) {
                    return value;
                }
            }
            return null;
        }
    
    }
    
     
  3. 定义一个类实现GenericConverter接口,并重写其中方法,通过反射获取类中被自定义注解标记的方法,通过这个方法将前端传递的值转换成正确的枚举值。
    import com.sky.annotation.EnumConvert;
    import com.sky.constant.RequestConstant;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.core.convert.TypeDescriptor;
    import org.springframework.core.convert.converter.GenericConverter;
    
    import java.lang.reflect.Method;
    import java.util.Set;
    
    @Slf4j
    public class EnumConverter implements GenericConverter {
    
        @Override
        public Set<ConvertiblePair> getConvertibleTypes() {
            // 指定支持的转换类型对
            return Set.of(new ConvertiblePair(String.class, Enum.class));
        }
    
        /**
         * 转换方法
         * @param source 源对象(前端传递对象)
         * @param sourceType 前端传递类型
         * @param targetType 目标类型(就是后端接收参数类型)
         * @return 枚举
         */
        @Override
        public Enum convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
            String value = (String) source;
            if (source == null) {
                return null;
            }
            // 根据目标类型进行转换
            Class<?> targetTypeClass = targetType.getType();
            Enum invoke = null;
            try {
                // 反射获得类中的被注解的方法
                Method method = getAnEnumMethod(targetTypeClass);
                // 如果方法存在,调用方法准换成正确的枚举类
                if (method != null) {
                    invoke = (Enum) method.invoke(null, Integer.valueOf(value));
                    if (invoke == null) {
                        throw new RuntimeException(RequestConstant.ENUM_PARAM_ERROR);
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(RequestConstant.ENUM_PARAM_ERROR);
            }
            return invoke;
        }
    
        // 获取被注解的方法
        private static Method getAnEnumMethod(Class<?> targetTypeClass) {
            // 获取类的静态方法
            Method[] methods = targetTypeClass.getMethods();
            // 循环找到被注解的方法
            for (Method method : methods) {
                if (method.isAnnotationPresent(EnumConvert.class)) {
                    return method;
                }
            }
            return null;
        }
    
    }
    
  4. 进行注册
    import com.sky.converter.EnumConverter;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.format.FormatterRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    
    /**
     * 配置类,注册web层相关组件
     */
    @Configuration
    @Slf4j
    @RequiredArgsConstructor
    public class WebMvcConfiguration extends WebMvcConfigurationSupport {
    
    
        /**
         * 自定义枚举准换器
         *
         * @return 枚举转换器
         */
        @Bean
        public EnumConverter enumConverter() {
            return new EnumConverter();
        }
    
        @Override
        public void addFormatters(FormatterRegistry registry) {
            registry.addConverter(enumConverter());
        }
    }
    


结果验证

  1. 测试代码
        @GetMapping("/list")
        public void listByType(@NonNull CategoryType type) {
            System.out.println("type = " + type);
        }

  2.  测试数据

    3.测试结果 

可以看到以及正确的转换成枚举类型了。 

注意事项

        SpringMVC的转换器只对Get请求起作用,如果是POST请求是无法起作用的,那么对于POST请求又该怎么办呢?其实方法更加简单,在Spring内部会帮你自动转换,你只需要添加注解就可以实现。

  • @JsonValue:在序列化时,只序列化 @JsonValue 注解标注的值
  • @JsonCreator:在反序列化时,调用 @JsonCreator 标注的构造器或者工厂方法来创建对象

        你可以在枚举类中添加这两个注解。

import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import com.sky.annotation.EnumConvert;
import lombok.Getter;

@Getter
public enum CategoryType {
    SETMEAL(1, "套餐分类"),
    DISH(2, "菜品分类");

    @EnumValue
    @JsonValue
    private final Integer code;
    private final String value;

    CategoryType(Integer code, String value) {
        this.code = code;
        this.value = value;
    }

    /**
     * 根据code获取枚举对象
     * @param code 值
     * @return 根据code获取枚举对象 不存在返回null
     */
    @EnumConvert
    @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
    public static CategoryType of(Integer code) {
        if (code == null) {
            return null;
        }
        for (CategoryType value : CategoryType.values()) {
            if (value.code.equals(code)) {
                return value;
            }
        }
        return null;
    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值