java常量数据用枚举封装(三)优雅对接前端-ConverterFactory

前言

前面的文章已经解决了处理单个枚举常量的数据转换的问题。
但这并不是最终解决方案,我们不可能为每一个枚举类都创建一套完整的转换类,所以就有了本文,最终版的解决方案

问题处理

改造枚举类

解决通用化问题,自然是要用到泛型,所以我们要为每一个枚举类抽取一个父类。
但java中的枚举类型是无法继承的,不过可以实现接口,所以我们在这里抽取一个接口出来,并让后续所有的枚举常量类都实现该接口。
改造之前,首先要明白,枚举实际上也是一个类,枚举实例也是一个对象
所以枚举类实现了接口后,所有枚举类对象都会有该接口方法。

要抽取的内容:

首先要抽取getValue方法,因为一般情况,都要用父类引用调用子类方法,避免不必要的强制转换。
然后就是获取所有枚举类型的方法,用户遍历匹配枚举对象。
这里仅仅是我个人的思路,如果你们有更好的匹配方法,也可以自行设计。
接着可以在接口中创建一个默认方法,用于匹配枚举对象。
所以,这个接口需要有泛型,泛型就是实现该接口的枚举类型!

接口源码

/**
 * @author jeff
 * @since 2023/4/30 17:49
 */
public interface ConstantEnumIFace<T extends ConstantEnumIFace> {
    /**
     * 返回每个枚举对象的真值
     * @return
     */
    int getValue();

    /**
     * 返回每个枚举的所有枚举对象
     * @return
     */
    T[] theEnums();
    /**
     * 遍历匹配
     * @param value
     * @return
     */
    default T theEnum(int value) {
        T[] enums = theEnums();
        for (T theEnum: enums) {
            if (value==theEnum.getValue()){
                System.out.println("已匹配到数据:"+theEnum);
                return theEnum;
            }
        }
        throw new RuntimeException("无效枚举值"+value);
    }

}

这里需要声明接口泛型是继承该接口的,因为我们要调用接口中的方法,不过具体方法内容由子类编写。

子类内容

import lombok.RequiredArgsConstructor;

/**
 * @author jeff
 * @since 2023/4/30 17:46
 */
@RequiredArgsConstructor
public enum UserStatus  implements ConstantEnumIFace<UserStatus> {

    DISABLE(2,"禁用"),
    ENABLE(3,"启用");

    private final int value;
    private final String desc;

    @Override
    public int getValue() {
        return value;
    }
    @Override
    public UserStatus[] theEnums() {
        return UserStatus.values();
    }
}

可以看到子类实现了上面的接口,并设置泛型为自身

自定义ConverterFactory

思路

查看springMVC的FormatterRegistry类的方法,可以发现有这么一个内容:
在这里插入图片描述
也就是提供一个工厂类,当SpringMVC需要转换某类数据时,会先判断有无该类型的Converter.
如果无,则找该类型的ConverterFactory,并执行其中的getConverter方法,传递的是该枚举类的Class对象
所以我们让所有的枚举类实现了同一个接口,这时,我们只要设置一个该接口类型的FactorySpringMVC为碰到的每一个枚举对象,就会调用该Facotry创建Converter
这时,可能会有人有疑问,为什么不能使用一个Converter,匹配所有该接口的数据类型呢?
这个问题本人也考虑过,同时也实践过,发现虽然SpringMVC不会调用该Converter的convert方法,同时我们没法知道他现在正在为具体哪个枚举进行转换,所以放弃了。

编写ConverterFactory

综上所述,可以知道现在要写一个,让SpringMVC碰到枚举类后,会调用其getConverter方法的Factory类

/**
 * @author jeff
* @since 2023/5/1 16:46
 */
public class ConstantEnumConverterFactory implements ConverterFactory<String, ConstantEnumIFace> {
    Map<Class,StringToEnumConverter> enumMap = new HashMap<>();
	public ConstantEnumConverterFactory() {
    }

    @Override
    public <T extends ConstantEnumIFace> Converter<String, T> getConverter(Class<T> targetType) {
        StringToEnumConverter stringToEnumConverter = enumMap.get(targetType);
        if (null==stringToEnumConverter) {
            stringToEnumConverter = new StringToEnumConverter(targetType);
            enumMap.put(targetType,stringToEnumConverter);
        }
        return stringToEnumConverter;
    }

    private static class StringToEnumConverter<T extends ConstantEnumIFace<T>> implements Converter<String, T> {
        private T firstEnum;

        public StringToEnumConverter(Class<T> enumType) {
            firstEnum=enumType.getEnumConstants()[0];
        }

        @Override
        public T convert(String sourceStr) {
            return firstEnum.theEnum(Integer.parseInt(sourceStr));
        }
    }
}

至于这里的getConverter方法,为什么要先从Map中获取,下文会解释的。

这时,当SpringMVC碰到ConstantEnumIFace的实现类时,会调用此Factory对象的getConverter方法,并传递实际的实现类,也就是枚举类的class对象。
这时,我们再利用该对象,去创建对应的Converter实例。
我们只要在创建Convertor时,调用类对象的getEnumConstants方法,并获取首位枚举对象供后续使用。

firstEnum = enumType.getEnumConstants()[0];

而真正的convert方法,则是使用刚刚初始化的首位枚举对象的方法

firstEnum.theEnum(Integer.parseInt(sourceStr));

而该方法,具体执行的是ConstantEnumIFace的默认方法。
同时该方法中的,获取所有枚举对象实例的方法,则由具体的枚举对象提供

注册ConverterFactory

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(new ConstantEnumConverterFactory());
    }
}

测试

在这里插入图片描述
可以看到debug断点已进入接口方法,从调用栈中,也能看到SpringMVC在调用FactorygetConverter方法。
但是!这不是我第一次发起的请求,这就说明,SpringMVC每次碰到相应数据时,都会调用一次getConverter方法获取Convertor
所以在上文中,getConverter方法要缓存一份Convertormap中,防止每次来数据都new一个Converter方法。

总结

到这里,用枚举类接收,注意是接收Get请求的query参数的问题,就彻底解决了。
接下来就是bodyJson数据了.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值