Guida alle conversioni di tipo primavera

1. Introduzione

In questo articolo, daremo uno sguardo alle conversioni di tipo di Spring.

Spring fornisce diversi convertitori pronti all'uso per i tipi incorporati; questo significa convertire in / da tipi di base come String, Integer, Boolean e una serie di altri tipi.

Oltre a questo, Spring fornisce anche un solido SPI di conversione del tipo per lo sviluppo dei nostri convertitori personalizzati.

2. Built-in Converter s

Inizieremo con i convertitori disponibili out-of-the-box in primavera; diamo un'occhiata alla conversione da String a Integer :

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

L'unica cosa che dobbiamo fare qui è eseguire il cablaggio automatico del ConversionService fornito da Spring e chiamare il metodo convert () . Il primo argomento è il valore che vogliamo convertire e il secondo argomento è il tipo di destinazione in cui vogliamo convertire.

Oltre a questo esempio da String a Integer , ci sono molte altre combinazioni disponibili per noi.

3. Creazione di un convertitore personalizzato

Diamo un'occhiata a un esempio di conversione di una rappresentazione String di un Employee in un'istanza Employee .

Ecco la classe Employee :

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

La stringa sarà una coppia separata da virgole che rappresenta id e stipendio. Ad esempio, "1,50000,00".

Al fine di creare il nostro personalizzato Converter , abbiamo bisogno di implementare il convertitore di interfaccia e attuare il convert () Metodo:

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

Non abbiamo ancora finito. Dobbiamo anche dire a Spring di questo nuovo convertitore aggiungendo StringToEmployeeConverter a FormatterRegistry . Questo può essere fatto implementando il WebMvcConfigurer e sovrascrivendo il metodo addFormatters () :

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

E questo è tutto. Il nostro nuovo convertitore è ora disponibile per ConversionService e possiamo usarlo allo stesso modo di qualsiasi altro convertitore integrato :

@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. Conversione implicita

Oltre a queste conversioni esplicite tramite ConversionService , Spring è anche in grado di convertire implicitamente i valori direttamente nei metodi Controller per tutti i convertitori registrati:

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

Questo è un modo più naturale di utilizzare il convertitore . Aggiungiamo un test per vederlo in azione:

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

Come puoi vedere, il test stamperà tutti i dettagli della richiesta oltre alla risposta. Ecco l' oggetto Employee in formato JSON che viene restituito come parte della risposta:

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

4. Creazione di un ConverterFactory

È anche possibile creare un ConverterFactory che crea Converter su richiesta. Ciò è particolarmente utile nella creazione di convertitori per enumerazioni .

Diamo un'occhiata a un Enum davvero semplice:

public enum Modes { ALPHA, BETA; }

Successivamente, creiamo un StringToEnumConverterFactory che può generare Convertitori per convertire una stringa in qualsiasi 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); } }

Come possiamo vedere, la classe factory utilizza internamente un'implementazione dell'interfaccia Converter .

Una cosa da notare qui è che sebbene useremo le nostre modalità Enum per dimostrare l'utilizzo, non abbiamo menzionato Enum da nessuna parte in StringToEnumConverterFactory . La nostra classe factory è abbastanza generica da generare i convertitori su richiesta per qualsiasi tipo di Enum .

Il prossimo passo è registrare questa classe di fabbrica come abbiamo registrato il nostro convertitore nell'esempio precedente:

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

Ora il ConversionService è pronto per convertire String s in Enum s:

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

5. Creazione di un GenericConverter

Un GenericConverter ci fornisce una maggiore flessibilità per creare un convertitore per un uso più generico a costo di perdere una certa sicurezza di tipo.

Consideriamo un esempio di conversione di un valore Integer , Double o String in un valore BigDecimal. Non abbiamo bisogno di scrivere tre Converter per questo. Un semplice GenericConverter potrebbe servire allo scopo.

Il primo passo è dire a Spring quali tipi di conversione sono supportati. Lo facciamo creando un set di 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); } }

Il passo successivo è sovrascrivere il metodo convert () nella stessa classe:

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

Il metodo convert () è il più semplice possibile. Tuttavia, TypeDescriptor ci fornisce una grande flessibilità in termini di acquisizione dei dettagli riguardanti l'origine e il tipo di destinazione.

Come avrai già intuito, il passaggio successivo è registrare questo convertitore :

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

L'utilizzo di questo convertitore è simile agli altri esempi che abbiamo già visto:

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

In questo tutorial, abbiamo visto come utilizzare ed estendere il sistema di conversione del tipo di Spring con vari esempi.

Come sempre, il codice sorgente completo di questo articolo può essere trovato su GitHub.