知識庫 / Spring / Spring Boot RSS 訂閱

Spring Boot 枚舉映射

Spring Boot
HongKong
6
11:48 AM · Dec 06 ,2025

1. 概述

本教程將探討在 Spring Boot 中實現不區分大小寫的枚舉映射的不同方法。

首先,我們將瞭解枚舉在 Spring 中默認是如何映射的。然後,我們將學習如何解決大小寫敏感問題。

2. Spring 中默認枚舉映射

Spring 依賴於內置轉換器來處理請求參數中的字符串轉換。

通常,當我們通過請求參數傳遞一個枚舉時,它會使用 <a href="https://github.com/spring-projects/spring-framework/blob/main/spring-core/src/main/java/org/springframework/core/convert/support/StringToEnumConverterFactory.java">StringToEnumConverterFactory</a> 在其底層進行字符串的轉換到枚舉。

通過設計,這個轉換器被稱為 <em>Enum.valueOf(Class, String)</em>, 這意味着提供的字符串必須與聲明的枚舉常量完全匹配。

例如,讓我們考慮 <em>Level</em> 枚舉:

public enum Level {
    LOW, MEDIUM, HIGH
}

接下來,讓我們創建一個接受枚舉作為參數的處理方法:

@RestController
@RequestMapping("enummapping")
public class EnumMappingController {

    @GetMapping("/get")
    public String getByLevel(@RequestParam(name = "level", 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

<em >ApplicationConversionService</em > 類自帶了一組配置轉換器和格式器。

在這些內置轉換器中,我們找到 `StringToEnumIgnoringCaseConverterFactory。正如其名稱所示,它以不區分大小寫的方式將字符串轉換為枚舉。

首先,我們需要添加和配置 <em >ApplicationConversionService</em >:

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

此類配置 FormatterRegistry,提供適用於大多數 Spring Boot 應用程序的就緒式轉換器。

現在,讓我們通過一個測試用例確認一切正常工作:

@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());
}
<p>這就是全部。我們的 <em >StringToLevelConverter</em> 現在已在 <a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/convert/ConversionService.html"><em >ConversionService</em></a > 中可用。</p>
<p>現在,我們可以像使用任何其他轉換器一樣使用它:</p>
@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);
    }

}

如上所示,測試用例確認了 “低” 值被轉換為 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() 方法。

覆蓋 setAsText() 的思路是將給定的字符串的大寫形式轉換為 Level 枚舉。

值得注意的是,PropertyEditorSupport 也提供了 getAsText()。它在將 Java 對象序列化為字符串時被調用。因此,我們此處無需覆蓋它。

我們需要註冊我們的 LevelEditor,因為 Spring 不會自動檢測自定義 property editor。為此,我們需要在我們的 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 中不區分大小寫的枚舉映射的方法。

沿途,我們探討了使用內置和自定義轉換器來實現這些方法。此外,我們還看到了如何使用自定義屬性編輯器來實現相同的目標。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.