Spring型変換の手引き

スプリングタイプ変換のガイド

1. 前書き

この記事では、Springの型変換について見ていきます。

Springは、組み込み型用のさまざまなコンバーターをすぐに使用できるように提供します。これは、String, Integer, Booleanや他の多くのタイプなどの基本タイプとの間で変換することを意味します。

これとは別に、Springはカスタムコンバータを開発するためのソリッドタイプ変換SPIも提供します。

2. 組み込みのConverters

Springですぐに利用できるコンバーターから始めます。 StringからIntegerへの変換を見てみましょう。

@Autowired
ConversionService conversionService;

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

ここで行う必要があるのは、Springによって提供されるConversionServiceを自動配線し、convert()メソッドを呼び出すことだけです。 最初の引数は変換する値で、2番目の引数は変換先のターゲットタイプです。

このStringからIntegerの例とは別に、他にもさまざまな組み合わせがあります。

3. カスタムConverterの作成

EmployeeString表現をEmployeeインスタンスに変換する例を見てみましょう。

Employeeクラスは次のとおりです。

public class Employee {

    private long id;
    private double salary;

    // standard constructors, getters, setters
}

Stringは、idsalary.を表すコンマ区切りのペアになります(例:「1,50000.00」)。

カスタムConverterを作成するには、Converter<S, T>インターフェイスを実装し、convert()メソッドを実装する必要があります。

public class StringToEmployeeConverter
  implements Converter {

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

まだ終わっていません。 また、StringToEmployeeConverterFormatterRegistryに追加して、この新しいコンバーターについてSpringに通知する必要があります。 これは、WebMvcConfigurerを実装し、addFormatters()メソッドをオーバーライドすることで実行できます。

@Configuration
public class WebConfig implements WebMvcConfigurer {

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

以上です。 新しいConverterConversionServiceで利用できるようになり、他の組み込みの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. 暗黙的な変換

登録されているすべてのコンバーターに対して、ConversionServiceSpring is also capable of implicitly converting values right in Controller methodsを使用したこれらの明示的な変換以外に、次のようになります。

@RestController
public class StringToEmployeeConverterController {

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


これは、Convertersを使用するより自然な方法です。 テストを追加して、実際の動作を確認しましょう。

@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)))
}

ご覧のように、テストは要求と応答のすべての詳細を出力します。 応答の一部として返されるJSON形式のEmployeeオブジェクトは次のとおりです。

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

4. ConverterFactoryの作成

オンデマンドでConvertersを作成するConverterFactoryを作成することもできます。 これは、EnumsConvertersを作成する場合に特に役立ちます。

本当に単純な列挙型を見てみましょう。

public enum Modes {
    ALPHA, BETA;
}

次に、Stringを任意のEnumに変換するためのConvertersを生成できるStringToEnumConverterFactoryを作成しましょう。

@Component
public class StringToEnumConverterFactory
  implements ConverterFactory {

    private static class StringToEnumConverter
      implements Converter {

        private Class enumType;

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

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

    @Override
    public  Converter getConverter(
      Class targetType) {
        return new StringToEnumConverter(targetType);
    }
}

ご覧のとおり、ファクトリクラスは内部でConverterインターフェイスの実装を使用しています。

ここで注意すべきことの1つは、Modes Enumを使用して使用法を示しますが、StringToEnumConverterFactoryのどこにもEnumについては言及していないということです。 Our factory class is generic enough to generate the Converters on demand for any Enum type

次のステップは、前の例でConverterを登録したので、このファクトリクラスを登録することです。

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

これで、ConversionServiceStringsをEnumsに変換する準備ができました。

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

5. GenericConverterの作成

GenericConverterは、型の安全性を失うことを犠牲にして、より一般的な使用のためにConverterを作成するためのより多くの柔軟性を提供します。

IntegerDouble、またはStringBigDecimal値に変換する例を考えてみましょう。このために3つのConvertersを記述する必要はありません。 。 単純なGenericConverterが目的を果たすことができます。

最初のステップは、サポートされている変換のタイプをSpringに伝えることです。 これを行うには、ConvertiblePairSetを作成します。

public class GenericBigDecimalConverter
  implements GenericConverter {

@Override
public Set 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());
}

このConverterの使用は、すでに見た他の例と似ています。

@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の型変換システムを使用および拡張する方法を説明しました。

いつものように、この記事の完全なソースコードはover on GitHubにあります。