前端传的参数自动转换为枚举的方式——spring convert转换

前端传的字符串自动转换为对应枚举的方式

背景

  1. 在项目中我们通常会使用枚举来接收一些值固定的参数,例如此次项目中我们需要传递一个语言参数,这个参数在系统中有三个值:中文 、英文、和繁体中文,在数据库存的是相对应的Integer值
    (别问我为啥还要整个小写,前端规范是小写,后段枚举规范用大写,没发用spring自带的枚举转换器。)
 @Getter
@RequiredArgsConstructor
public enum LanguageType {
    /**
     * 中文
     */
    CN("zh-cn",0),
    /**
     * 英文
     */
    EN("en-us",1),
    /**
     * 繁体中文
     */
    TC("zh-tw",2)
    ;
    private final String type;
    private final Integer index;
}

我们希望通过前端穿过来的字符串来找到对应的枚举。
之前我的方案是这样的

public static LanguageType get(String type) {
        for (LanguageType value : LanguageType.values()) {
            if (value.getType().equals(type)) {
                return value;
            }
        }
        return null;
    }

前端传过来的字符串,需要我们手动使用findByType来转换为对应的枚举,这样着实有点麻烦。

我们知道spring有一个自带的枚举转换器,是否可以参照spring的方法自己也写一个转换器呢?

实现自定义枚举转换器

Converter是Spring3中引入的一项比较特殊的功能,其实就是一个转换器,可以把一种类型转换为另一种类型。可以将前端出传入的参数转换为后端可以使用的类型,如:日期格式化,字符串去空格等。

package org.springframework.core.convert.converter;

public interface Converter<S, T> {
    T convert(S var1);
}

ConverterFactory是创建Converter的工厂类。

我们通过实现ConverterFactory和Converter来实现自定义枚举转换,

代码实现
@SuppressWarnings("all")
public class EnumConverterFactory implements ConverterFactory<String, Enum<?>> {

    private final ConcurrentMap<Class<? extends Enum<?>>, EnumConverterHolder> holderMapper = new ConcurrentHashMap<>();

    @Override
    public <T extends Enum<?>> Converter<String, T> getConverter(Class<T> targetType) {
        EnumConverterHolder holder = holderMapper.computeIfAbsent(targetType,EnumConverterHolder::createHolder);
        return (Converter<String, T>) holder.converter;
    }


    @AllArgsConstructor
    static class EnumConverterHolder {
        @Nullable
        final EnumMvcConverter<?> converter;

        static EnumConverterHolder createHolder(Class<?> targetType) {
            //获取EnumConvertMethod注解的所有方法
            List<Method> methodList = MethodUtils.getMethodsListWithAnnotation(targetType, EnumConvertMethod.class, false, true);
            if (CollectionUtil.isEmpty(methodList)) {
                return new EnumConverterHolder(null);
            }
            Assert.isTrue(methodList.size() == 1, "@EnumConvertMethod 只能标记在一个工厂方法(静态方法)上");
            Method method = methodList.get(0);
            Assert.isTrue(Modifier.isStatic(method.getModifiers()), "@EnumConvertMethod 只能标记在工厂方法(静态方法)上");
            return new EnumConverterHolder(new EnumMvcConverter<>(method));
        }

    }

    static class EnumMvcConverter<T extends Enum<T>> implements Converter<String, T> {

        private final Method method;

        EnumMvcConverter(Method method) {
            this.method = method;
            this.method.setAccessible(true);
        }

        @Override
        public T convert(String source) {
            if (source.isEmpty()) {
                // reset the enum value to null.
                return null;
            }
            try {
                return (T) method.invoke(null, Integer.valueOf(source));
            } catch (Exception e) {
                throw new IllegalArgumentException(e);
            }
        }

    }
}

这里我们使用@EnumConvertMethod来标识转换的方法,然后再Holder类里面拿到这些方法反射执行。

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnumConvertMethod {
}

加载配置

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Bean
    public EnumConverterFactory enumConverterFactory() {
        return new EnumConverterFactory();
    }
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(enumConverterFactory());
    }
}

使用

在该方法上加上@EnumConvertMethod

@EnumConvertMethod
public static LanguageType get(String type) {
        for (LanguageType value : LanguageType.values()) {
            if (value.getType().equals(type)) {
                return value;
            }
        }
        return null;
    }

对请求体参数的处理

springMvc的枚举转换器只支持get请求额参数转换,如果是POST请求是无法支持的。
另外如果我们从数据库查出来返回给前端时, 还需要我们去转换为对应的字符串,这样仍然不够方便。

我们传给前端的时候是将参数通过json序列化传给前端的,我们可以使用Jackson 为我们提供了两个注解,解决这个问题。

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

最终的代码

@EnumConvertMethod
@JsonCreator(mode = JsonCreator.Mode.DELEGATING)
@Nullable
public static LanguageType get(String type) {
        for (LanguageType value : LanguageType.values()) {
            if (value.getType().equals(type)) {
                return value;
            }
        }
        return null;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值