知識庫 / Spring RSS 訂閱

Spring 類型轉換指南

Spring
HongKong
4
02:13 PM · Dec 06 ,2025

 

1. 引言

本文將探討 Spring 的類型轉換功能。

Spring 提供了多種內置轉換器,用於轉換內置類型。這意味着它可以將數據轉換為/從諸如 String, Integer, Boolean 等基本類型以及其他多種類型。

此外,Spring 還提供了強大的類型轉換 SPI,用於開發自定義轉換器。

2. 內置 Converters</em translate="translate">

我們將從 Spring 中提供的內置 Converters 開始,先來看一下 StringInteger 轉換:

@Autowired
ConversionService conversionService;

@Test
public void whenConvertStringToIntegerUsingDefaultConverter_thenSuccess() {
    assertThat(
      conversionService.convert("25", Integer.class)).isEqualTo(25);
}

這裏唯一需要做的事情就是自動注入 Spring 提供的 ConversionService,並調用 convert() 方法。第一個參數是要轉換的值,第二個參數是目標類型。

除了這個 StringInteger 的示例之外,還有很多其他的組合方式可供我們使用。

3. 創建自定義轉換器

讓我們來看一個將 String 形式的 Employee 對象轉換為 Employee 實例的示例。

以下是 Employee 類:

public class Employee {

    private long id;
    private double salary;

    // standard constructors, getters, setters
}

字符串將是一個用逗號分隔的鍵值對,表示ID薪水。例如:“1,50000.00”。

為了創建我們自定義的轉換器,我們需要實現Converter<S, T>接口並實現convert()方法。

public class StringToEmployeeConverter
  implements Converter<String, Employee> {

    @Override
    public Employee convert(String from) {
        String[] data = from.split(",");
        return new Employee(
          Long.parseLong(data[0]), 
          Double.parseDouble(data[1]));
    }
}

我們還沒有完成。我們還需要將 Spring 通知關於這個新轉換器的信息,通過將 StringToEmployeeConverter 添加到 FormatterRegistry 來實現。這可以通過實現 WebMvcConfigurer 接口並覆蓋 addFormatters() 方法來完成:

@Configuration
public class WebConfig implements WebMvcConfigurer {

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

以下是翻譯後的內容:

這就是全部。我們的新 Converter 現在已提供給 ConversionService,並且我們可以像任何其他內置的 Converter 一樣使用它:

@Test
public void whenConvertStringToEmployee_thenSuccess() {
    Employee employee = conversionService
      .convert("1,50000.00", Employee.class);
    Employee actualEmployee = new Employee(1, 50000.00);
    
    assertThat(conversionService.convert("1,50000.00", 
      Employee.class))
      .isEqualToComparingFieldByField(actualEmployee);
}

3.1. 隱式轉換

除了使用 ConversionService 進行顯式轉換,Spring 還可以直接在 Controller 方法中將值隱式地轉換為所有已註冊的轉換器。

@RestController
public class StringToEmployeeConverterController {

    @GetMapping("/string-to-employee")
    public ResponseEntity<Object> getStringToEmployee(
      @RequestParam("employee") Employee employee) {
        return ResponseEntity.ok(employee);
    }
}

這是更自然的方式來使用 Converter。 讓我們添加一個測試,以驗證其效果:

@Test
public void getStringToEmployeeTest() throws Exception {
    mockMvc.perform(get("/string-to-employee?employee=1,2000"))
      .andDo(print())
      .andExpect(jsonPath("$.id", is(1)))
      .andExpect(jsonPath("$.salary", is(2000.0)))
}

如您所見,測試將打印所有請求的詳細信息以及響應。以下是作為響應返回的 Employee 對象,格式為 JSON 格式:

{"id":1,"salary":2000.0}

4. 創建 ConverterFactory

還可以創建一種 ConverterFactory,該 ConverterFactory會在需要時創建 Converter。 這在創建用於 枚舉(Enum)Converter 時尤其有用。

讓我們來看一個非常簡單的枚舉:

public enum Modes {
    ALPHA, BETA;
}

接下來,我們創建一個 StringToEnumConverterFactory,它可以生成用於將 String 轉換為任何 EnumConverter

@Component
public class StringToEnumConverterFactory 
  implements ConverterFactory<String, Enum> {

    private static class StringToEnumConverter<T extends Enum> 
      implements Converter<String, T> {

        private Class<T> enumType;

        public StringToEnumConverter(Class<T> enumType) {
            this.enumType = enumType;
        }

        public T convert(String source) {
            return (T) Enum.valueOf(this.enumType, source.trim());
        }
    }

    @Override
    public <T extends Enum> Converter<String, T> getConverter(
      Class<T> targetType) {
        return new StringToEnumConverter(targetType);
    }
}

如我們所見,工廠類內部使用 Converter 接口的實現。

需要注意的是,雖然我們將使用 Modes Enum 來演示其用法,但我們並未在 StringToEnumConverterFactory 中提及 Enum

我們的工廠類足夠通用,可以根據需要為任何 Enum 類型生成 Converter

下一步是像我們在之前示例中註冊的 Converter 一樣註冊此工廠類:

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

現在,ConversionService 已準備好將 String 轉換為 Enum

@Test
public void whenConvertStringToEnum_thenSuccess() {
    assertThat(conversionService.convert("ALPHA", Modes.class))
      .isEqualTo(Modes.ALPHA);
}

5. 創建一個通用的轉換器 (GenericConverter)

一個通用的轉換器 (GenericConverter) 提供了更大的靈活性,用於創建更通用的轉換器,但會犧牲一些類型安全。

考慮一下將 IntegerDoubleString 轉換為 BigDecimal 值的示例。我們不需要為這些類型分別編寫三個 Converter。一個簡單的 GenericConverter 可以滿足這個需求。

第一步是告訴 Spring 支持哪些轉換類型。我們通過創建一個 SetConvertiblePair 來完成:

public class GenericBigDecimalConverter 
  implements GenericConverter {

@Override
public Set<ConvertiblePair> getConvertibleTypes () {

    ConvertiblePair[] pairs = new ConvertiblePair[] {
          new ConvertiblePair(Number.class, BigDecimal.class),
          new ConvertiblePair(String.class, BigDecimal.class)};
        return ImmutableSet.copyOf(pairs);
    }
}

下一步是覆蓋同一類中 convert() 方法:

@Override
public Object convert (Object source, TypeDescriptor sourceType, 
  TypeDescriptor targetType) {

    if (sourceType.getType() == BigDecimal.class) {
        return source;
    }

    if(sourceType.getType() == String.class) {
        String number = (String) source;
        return new BigDecimal(number);
    } else {
        Number number = (Number) source;
        BigDecimal converted = new BigDecimal(number.doubleValue());
        return converted.setScale(2, BigDecimal.ROUND_HALF_EVEN);
    }
}

convert() 方法非常簡單易懂。但是,TypeDescriptor 提供了我們在源類型和目標類型方面獲取詳細信息的強大靈活性。

正如你可能已經猜到的,下一步是註冊這個Converter

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

使用這個轉換器與我們之前已經見過的其他示例非常相似:

@Test
public void whenConvertingToBigDecimalUsingGenericConverter_thenSuccess() {
    assertThat(conversionService
      .convert(Integer.valueOf(11), BigDecimal.class))
      .isEqualTo(BigDecimal.valueOf(11.00)
      .setScale(2, BigDecimal.ROUND_HALF_EVEN));
    assertThat(conversionService
      .convert(Double.valueOf(25.23), BigDecimal.class))
      .isEqualByComparingTo(BigDecimal.valueOf(Double.valueOf(25.23)));
    assertThat(conversionService.convert("2.32", BigDecimal.class))
      .isEqualTo(BigDecimal.valueOf(2.32));
}

6. 結論

在本教程中,我們學習瞭如何使用和擴展 Spring 的類型轉換系統,並通過各種示例進行了演示。

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

發佈 評論

Some HTML is okay.