Veiledning til vårtypekonvertering

1. Introduksjon

I denne artikkelen tar vi en titt på Spring's type konverteringer.

Spring tilbyr out-of-the-box forskjellige omformere for innebygde typer; dette betyr å konvertere til / fra grunnleggende typer som String, Integer, Boolean og en rekke andre typer.

Bortsett fra dette, tilbyr Spring også en solid type konverterings-SPI for å utvikle våre tilpassede omformere.

2. Innebygd Konverters

Vi starter med omformerne tilgjengelig utenom boksen om våren; la oss ta en titt på String til Heltall omdannelse:

@Autowired ConversionService conversionService; @Test offentlig ugyldig nårConvertStringToIntegerUsingDefaultConverter_thenSuccess () {assertThat (conversionService.convert ("25", Integer.class)). IsEqualTo (25); }

Det eneste vi trenger å gjøre her er å autoledre ConversionService levert av Spring og ring konvertere() metode. Det første argumentet er verdien vi vil konvertere, og det andre argumentet er måltypen vi vil konvertere til.

Bortsett fra dette String til Heltall for eksempel er det mange forskjellige andre kombinasjoner tilgjengelig for oss.

3. Opprette en egendefinert Konverter

La oss ta en titt på et eksempel på å konvertere en String representasjon av en Ansatt til en Ansatt forekomst.

Her er Ansatt klasse:

offentlig klasse ansatt {privat lang id; privat dobbeltlønn; // standard konstruktører, getters, setters}

De String vil være et kommaseparert par som representerer id og lønn. For eksempel “1,50000,00”.

For å skape vår skikk Konverter, må vi implementere Konverter grensesnitt og implementere konvertere() metode:

offentlig klasse StringToEmployeeConverter implementerer Converter {@Override public Employee convert (String from) {String [] data = from.split (","); returner ny ansatt (Long.parseLong (data [0]), Double.parseDouble (data [1])); }}

Vi er ikke ferdige ennå. Vi må også fortelle våren om denne nye omformeren ved å legge til StringToEmployeeConverter til FormatterRegistry. Dette kan gjøres ved å implementere WebMvcConfigurer og overordnet addFormatters () metode:

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

Og det er det. Vår nye Konverter er nå tilgjengelig for ConversionService og vi kan bruke den på samme måte som alle andre innebygde Konverter:

@Test offentlig ugyldig nårConvertStringToEmployee_thenSuccess () {Ansatt ansatt = conversionService .convert ("1,50000,00", Employee.class); Ansattes faktiskeMedarbeider = ny ansatt (1, 50000,00); assertThat (conversionService.convert ("1,50000,00", Employee.class)) .isEqualToComparingFieldByField (actualEmployee); }

3.1. Implisitt konvertering

Utover disse eksplisitte konvertering ved hjelp av ConversionService, Våren er også i stand til implisitt å konvertere verdier rett inn Kontroller metoder for alle registrerte omformere:

@RestController offentlig klasse StringToEmployeeConverterController {@GetMapping ("/ streng-til-ansatt") offentlig ResponseEntity getStringToEmployee (@RequestParam ("ansatt") Ansatt ansatt) {return ResponseEntity.ok (ansatt); }}

Dette er en mer naturlig måte å bruke Konverters. La oss legge til en test for å se den i aksjon:

@Test offentlig ugyldig getStringToEmployeeTest () kaster unntak {mockMvc.perform (get ("/ string-to-employee? Medarbeider = 1.2000")) .og gjør (skriv ut ()). Og forvent (jsonPath ("$. Id", er (1)) .andExpect (jsonPath ("$. lønn", er (2000.0)))}

Som du kan se, vil testen skrive ut alle detaljene i forespørselen samt svaret. Her er Ansatt objekt i JSON-format som returneres som en del av svaret:

{"id": 1, "lønn": 2000.0}

4. Opprette en ConverterFactory

Det er også mulig å lage en ConverterFactory som skaper Konverters på forespørsel. Dette er spesielt nyttig i å skape Konverters for Enums.

La oss ta en titt på en veldig enkel Enum:

public enum Modes {ALPHA, BETA; }

La oss deretter lage en StringToEnumConverterFactory som kan generere Konverters for konvertering av a String til hva som helst Enum:

@Component public class StringToEnumConverterFactory implementerer ConverterFactory {privat statisk klasse StringToEnumConverter implementerer Converter {private Class enumType; public StringToEnumConverter (Class enumType) {this.enumType = enumType; } offentlig T-konvertering (strengkilde) {retur (T) Enum.valueOf (this.enumType, source.trim ()); }} @Override public Converter getConverter (Class targetType) {return new StringToEnumConverter (targetType); }}

Som vi ser, bruker fabrikklassen internt en implementering av Konverter grensesnitt.

En ting å merke seg her er at selv om vi bruker vår Modus Enum For å demonstrere bruken har vi ikke nevnt Enum hvor som helst i StringToEnumConverterFactory. Fabrikklassen vår er generisk nok til å generere Konverters på forespørsel for noen Enum type.

Det neste trinnet er å registrere denne fabrikklassen når vi registrerte vår Konverter i forrige eksempel:

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

Nå er det ConversionService er klar til å konvertere Strings til Enums:

@Test offentlig ugyldig nårConvertStringToEnum_thenSuccess () {assertThat (conversionService.convert ("ALPHA", Modes.class)) .isEqualTo (Modes.ALPHA); }

5. Opprette en GenericConverter

EN GenericConverter gir oss mer fleksibilitet til å lage en Konverter for en mer generisk bruk på bekostning av å miste en viss type sikkerhet.

La oss vurdere et eksempel på konvertering av en Heltall, Dobbelt, eller a String til en BigDecimal verdi. Vi trenger ikke skrive tre Konverters for dette. En enkel GenericConverter kunne tjene formålet.

Det første trinnet er å fortelle Spring hvilke typer konvertering som støttes. Vi gjør dette ved å lage en Sett av ConvertiblePair:

offentlig klasse GenericBigDecimalConverter implementerer GenericConverter {@Override public Set getConvertibleTypes () {ConvertiblePair [] pairs = new ConvertiblePair [] {new ConvertiblePair (Number.class, BigDecimal.class), new ConvertiblePair (String.class, BigDecimal.class)}; returner ImmutableSet.copyOf (par); }}

Det neste trinnet er å overstyre konvertere() metode i samme klasse:

@Override public Object convert (Objektkilde, TypeDescriptor sourceType, TypeDescriptor targetType) {if (sourceType.getType () == BigDecimal.class) {returkilde; } if (sourceType.getType () == String.class) {String number = (String) source; returner nye BigDecimal (tall); } annet {Number number = (Number) kilde; BigDecimal konvertert = ny BigDecimal (number.doubleValue ()); returner convert.setScale (2, BigDecimal.ROUND_HALF_EVEN); }}

De konvertere() metoden er så enkel som den kan være. Imidlertid, den TypeDescriptor gir oss stor fleksibilitet når det gjelder å få informasjon om kilden og måltypen.

Som du kanskje allerede har gjettet, er neste trinn å registrere dette Konverter:

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

Bruker dette Konverter ligner på de andre eksemplene vi allerede har sett:

@Test offentlig ugyldig nårConvertingToBigDecimalUsingGenericConverter_thenSuccess () {assertThat (conversionService .convert (Integer.valueOf (11), BigDecimal.class)) .isEqualTo (BigDecimal.valueOf (11.00) .setScALAL._ 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. Konklusjon

I denne opplæringen har vi sett hvordan du kan bruke og utvide Spring's type konverteringssystem med forskjellige eksempler.

Som alltid kan hele kildekoden for denne artikkelen finnes på GitHub.