• spring boot 枚举使用的坑


    java 枚举的功能挺多,但是坑更多,使用的时候要注意。如下面这个枚举。

    @Getter
    @AllArgsConstructor
    public enum  EnumExpenseType implements BaseEnum {
        小欢喜(1),
        大欢喜(2);
    private final int value; }

    咋一看,没什么问题,但是具体使用过程中,总是会出问题。原因就是这个枚举没有按照从0开始索引,除此之外即使从0开始,中间有断的索引也会有问题。主要出现在以下方面:

    1. 在controller的方法中,比如以这个枚举为参数,如下代码:

        @RequestMapping("/**")
        public String getRejectReasons(EnumExpenseType type) {
            return "";
        }

    前台传入的参数如果是type:1, 那它值应该是:小欢喜,实际上呢?

    Caused by: java.lang.IllegalArgumentException: No enum constant com.**.EnumReasonType.1
    at java.lang.Enum.valueOf(Enum.java:238) ~[?:1.8.0_111]
    at org.springframework.core.convert.support.StringToEnumConverterFactory$StringToEnum.convert(StringToEnumConverterFactory.java:52) ~[spring-core-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.core.convert.support.StringToEnumConverterFactory$StringToEnum.convert(StringToEnumConverterFactory.java:38) ~[spring-core-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.core.convert.support.GenericConversionService$ConverterFactoryAdapter.convert(GenericConversionService.java:436) ~[spring-core-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41) ~[spring-core-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:191) ~[spring-core-5.1.8.RELEASE.jar:5.1.8.RELEASE]
    at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:129) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:73) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:53) ~[spring-beans-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:693) ~[spring-context-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    at org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:124) ~[spring-web-5.1.7.RELEASE.jar:5.1.7.RELEASE]
    ... 81 more

    Failed to convert value of type 'java.lang.String' to required type 'com.**.EnumExpenseType'; 
    nested exception is org.springframework.core.convert.ConversionFailedException:
    Failed to convert from type [java.lang.String] to type [com.**.EnumExpenseType] for value '1';
    nested exception is java.lang.IllegalArgumentException: No enum constant com.***.EnumExpenseType.1
    实际上它却报了个错。转换失败了。
    查看报错信息,可以定位到是spring框架中StringToEnumConverterFactory中转换失败,具体代码如下:
    private static class StringToEnum<T extends Enum> implements Converter<String, T> {
    
            private final Class<T> enumType;
    
            public StringToEnum(Class<T> enumType) {
                this.enumType = enumType;
            }
    
            @Override
            public T convert(String source) {
                if (source.isEmpty()) {
                    // It's an empty enum identifier: reset the enum value to null.
                    return null;
                }
                return (T) Enum.valueOf(this.enumType, source.trim()); 
            }
        }

    是Enum.valueOf这里报错,Enum.valueOf的后面的值并不是我们的value,而是name(这里的小欢喜)。

    所以,我们不能使用这个spring提供converter,需要自定义一个:StringToEnumConverterFactory 

    public class StringToEnumConverterFactory implements ConverterFactory<String, BaseEnum> {
    
        private static final Map<Class, Converter> converterMap =  new HashMap<>();
    
        @Override
        public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
            Converter<String, T> converter = converterMap.get(targetType);
            if(converter == null) {
                converter = new StringToEnumConverter<>(targetType);
                converterMap.put(targetType, converter);
            }
            return converter;
        }
    
        class StringToEnumConverter<T extends BaseEnum> implements Converter<String, T> {
            private Map<String, T> enumMap = new HashMap<>();
    
            StringToEnumConverter(Class<T> enumType) {
                T[] enums = enumType.getEnumConstants();
                for(T e : enums) {
                    enumMap.put(String.valueOf(e.getValue()), e);
                }
            }
    
            @Override
            public T convert(String source) {
    
                T t = enumMap.get(source);
                if (t == null) {
                    // 异常可以稍后去捕获
                    throw new IllegalArgumentException("No element matches " + source);
                }
                return t;
            }
        }
    
    }

    然后再将这个工厂配置到项目中WebMvcConfigurationSupport:

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

    意思就是string 转 BaesEnum都走这个converter。

    至此这个坑就算解决了。

    下一篇继续...

  • 相关阅读:
    [洛谷P1886]滑动窗口 (单调队列)(线段树)
    树状数组详细解析
    离散化
    kettle使用总结(一)
    springBoot开发的web项目打war包部署到已有的tomcat容器中
    集群时钟同步
    linux服务器安装nginx
    Tomcat学习之体系架构
    码云新建仓库上传项目
    linux系统安装mysql yum方式
  • 原文地址:https://www.cnblogs.com/hankuikui/p/11429689.html
Copyright © 2020-2023  润新知