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。

至此这个坑就算解决了。

下一篇继续...

原文地址:https://www.cnblogs.com/hankuikui/p/11429689.html

时间: 2024-10-11 21:20:52

spring boot 枚举使用的坑的相关文章

初学spring boot踩过的坑

一.搭建spring boot环境 maven工程 pom文件内容 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-

部署spring boot + Vue遇到的坑(权限、刷新404、跨域、内存)

部署spring boot + Vue遇到的坑(权限.刷新404.跨域.内存) 项目背景是采用前后端分离,前端使用vue,后端使用springboot. 工具 工欲善其事必先利其器,我们先找一个操作Linux系统的工具极力推荐FinalShell. 一眼看过去是不是感觉很方便,对内存.CPU的监控也可以实时看到,访问目录更是方便,对于Linux小白来说简直是神兵利器. 好了,我要开始入坑了. 问题一:权限不够 把vue包放到tomcat->webapps->ROOT目录下. 启动tomcat:

记录Spring Boot小项目的一些坑

现有一个用spring boot的后台项目,项目开发rest端口与前端交互,使用Jython调用本地python代码.项目基于IDEA开发,部署在windows系统中. 第一个坑:跨域请求 前端使用ajax请求后台接口,后台返回json数据.后台独立测试(curl.restlet)没问题,前端出现报错跨域问题. 首先我们试图在ajax请求中增加dataType并设定为jsonp,结果没有报跨域问题,返回直接进入了error,状态码200,一些博客认为是后台数据不够严谨,不是严格的json格式,然

Spring Boot Admin 使用的坑

这几天公司需要部署SpringBootAdmin监控,用来时刻关注微服务的状态 按照官网的操作非常简单: 1. 添加依赖到pom.xml <dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-server</artifactId> <version>${springboot.admin.version}</version>

Spring Boot和JPA开发的一些坑

最近新的项目API接口基于Spring Boot和JPA开发,做了几天的学习和预研,发现使用Spring Boot开发效率上确实比较高,但也碰到了一些小坑,特别记录下. 配置文件问题 ,默认只要一个配置文件application.properties,不要再想ApplicationContent.xml等spring的配置文件了 logback配置文件路径问题,使用绝对路径读取没问题,基于classpath路径则读取不到 ,这个问题有待进一步解决 Spring-boot不能扫描到启动函数包外的b

Spring Boot 开发系列一 开发踩坑

这是学习spring boot 的第二周,公司号称这玩意是啥都不会的新手就可以填空开发,于是决定上手一把,怎么说我也是搞了快七八年的.NET和.NETcore,没想到无情打脸,快被这个能填空开的IDE搞疯了,下面是记下一些自己踩坑开发中遇到的一系列无穷无尽的问题. 一 .集成MyBatis a)首先如何理解这个  MyBatis 的东西,我的理解是 :MyBatis 有点类似ORM的感觉,跟 JPA很类似,就是一个orm,需要一个类似 dbcontext的东西,这个东西,在MyBatis 里面叫

spring cloud: 升级到spring boot 2.x/Finchley.RELEASE遇到的坑

spring boot2.x已经出来好一阵了,而且spring cloud 的最新Release版本Finchley.RELEASE,默认集成的就是spring boot 2.x,这几天将一个旧项目尝试着从低版本升级到 2.x,踩坑无数,记录一下: 一.gradle的问题 spring boot 2.x 要求gradle版本不能太旧,先把gradle升级到4.6版本,然后编译,各种问题,到gradle官网上查了下,build.gradle有几个小地方要调整 1.1 java-libary 的项目

Spring Boot 中的同一个 Bug,竟然把我坑了两次!

真是郁闷,不过这事又一次提醒我解决问题还是要根治,不能囫囵吞枣,否则相同的问题可能会以不同的形式出现,每次都得花时间去搞.刨根问底,一步到位,再遇到类似问题就可以分分钟解决了. 如果大家没看过松哥之前写的 Spring Boot 整合 Spring Session,可以先回顾下: Spring Boot 一个依赖搞定 session 共享,没有比这更简单的方案了! 第一次踩坑 事情是这样的,大概在今年 6 月初的时候,我在项目中使用到了 Session 共享,当时采用的方案就是 Redis+Sp

Spring Boot连接数据库,从MySql5.13飞跃到MySql8.0.18的坑

Spring Boot连接数据库,从MySql5.13飞跃到MySql8.0.18的坑 1.驱动名 驱动包用的是:mysql-connector-java-8.0.18.jar 驱动名从之前的:com.mysql.jdbc.Driver 改为:com.mysql.cj.jdbc.Driver 2.时区问题 昨天用MyBatis逆行工程的时候,出现了以下错误: ? The server time zone value '?D1ú±ê×?ê±??' is unrecognized or repres