Spring Преобразование типа MVC: PropertyEditor или Converter?
Я ищу самый простой и простой способ привязки и преобразования данных в Spring MVC. Если возможно, без какой-либо конфигурации xml.
До сих пор я использовал PropertyEditors следующим образом:
public class CategoryEditor extends PropertyEditorSupport {
// Converts a String to a Category (when submitting form)
@Override
public void setAsText(String text) {
Category c = new Category(text);
this.setValue(c);
}
// Converts a Category to a String (when displaying form)
@Override
public String getAsText() {
Category c = (Category) this.getValue();
return c.getName();
}
}
и
...
public class MyController {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Category.class, new CategoryEditor());
}
...
}
Это просто: оба преобразования определены в одном классе, а привязка проста. Если бы я хотел выполнить общую привязку ко всем моим контроллерам, я мог бы добавить 3 строки в моей конфигурации xml.
Но Spring 3.x ввел новый способ сделать это, используя Преобразователи:
В контейнере Spring эта система может использоваться как альтернатива для PropertyEditors
Итак, скажем, я хочу использовать Конвертеры, потому что это "последняя альтернатива". Мне пришлось бы создавать два преобразователя:
public class StringToCategory implements Converter<String, Category> {
@Override
public Category convert(String source) {
Category c = new Category(source);
return c;
}
}
public class CategoryToString implements Converter<Category, String> {
@Override
public String convert(Category source) {
return source.getName();
}
}
Первый недостаток: Мне нужно сделать два класса. Преимущество: нет необходимости бросать благодарность за универсальность.
Затем, как я просто привязываю данные к конвертерам?
Второй недостаток: Я не нашел простой способ (аннотации или другие программные средства) сделать это в контроллере: ничего как someSpringObject.registerCustomConverter(...);
.
Единственные способы, которые я нашел, были бы утомительными, а не просто, и только об общей привязке перекрестного контроллера:
-
Конфигурация XML:
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="somepackage.StringToCategory"/>
<bean class="somepackage.CategoryToString"/>
</set>
</property>
</bean>
-
Конфигурация Java (только в Spring 3.1 +):
@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
protected void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToCategory());
registry.addConverter(new CategoryToString());
}
}
Со всеми этими недостатками, зачем использовать преобразователи? Я что-то упускаю? Есть ли другие трюки, о которых я не знаю?
У меня возникает соблазн продолжить использование PropertyEditors... Связывание намного проще и быстрее.
Ответы
Ответ 1
Со всеми этими недостатками, зачем использовать преобразователи? Я скучаю что нибудь? Есть ли другие трюки, о которых я не знаю?
Нет, я думаю, что вы очень подробно описали PropertyEditor и Converter, как каждый объявлен и зарегистрирован.
На мой взгляд, PropertyEditors ограничены по объему - они помогают преобразовать String в тип, и эта строка обычно поступает из пользовательского интерфейса, поэтому регистрация PropertyEditor с использованием @InitBinder и использование WebDataBinder имеет смысл.
Конвертер, с другой стороны, более общий, он предназначен для ЛЮБОГО преобразования в системе - не только для конверсий, связанных с UI (String to target type). Например, Spring Интеграция широко использует конвертер для преобразования полезной нагрузки сообщения в требуемый тип.
Я думаю, что для связанных с пользовательским интерфейсом потоков PropertyEditors по-прежнему подходят, особенно для случая, когда вам нужно сделать что-то обычное для определенного свойства команды. В других случаях я бы взял рекомендацию из справочника Spring и вместо этого записал конвертер (например, для преобразования из длинного идентификатора в объект в качестве примера).
Ответ 2
- Для конверсий String для конвертирования используются конвертеры (реализация org.springframework.format.Formatter) вместо конвертеров. Он имеет методы print (...) и parse (...), поэтому вам нужен только один класс вместо двух.
Чтобы зарегистрировать их, используйте FormattingConversionServiceFactoryBean, который может регистрировать как конвертеры, так и форматировщики вместо ConversionServiceFactoryBean.
- Новый материал Formatter имеет несколько дополнительных преимуществ:
- Интерфейс Formatter предоставляет объект Locale в его методах печати (...) и parse (...), поэтому преобразование строк может быть чувствительным к языку
- В дополнение к предварительно зарегистрированным форматам FormattingConversionServiceFactoryBean поставляется с несколькими удобными предварительно зарегистрированными объектами AnnotationFormatterFactory, которые позволяют вам указывать дополнительные параметры форматирования с помощью аннотации.
Например:
@RequestParam @DateTimeFormat (pattern = "MM-dd-yy" ) LocalDate baseDate...
Создавать собственные классы AnnotationFormatterFactory не очень сложно, см. Spring NumberFormatAnnotationFormatterFactory для простого примера.
Я думаю, что это устраняет необходимость в форматировщиках/редакторах, специфичных для контроллера. Используйте один ConversionService для всех контроллеров и настройте форматирование с помощью аннотаций.
- Я согласен с тем, что, если вам по-прежнему требуется некоторое преобразование строк в контроллере, самый простой способ - использовать редактор настраиваемых свойств. (Я попытался вызвать "binder.setConversionService(...)" в моем методе @InitBinder, но он терпит неудачу, поскольку объект связующего объекта имеет уже установленную "глобальную" службу преобразования. Кажется, что классы преобразования для каждого контроллера обескуражены Spring 3).
Ответ 3
Самый простой (при условии, что вы используете инфраструктуру persistence), но не лучшим способом является внедрение универсального конвертера сущностей через интерфейс ConditionalGenericConverter
, который будет конвертировать объекты, используя их метаданные.
Например, если вы используете JPA, этот конвертер может посмотреть, имеет ли указанный класс аннотацию @Entity
и использует аннотированное поле @Id
для извлечения информации и автоматического выполнения поиска с использованием поставленного значения String в качестве идентификатора для поиск.
public interface ConditionalGenericConverter extends GenericConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
ConditionalGenericConverter
является "конечным оружием" API конверсии Spring, но реализуется после того, как он сможет обрабатывать большинство преобразований сущностей, экономя время разработчика - это очень помогает, когда вы просто указываете классы сущности как параметры вашего контроллера и никогда не задумывайтесь о внедрении нового конвертера (конечно, за исключением пользовательских и не-сущностей).
Ответ 4
Вы можете сортировать работу вокруг необходимости иметь два отдельных класса конвертера, реализуя два преобразователя как статические внутренние классы.
public class FooConverter {
public static class BarToBaz implements Converter<Bar, Baz> {
@Override public Baz convert(Bar bar) { ... }
}
public static class BazToBar implements Converter<Baz, Bar> {
@Override public Bar convert(Baz baz) { ... }
}
}
Вам все равно необходимо будет зарегистрировать их отдельно, но по крайней мере это сократит количество файлов, которые вам нужно изменить, если вы внесете какие-либо изменения.