拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Spring Boot 中的枚举映射

Spring Boot 中的枚举映射

白鹭 - 2022-12-26 2267 0 2

一、概述

在本教程中,我们将探讨在Spring Boot 中实现不区分大小写的枚举映射的不同方法。

首先,我们将了解枚举在Spring 中是如何默认映射的。然后,我们将学习如何应对区分大小写的挑战。

2. Spring默认枚举映射

Spring 在处理请求参数时依赖于几个内置的转换器来处理字符串转换。

通常,当我们将枚举作为请求参数传递时,它会在后台使用StringToEnumConverterFactory将传递的String转换为enum .

按照设计,此转换器调用Enum.valueOf(Class, String)这意味着给定的字符串必须与声明的枚举常量之一完全匹配

例如,让我们考虑Level枚举:

public enum Level {
LOW, MEDIUM, HIGH
}

接下来,让我们创建一个接受枚举作为参数的处理程序方法:

@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

处理程序方法发回MEDIUM,枚举常量MEDIUM的名称。

现在,让我们传递medium而不是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类带有一组已配置的转换器和格式化程序。

在这些开箱即用的转换器中,我们找到了StringToEnumIgnoringCaseConverterFactory.顾名思义,它以不区分大小写的方式将字符串转换为枚举

首先,我们需要添加和配置ApplicationConversionService

@Configuration
public class EnumMappingConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
ApplicationConversionService.configure(registry);
}
}

此类使用适用于大多数Spring Boot 应用程序的现成转换器配置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.使用自定义转换器

另一种解决方案是使用自定义转换器。在这里,我们将使用Apache Commons Lang 3 库。

首先,我们需要添加它的依赖

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>

这里的基本思想是创建一个转换字符串的转换器 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<S,T>接口的简单类

如我们所见,我们将String对象转换为大写。然后,我们使用Apache Commons Lang 3 库中的EnumUtils实用程序类从大写字符串中获取Level常量。

现在,让我们添加最后一块缺失的拼图。我们需要告诉Spring 我们新的自定义转换器。为此,我们将使用与之前相同的FormatterRegistry它提供了addConverter()方法来注册自定义转换器

@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToLevelConverter());
}

就是这样。我们的StringToLevelConverter现在在ConversionService中可用。

现在,我们可以像使用任何其他转换器一样使用它:

@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.使用自定义属性编辑器

Spring 在后台使用多个内置属性编辑器来管理String值和Java 对象之间的转换。

同样,我们可以创建一个自定义属性编辑器来将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()方法。

覆盖etAsText()的想法是将给定字符串的大写版本转换为Level枚举

值得注意的是,PropertyEditorSupport也提供了getAsText()。它在将Java 对象序列化为字符串时调用。所以,我们不需要在这里重写它。

我们需要注册LevelEditor,因为Spring 不会自动检测自定义属性编辑器。为此,我们需要在Spring 控制器中创建一个用@InitBinder注释的方法

@InitBinder
public void initBinder(WebDataBinder dataBinder) {
dataBinder.registerCustomEditor(Level.class, new LevelEditor());
}

现在我们将所有部分放在一起,让我们确认我们的自定义属性编辑器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,我们需要稍微改变我们的处理方法:

public String getByLevel(@RequestParam(required = false) Level level) {
if (level != null) {
return level.name();
}
return "undefined";
}

现在,让我们添加一个简单的测试用例来测试它:

@Test
public void whenPassingUnknownEnumConstant_thenReturnUndefined() throws Exception {
mockMvc.perform(get("/enummapping/get?level=unknown"))
.andExpect(status().isOk())
.andExpect(content().string("undefined"));
}

4。结论

在本文中,我们学习了在Spring 中实现不区分大小写的枚举映射的多种方法。

在此过程中,我们研究了一些使用内置和自定义转换器来完成此操作的方法。然后,我们了解了如何使用自定义属性编辑器实现相同的目标。


标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *