Panduan untuk Konversi Jenis Musim Semi

1. Perkenalan

Pada artikel ini, kita akan melihat jenis konversi Spring.

Spring menyediakan berbagai konverter out-of-the-box untuk tipe built-in; ini berarti mengonversi ke / dari tipe dasar seperti String, Integer, Boolean, dan sejumlah tipe lainnya.

Selain itu, Spring juga menyediakan SPI konversi tipe solid untuk mengembangkan konverter kustom kami.

2. Built-in Converter s

Kami akan mulai dengan konverter yang tersedia di luar kotak di Spring; mari kita lihat konversi String ke Integer :

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

Satu-satunya hal yang perlu kita lakukan di sini adalah melakukan autowire ConversionService yang disediakan oleh Spring dan memanggil metode convert () . Argumen pertama adalah nilai yang ingin kita ubah dan argumen kedua adalah tipe target yang ingin kita ubah.

Terlepas dari contoh String to Integer ini , ada banyak kombinasi lain yang tersedia untuk kita.

3. Membuat Konverter Kustom

Mari kita lihat contoh konversi representasi String dari sebuah instance Employee menjadi sebuah instance Employee .

Inilah kelas Karyawan :

public class Employee { private long id; private double salary; // standard constructors, getters, setters }

The String akan menjadi pasangan dipisahkan koma mewakili id dan gaji. Misalnya, "1.50000.00".

Untuk membuat Konverter kustom kita, kita perlu mengimplementasikan antarmuka Konverter dan mengimplementasikan metode 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])); } }

Kami belum selesai. Kita juga perlu memberi tahu Spring tentang konverter baru ini dengan menambahkan StringToEmployeeConverter ke FormatterRegistry . Ini bisa dilakukan dengan mengimplementasikan WebMvcConfigurer dan mengganti metode addFormatters () :

@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new StringToEmployeeConverter()); } }

Dan itu dia. Konverter baru kami sekarang tersedia untuk Layanan Konversi dan kami dapat menggunakannya dengan cara yang sama seperti Konverter bawaan lainnya :

@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. Konversi Implisit

Di luar konversi eksplisit ini menggunakan ConversionService , Spring juga mampu secara implisit mengubah nilai langsung dalam metode Controller untuk semua konverter terdaftar:

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

Ini adalah cara yang lebih alami untuk menggunakan Konverter . Mari tambahkan tes untuk melihatnya beraksi:

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

Seperti yang Anda lihat, tes akan mencetak semua detail permintaan serta responsnya. Berikut adalah objek Employee dalam format JSON yang dikembalikan sebagai bagian dari respons:

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

4. Membuat ConverterFactory

Anda juga dapat membuat ConverterFactory yang membuat Konverter sesuai permintaan. Ini sangat membantu dalam membuat Konverter untuk Enums .

Mari kita lihat Enum yang sangat sederhana:

public enum Modes { ALPHA, BETA; }

Selanjutnya, mari buat StringToEnumConverterFactory yang dapat menghasilkan Converter s untuk mengonversi String ke Enum apa pun :

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

Seperti yang bisa kita lihat, kelas pabrik secara internal menggunakan implementasi antarmuka Konverter .

One thing to note here is that although we'll use our Modes Enum to demonstrate the usage, we haven't mentioned the Enum anywhere in the StringToEnumConverterFactory. Our factory class is generic enough to generate the Converters on demand for any Enum type.

The next step is to register this factory class as we registered our Converter in the previous example:

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

Now the ConversionService is ready to convert Strings to Enums:

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

5. Creating a GenericConverter

A GenericConverter provides us more flexibility to create a Converter for a more generic use at the cost of losing some type safety.

Let's consider an example of converting an Integer, Double, or a String to a BigDecimal value.We don't need to write three Converters for this. A simple GenericConverter could serve the purpose.

The first step is to tell Spring what types of conversion are supported. We do this by creating a Set of ConvertiblePair:

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

The next step is to override the convert() method in the same class:

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

Metode convert () sesederhana mungkin. Namun, TypeDescriptor memberi kami fleksibilitas yang tinggi dalam hal mendapatkan detail tentang sumber dan tipe target.

Seperti yang mungkin sudah Anda duga, langkah selanjutnya adalah mendaftarkan Konverter ini :

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

Menggunakan Konverter ini mirip dengan contoh lain yang telah kita lihat:

@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. Kesimpulan

Dalam tutorial ini, kita telah melihat bagaimana menggunakan dan memperluas sistem konversi tipe Spring dengan berbagai contoh.

Seperti biasa, kode sumber lengkap untuk artikel ini dapat ditemukan di GitHub.