Leitfaden für die Umrüstung von Federtypen

1. Einleitung

In diesem Artikel werfen wir einen Blick auf die Typkonvertierungen von Spring.

Spring bietet sofort einsatzbereite verschiedene Konverter für eingebaute Typen. Dies bedeutet das Konvertieren in / von Basistypen wie String, Integer, Boolean und einer Reihe anderer Typen.

Abgesehen davon bietet Spring auch einen soliden SPI für die Konvertierung von Typen für die Entwicklung unserer kundenspezifischen Konverter.

2. Eingebauter Konverter s

Wir werden im Frühjahr mit den sofort einsatzbereiten Konvertern beginnen. Schauen wir uns die Konvertierung von String in Integer an :

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

Das einzige, was wir hier tun müssen, ist, den von Spring bereitgestellten ConversionService automatisch zu verdrahten und die convert () -Methode aufzurufen . Das erste Argument ist der Wert, den wir konvertieren möchten, und das zweite Argument ist der Zieltyp, in den wir konvertieren möchten.

Abgesehen von diesem Beispiel für String zu Integer stehen uns viele verschiedene andere Kombinationen zur Verfügung.

3. Erstellen eines benutzerdefinierten Konverters

Schauen wir uns ein Beispiel für die Konvertierung einer String- Darstellung eines Mitarbeiters in eine Mitarbeiterinstanz an .

Hier ist die Mitarbeiterklasse :

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

Der String ist ein durch Kommas getrenntes Paar, das ID und Gehalt darstellt. Zum Beispiel "1.50000,00".

Um unseren benutzerdefinierten Konverter zu erstellen , müssen wir die Konverter- Schnittstelle implementieren und die convert () -Methode implementieren :

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

Wir sind noch nicht fertig. Wir brauchen auch durch Zugabe des zu diesem neuen Konverter Frühling sagen StringToEmployeeConverter zum FormatterRegistry . Dies kann durch Implementieren des WebMvcConfigurer und Überschreiben der Methode addFormatters () erfolgen :

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

Und das ist es. Unser neuer Konverter ist jetzt für den ConversionService verfügbar und kann wie jeder andere integrierte Konverter verwendet werden :

@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. Implizite Konvertierung

Über diese explizite Konvertierung mit dem ConversionService hinaus kann Spring auch implizit Werte direkt in Controller- Methoden für alle registrierten Konverter konvertieren:

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

Dies ist eine natürlichere Art der Verwendung der Konverter . Fügen wir einen Test hinzu, um ihn in Aktion zu sehen:

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

Wie Sie sehen können, druckt der Test alle Details der Anfrage sowie die Antwort. Hier ist das Employee- Objekt im JSON-Format, das als Teil der Antwort zurückgegeben wird:

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

4. Erstellen einer ConverterFactory

Es ist auch möglich , eine erstellen ConverterFactory , die erzeugt Converter auf Anfrage s. Dies ist besonders hilfreich beim Erstellen von Konvertern für Enums .

Schauen wir uns eine wirklich einfache Aufzählung an:

public enum Modes { ALPHA, BETA; }

Als nächstes wollen wir erstellen StringToEnumConverterFactory , die erzeugen kann Converter s zur Umwandlung eines String in jedem Enum :

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

Wie wir sehen können, verwendet die Factory-Klasse intern eine Implementierung der Converter- Schnittstelle.

Eine Sache, die hier zu beachten ist, ist, dass wir, obwohl wir unsere Modi Enum verwenden , um die Verwendung zu demonstrieren, die Enum nirgendwo in der StringToEnumConverterFactory erwähnt haben . Unsere Factory-Klasse ist generisch genug, um die Konverter bei Bedarf für jeden Enum- Typ zu generieren .

Der nächste Schritt besteht darin, diese Werksklasse so zu registrieren, wie wir unseren Konverter im vorherigen Beispiel registriert haben :

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

Jetzt ist der ConversionService bereit, String s in Enum s zu konvertieren :

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

5. GenericConverter erstellen

Ein GenericConverter bietet uns mehr Flexibilität bei der Erstellung eines Konverters für eine allgemeinere Verwendung auf Kosten des Verlusts der Typensicherheit .

Betrachten wir ein Beispiel , eine der Umwandlung von Integer , Doppel- , oder einen String zu einem BigDecimal value.We brauchen nicht zu schreiben drei Converter s für diese. Ein einfacher GenericConverter könnte den Zweck erfüllen.

Der erste Schritt besteht darin, Spring mitzuteilen, welche Konvertierungsarten unterstützt werden. Wir tun dies durch die Schaffung eines Sets von 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); } }

Der nächste Schritt besteht darin, die convert () -Methode in derselben Klasse zu überschreiben :

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

Die convert () -Methode ist so einfach wie möglich. Der TypeDescriptor bietet uns jedoch große Flexibilität beim Abrufen der Details zu Quelle und Zieltyp .

Wie Sie vielleicht bereits erraten haben, besteht der nächste Schritt darin, diesen Konverter zu registrieren :

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

Die Verwendung dieses Konverters ähnelt den anderen Beispielen, die wir bereits gesehen haben:

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

In diesem Tutorial haben wir anhand verschiedener Beispiele gesehen, wie das Typkonvertierungssystem von Spring verwendet und erweitert wird.

Wie immer finden Sie den vollständigen Quellcode für diesen Artikel auf GitHub.