У меня есть следующая (упрощенная) форма в одном из моих представлений:
<form:form commandName="entry" method="POST">
<form:input type="text" path="name"/>
<form:input type="text" path="tags" />
<input type="submit" value="Submit"/>
</form:form>
Который будет привязан к следующему JavaBean:
public class Entry {
private String name;
private List<Tag> tags = new LinkedList<Tag>();
// setters and getters omitted
}
, поскольку я хочу использовать все новые необычные функции Spring 3, я использую контроллер на основе аннотаций для получения запроса POST:
@Controller
@RequestMapping("/entry")
public class EntryController {
@RequestMapping(method = RequestMethod.GET)
public ModelAndView show() {
ModelAndView mav = new ModelAndView("entry");
mav.addObject(new Entry());
return mav;
}
@RequestMapping(method = RequestMethod.POST)
public String add(@ModelAttribute("entry") @Valid Entry entry,
BindingResult result) {
// check validation from Binding result
// execute method on business beans: adding this entry to the system
// return a view if correct
}
}
Как видите, мне нужно преобразовать свой входной текст (который выглядит как tag1, tag2, tag3
) как список тегов, определив его так:
public class Tag {
private String name;
// setter and getter omitted
}
Существует несколько стратегий сделать это с помощью Spring 3.0:
( Извините, длинный пост, вопросы выделены жирным шрифтом )
Самый простой
Программирование нового свойства tagsAsText
для получения метода получения / установки в виде строки:
public class Entry {
// ...
public void setTagsAsText(String tags) {
// convert the text as a list of tags
}
public String getTagsAsText() {
// convert list of tags to a text
}
}
У этого подхода есть два недостатка:
- Я включил логику преобразования в свой объект домена, это проблема?
- Где я могу получить доступ к
BindingResult
в случае ошибки в строке?
Использование BeanInfo
Я также могу использовать BeanInfo для моего компонента:
public class EntryBeanInfo extends SimpleBeanInfo {
public PropertyDescriptor[] getPropertyDescriptors() {
try {
@Override
PropertyDescriptor tagsDescriptor = new PropertyDescriptor("tags", Entry.class) {
@Override
public PropertyEditor createPropertyEditor(Object bean) {
return new EntryTagListEditor(Integer.class, true);
};
};
// omitting others PropertyDescriptor for this object (for instance name)
return new PropertyDescriptor[] { tagListDescriptor };
}
catch (IntrospectionException ex) {
throw new Error(ex.toString());
}
}
}
и объявить один конвертер
public class EntryTagListEditor extends PropertyEditorSupport {
public void setAsText(String text) {
// convert the text to a list of Tag
}
public String getAsText() {
// convert the list of Tag to a String
}
}
У этого подхода также есть два недостатка:
- Мне нужно редактировать свой BeanInfo каждый раз, когда я добавляю / изменяю свой класс Entry. или есть какой-либо способ иметь простой способ определить мой BeanInfo (например, "для этого свойства, используйте это, иначе просто сделайте как обычно" )
- Где я могу получить доступ к
BindingResult
в случае ошибки в строке?
Использование конвертера
Конвертер использует общий механизм Java 5:
final class StringToTagList implements Converter<String, List<Tag>> {
public List<Tag> convert(String source) {
// convert my source to a list of Tag
}
}
Этот подход выглядит более элегантно, но все же имеет два недостатка:
- Кажется, я переопределяю все конвертеры по умолчанию, если я сконфигурирую этот конвертер в свойстве
ConversionServiceFactoryBean
, есть ли способ сохранить конвертеры по умолчанию?
- (снова) Где я могу получить доступ к
BindingResult
в случае ошибки в строке?