在本教程中,我们将学习如何在 Spring Boot 中实现不区分大小写的枚举映射。
2、Spring 默认的枚举映射
在处理请求参数时,Spring 依靠几个内置
Converter
来处理字符串转换。
通常情况下,将枚举作为请求参数时,默认会使用
StringToEnumConverterFactory
将传递的字符串转换为枚举。
该 Converter 会调用
Enum.valueOf(Class, String)
,这意味着给定的字符串必须要完全匹配枚举中的常量实例。
例如,让我们来看看
Level
枚举:
public enum Level {
LOW, MEDIUM, HIGH
接下来,创建一个使用枚举作为参数的 Handler Method:
@RestController
@RequestMapping("enummapping")
public class EnumMappingController {
@GetMapping("/get")
public String getByLevel(@RequestParam(required = false) Level level){
return level.name();
使用 CURL 向
http://localhost:8080/enummapping/get?level=MEDIUM
发送一个请求:
curl http://localhost:8080/enummapping/get?level=MEDIUM
Handler Method 会返回
MEDIUM
,即枚举实例
MEDIUM
的名称(
name()
)。
现在,让我们传递
medium
,看看会发生什么:
curl http://localhost:8080/enummapping/get?level=medium
{"timestamp":"2022-11-18T18:41:11.440+00:00","status":400,"error":"Bad Request","path":"/enummapping/get"}
如你所见,返回了无效请求异常:
Failed to convert value of type 'java.lang.String' to required type 'com.baeldung.enummapping.enums.Level';
nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam com.baeldung.enummapping.enums.Level] for value 'medium';
查看异常堆栈,可以发现 Spring 抛出了
ConversionFailedException
。它没有将
medium
识别为枚举实例。
3、不区分大小写的枚举映射
在映射枚举时,Spring 提供了几种方便的方法来解决大小写敏感性问题。
3.1、使用
ApplicationConversionService
ApplicationConversionService
类带有一组已配置好的的
Converter
和
Formatter
。
在这些开箱即用的 Converter 中,有一个
StringToEnumIgnoringCaseConverterFactory
。顾名思义,它能以大小写不敏感的方式将字符串转换为枚举实例。
首先,添加并配置
ApplicationConversionService
:
@Configuration
public class EnumMappingConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
ApplicationConversionService.configure(registry);
该类使用适合大多数 Spring Boot 应用的 Converter 来配置
FormatterRegistry
。
@RunWith(SpringRunner.class)
@WebMvcTest(EnumMappingController.class)
public class EnumMappingIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
public void whenPassingLowerCaseEnumConstant_thenConvert() throws Exception {
mockMvc.perform(get("/enummapping/get?level=medium"))
.andExpect(status().isOk())
.andExpect(content().string(Level.MEDIUM.name()));
我们可以看到,传递的
medium
参数 已成功转换为
MEDIUM
。
3.2、使用自定义 Converter
另一种解决方案是使用自定义 Converter。在这里,我们要使用
Apache Commons Lang 3
库。
首先,我们需要添加其依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
这里的基本思路是创建一个 Converter,将表示
Level
实例的字符串转换为实际的
Level
枚举实例:
public class StringToLevelConverter implements Converter<String, Level> {
@Override
public Level convert(String source) {
if (StringUtils.isBlank(source)) {
return null;
return EnumUtils.getEnum(Level.class, source.toUpperCase());
从技术角度看,自定义 Converter 只是一个实现
Converter<S,T>
接口的简单类。
正如我们所见,我们将
String
对象转换为大写。然后,我们使用 Apache Commons Lang 3 库中的
EnumUtils
工具类从
Level
中获取枚举实例。
最后一步,需要告诉 Spring 我们自定义的 Converter。为此,我们将使用之前的
FormatterRegistry
。它提供了
addConverter()
方法来注册自定义 Converter:
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToLevelConverter());
现在,可以在
ConversionService
中使用
StringToLevelConverter
了。
像使用其他 Converter 一样使用它:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = EnumMappingMainApplication.class)
public class StringToLevelConverterIntegrationTest {
@Autowired
ConversionService conversionService;
@Test
public void whenConvertStringToLevelEnumUsingCustomConverter_thenSuccess() {
assertThat(conversionService.convert("low", Level.class)).isEqualTo(Level.LOW);
如上所示,测试中的字符串
low
已转换为
Level.LOW
枚举实例。
3.3、使用自定义 Property Editor
Spring 使用多个内置
Property Editor
来管理
String
值和 Java 对象之间的转换。
同样,我们可以创建一个自定义 Property Editor,将
String
对象映射为
Level
枚举实例。
例如,自定义
LevelEditor
:
public class LevelEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) {
if (StringUtils.isBlank(text)) {
setValue(null);
} else {
setValue(EnumUtils.getEnum(Level.class, text.toUpperCase()));
如上,我们需要继承
PropertyEditorSupport
类并覆写
setAsText()
方法。
覆写
setAsText()
方法的目的是将给定字符串转换为大写后,解析为
Level
枚举实例。
注意,
PropertyEditorSupport
还提供了
getAsText()
方法。它在将 Java 对象序列化为字符串时被调用。因此,我们无需在此覆写它。
Spring 不会自动检测自定义的 Property Editor,我们需要手动注册
LevelEditor
。
在 Spring Controller 中创建一个注解了
@InitBinder
的
initBinder
方法:
@InitBinder
public void initBinder(WebDataBinder dataBinder) {
dataBinder.registerCustomEditor(Level.class, new LevelEditor());
现在,我们把所有组件组装在一起,使用一个测试用例来确认我们的自定义 Property Editor
LevelEditor
是否正常工作:
public class LevelEditorIntegrationTest {
@Test
public void whenConvertStringToLevelEnumUsingCustomPropertyEditor_thenSuccess() {
LevelEditor levelEditor = new LevelEditor();
levelEditor.setAsText("lOw");
assertThat(levelEditor.getValue()).isEqualTo(Level.LOW);
注意,
EnumUtils.getEnum()
如果未找到枚举会返回
null
值。
因此,为了避免
NullPointerException
,我们需要稍微修改一下 Handler Method:
public String getByLevel(@RequestParam(required = false) Level level) {
if (level != null) {
return level.name();
return "undefined";
现在,进行一下测试: