1. введение
В этой статье мы рассмотрим преобразования типов Spring.
Spring предоставляет готовые различные преобразователи для встроенных типов; это означает преобразование в/из базовых типов, таких как String, Integer, Boolean и ряд других типов.
Кроме того, Spring также предоставляет надежный SPI преобразования типов для разработки наших пользовательских конвертеров.
2. Встроенные преобразователи
Мы начнем с конвертеров, доступных из коробки весной; давайте посмотрим на преобразование String в Integer :
@Autowired ConversionService conversionService; @Test public void whenConvertStringToIntegerUsingDefaultConverter_thenSuccess() { assertThat( conversionService.convert("25", Integer.class)).isEqualTo(25); }
Единственное, что нам нужно сделать здесь, – это автоматически подключить службу преобразования , предоставляемую Spring, и вызвать метод convert () . Первый аргумент-это значение, которое мы хотим преобразовать, а второй аргумент-целевой тип, в который мы хотим преобразовать.
Помимо этого примера String to Integer , для нас доступно множество других комбинаций.
3. Создание пользовательского конвертера
Давайте рассмотрим пример преобразования String представления Employee в экземпляр Employee .
Вот Сотрудник класс:
public class Employee { private long id; private double salary; // standard constructors, getters, setters }
Строка | будет представлять собой пару, разделенную запятыми, представляющую id и зарплату. Например, “1,50000.00”.
Для того, чтобы создать наш обычай Конвертер , нам необходимо реализовать Преобразователь T> T> интерфейс и реализация преобразовать() метод:
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])); } }
Мы еще не закончили. Нам также нужно рассказать Spring об этом новом конвертере, добавив строку В EmployeeConverter в FormatterRegistry . Это можно сделать, реализовав метод WebMvcConfigurer и переопределив метод addFormatters() :
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new StringToEmployeeConverter()); } }
И это все. Наш новый Конвертер теперь доступен для Службы Преобразования , и мы можем использовать его так же, как и любой другой встроенный Конвертер :
@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. Неявное преобразование
Помимо этого явного преобразования с помощью службы преобразования , Spring также способен неявно преобразовывать значения прямо в Контроллере методах для всех зарегистрированных преобразователей:
@RestController public class StringToEmployeeConverterController { @GetMapping("/string-to-employee") public ResponseEntity
Это более естественный способ использования конвертера s. Давайте добавим тест, чтобы увидеть его в действии:
@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))) }
Как вы можете видеть, тест напечатает все детали запроса, а также ответ. Вот объект Employee в формате JSON, который возвращается как часть ответа:
{"id":1,"salary":2000.0}
4. Создание конвертерфактории
Также можно создать фабрику Конвертеров , которая создает Конвертеры по требованию. Это особенно полезно при создании Конвертера s для перечислений .
Давайте посмотрим на действительно простое перечисление:
public enum Modes { ALPHA, BETA; }
Далее, давайте создадим StringToEnumConverterFactory , который может генерировать Конвертер s для преобразования String в любое Перечисление :
@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); } }
Как мы видим, класс factory внутренне использует реализацию интерфейса Converter .
Здесь следует отметить, что, хотя мы будем использовать наш Modes Enum для демонстрации использования, мы не упоминали Enum нигде в StringToEnumConverterFactory . Наш фабричный класс достаточно универсален, чтобы генерировать Конвертер по требованию для любого Enum типа .
Следующим шагом является регистрация этого заводского класса, как мы зарегистрировали наш Конвертер в предыдущем примере:
@Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new StringToEmployeeConverter()); registry.addConverterFactory(new StringToEnumConverterFactory()); }
Теперь служба Преобразования готова преобразовать String s в Enum s:
@Test public void whenConvertStringToEnum_thenSuccess() { assertThat(conversionService.convert("ALPHA", Modes.class)) .isEqualTo(Modes.ALPHA); }
5. Создание универсального конвертера
A Универсальный Преобразователь предоставляет нам больше гибкости для создания Конвертер для более общего использования за счет потери некоторой безопасности типа.
Рассмотрим пример преобразования Integer , Double или String в BigDecimal значение.Для этого нам не нужно писать три Конвертера . Простой Универсальный конвертер может служить этой цели.
Первый шаг-сообщить Spring, какие типы преобразования поддерживаются. Мы делаем это, создавая Набор из Конвертируемой пары :
public class GenericBigDecimalConverter implements GenericConverter { @Override public SetgetConvertibleTypes () { 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() настолько прост, насколько это возможно. Однако дескриптор Type обеспечивает нам большую гибкость с точки зрения получения подробной информации об источнике и целевом типе.
Как вы, возможно, уже догадались, следующим шагом будет регистрация этого конвертера :
@Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new StringToEmployeeConverter()); registry.addConverterFactory(new StringToEnumConverterFactory()); registry.addConverter(new GenericBigDecimalConverter()); }
Использование этого конвертера похоже на другие примеры, которые мы уже видели:
@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 на различных примерах.
Как всегда, полный исходный код этой статьи можно найти на GitHub .