У Spring 3 з'явився пакет core.convert
, який надає
загальну систему перетворення типів. Система визначає SPI (інтерфейс постачальника служб) для реалізації логіки
перетворення типів та API-інтерфейсу для здійснення перетворення типів під час виконання програми. У контейнері
Spring можна використовувати цю систему як альтернативу реалізації PropertyEditor
для перетворення
вилучених рядків значень властивостей біну в необхідні типи властивостей. Ти також можеш використовувати публічний
API-інтерфейс в будь-якому місці програми, де потрібне перетворення типів.
SPI-інтерфейс Converter
SPI-інтерфейс для реалізації логіки перетворення типів є простим і сильно типізовани, як показано в наступному визначенні інтерфейсу:
package org.springframework.core.convert.converter;
public interface Converter<S, T> {
T convert(S source);
}
Щоб створити власний перетворювач, реалізуй інтерфейс Converter
і параметризуй
S
як тип, з якого ти перетворюєш, і T
як тип, на який перетворюєш. Можна також
зрозумілим способом застосовувати такий перетворювач, якщо колекцію або масив S
необхідно перетворити
на масив або колекцію T
, за умови, що делегуючий перетворювач масивів або колекцій також зареєстрований
(що DefaultConversionService
робить за замовчуванням).
Для кожного виклику
convert(S)
вихідний аргумент гарантовано не є порожнім (null). Твій Converter
може
згенерувати будь-який неперевірений виняток, якщо перетворення не вдалося. Зокрема, він повинен генерувати виняток
IllegalArgumentException
, щоб повідомляти про неприпустиме значення джерела. Переконайся, що твоя
реалізація Converter
була безпечною.
Кілька реалізацій перетворювача для зручності надано в
пакеті core.convert.support
. Вони включають перетворювачі рядків на числа та інші поширені типи. У
наступному лістингу показаний клас StringToInteger
, який є типовою реалізацією Converter
:
package org.springframework.core.convert.support;
final class StringToInteger implements Converter<String, Integer> {
public Integer convert(String source) {
return Integer.valueOf(source);
}
}
Використання ConverterFactory
Якщо необхідно централізувати логіку перетворення для
всієї ієрархії класів (наприклад, при перетворенні з об'єктів String
на об'єкти Enum
),
можна реалізувати ConverterFactory
, як показано в наступному прикладі:
package org.springframework.core.convert.converter;
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
Параметризуй S як тип, з якого ти перетворюєш, а R як базовий тип, що визначає діапазон класів, у
які ти можеш перетворювати. Потім реалізуй getConverter(Class<T>)
), де T є підкласом R.
Як приклад розглянемо StringToEnumConverterFactory
:
package org.springframework.core.convert.support;
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter(targetType);
}
private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {
private Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
return (T) Enum.valueOf(this.enumType, source.trim());
}
}
}
Використання GenericConverter
Якщо тобі потрібна складна реалізація
Converter
, скористайся інтерфейсом GenericConverter
. GenericConverter
з
більш гнучкою, але менш строго типізованою на відміну від Converter
сигнатурою, підтримує перетворення
між кількома вихідними та цільовими типами. До того ж, GenericConverter
робить доступним контекст
вихідного та цільового поля, який можна використовувати для реалізації логіки перетворення. Такий контекст дозволяє
перетворювати типи на основі анотації поля або загальної інформації, оголошеної у сигнатурі поля. У наступному
лістингу показано визначення інтерфейсу GenericConverter
:
package org.springframework.core.convert.converter;
public interface GenericConverter {
public Set<ConvertiblePair> getConvertibleTypes();
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
Для реалізації GenericConverter
необхідно, щоб функція getConvertibleTypes()
повертала підтримувані пари типів джерела→ ціль. Потім реалізуй convert(Object, TypeDescriptor,
TypeDescriptor)
, щоб додати логіку перетворення. TypeDescriptor
джерела надає доступ до поля
джерела, що містить перетворюване значення. TypeDescriptor
цілі забезпечує доступ до цільового поля, в
яке має бути встановлене перетворене значення.
Хорошим прикладом GenericConverter
є
перетворювач, який перетворює масив Java на колекцію. Такий ArrayToCollectionConverter
здійснює
самоаналіз поля, що оголошує тип цільової колекції, щоб визначити тип елемента колекції. Це дозволяє перетворити
кожен елемент вихідного масиву на тип елемента колекції до того, як колекція буде встановлена в цільове поле.
Оскільки GenericConverter
є складнішим SPI-интерфейсом, його слід використовувати у разі потреби. Для
базових потреб перетворення типів
використовуй Converter
або ConverterFactory
.
Використання ConditionalGenericConverter
Іноді потрібно, щоб Converter
запускався лише під час виконання певної умови. Наприклад, тобі може
знадобитися запустити Converter
, тільки якщо в цільовому полі є певна анотація, або запустити Converter
,
тільки якщо в цільовому класі визначено конкретний метод (наприклад, метод static valueOf
). ConditionalGenericConverter
— це об'єднання інтерфейсів GenericConverter
та ConditionalConverter
, що дозволяє
визначати такі спеціальні критерії відповідності:
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
public interface ConditionalGenericConverter extends GenericConverter, ConditionalConverter {
}
Хорошим прикладом ConditionalGenericConverter
є IdToEntityConverter
, який здійснює
перетворення між постійним ідентифікатором сутності та посиланням на сутність. Такий
IdToEntityConverter
може бути підходящим, лише якщо цільовий тип сутності оголошує статичний пошуковий
метод (наприклад, findAccount(Long)
). Ти можеш виконати таку перевірку пошукового методу в реалізації
matches(TypeDescriptor, TypeDescriptor)
.
API ConversionService
ConversionService
визначає уніфікований API для виконання логіки перетворення типів під час виконання програми. Перетворювачі часто
починаються після наступного фасадного інтерфейсу:
package org.springframework.core.convert;
public interface ConversionService {
boolean canConvert(Class<?> sourceType, Class<?> targetType);
<T> T convert(Object source, Class<T> targetType);
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
Більшість реалізацій ConversionService
також реалізують ConverterRegistry
, який
надає SPI-інтерфейс для реєстрації перетворювачів. Внутрішня реалізація ConversionService
делегує своїм
зареєстрованим перетворювачам виконання логіки перетворення типів.
Надійна реалізація
ConversionService
надається в пакеті core.convert.support
.
GenericConversionService
— це реалізація загального призначення, що підходить для використання в
більшості середовищ. ConversionServiceFactory
надає зручну фабрику для створення загальних конфігурацій
ConversionService
.
Конфігурування ConversionService
ConversionService
— це об'єкт, який не зберігає стан, екземпляр якого створюється під час запуску програми та
подальшого спільного використання кількома потоками. У програмі Spring екземпляр ConversionService
зазвичай конфігурується для кожного контейнера Spring (або ApplicationContext
). Spring визначає цей
ConversionService
і використовує його щоразу, коли фреймворку потрібно виконати перетворення типу.
Також можна впровадити цей ConversionService
у будь-який з твоїх бінів і викликати його безпосередньо.
ConversionService
, то
використовується оригінальна система на основі PropertyEditor
.
Щоб зареєструвати в Spring стандартний ConversionService
, додай наступне визначення біна із
зазначенням id
у вигляді conversionService
:
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean"/>
Стандартний ConversionService
може перетворювати рядки, числа, типи, колекції, асоціативні масиви
та інші поширені типи. Щоб доповнити або замінити стандартні перетворювачі власними спеціальними, встанови
властивість converters
. Значення властивостей можуть реалізовувати будь-який з інтерфейсів Converter
,
ConverterFactory
або GenericConverter
.
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="example.MyCustomConverter"/>
</set>
</property>
</bean>
Також у додатку на Spring MVC часто використовується ConversionService
.
У деяких ситуаціях можна застосовувати форматування під час перетворення.
Програмне використання ConversionService
Щоб працювати з екземпляром ConversionService
програмно, ви можете запровадити посилання на нього,
як це робиться для будь-якого іншого біна. У цьому прикладі показано, як це зробити:
@Service
public class MyService {
public MyService(ConversionService conversionService) {
this.conversionService = conversionService;
}
public void doIt() {
this.conversionService.convert(...)
}
}
@Service
class MyService(private val conversionService: ConversionService) {
fun doIt() {
conversionService.convert(...)
}
}
Для більшості випадків можна використовувати метод convert
, що вказує targetType
,
але він не спрацює з більш складними типами, такими як колекція параметризованого елемента. Наприклад, якщо
потрібно програмно перетворити List
типу Integer
на List
типу
String
, то необхідно надати формальне визначення вихідного і цільового типів.
На щастя, TypeDescriptor
надає різні опції, що дозволяють зробити це так само просто, як показано в наступному прикладі:
DefaultConversionService cs = new DefaultConversionService();
List<Integer> input = ...
cs.convert(input,
TypeDescriptor.forObject(input), // Дескриптор типу List<Integer>
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));
val cs = DefaultConversionService()
val input: List<Integer> = ...
cs.convert(input,
TypeDescriptor.forObject(input), // Дескриптор типу List<Integer>
TypeDescriptor.collection(List::class.java, TypeDescriptor.valueOf(String::class.java)))
Зверни увагу, що DefaultConversionService
автоматично реєструє перетворювачі, які підходять для
більшості середовищ. До них належать перетворювачі колекцій, скалярні перетворювачі та базові перетворювачі Object-to-String
.
Ти можеш зареєструвати ті ж перетворювачі ConverterRegistry
за допомогою статичного методу addDefaultConverters
класу DefaultConversionService
.
Перетворювачі для типів значень повторно використовуються
для масивів та колекцій, тому немає необхідності створювати спеціалізований перетворювач для перетворення з Collection
типу S
на Collection
типу T
, якщо виходити з того, що стандартна обробка
колекцій буде доречною.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ