I am looking for the easiest and simplest way to bind and convert data in Spring MVC. If possible, without doing any xml configuration.
So far I've been using PropertyEditors like so :
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();
}
}
and
...
public class MyController {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Category.class, new CategoryEditor());
}
...
}
It is simple : both conversion are defined in the same class, and the binding is straightforward. If I wanted to do a general binding across all my controllers, I could still add 3 lines in my xml config.
But Spring 3.x introduced a new way to do it, using Converters :
Within a Spring container, this system can be used as an alternative to PropertyEditors
So let's say I want to use Converters because it is "the latest alternative". I would have to create two converters :
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();
}
}
First drawback : I have to make two classes. Benefit : no need to cast thanks to genericity.
Then, how do I simply data bind the converters ?
Second drawback : I haven't found any simple way (annotations or other programmatic facilities) to do it in a controller : nothing like someSpringObject.registerCustomConverter(...);
.
The only ways I've found would be tedious, not simple, and only about general cross-controller binding :
<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 config (only in Spring 3.1+) :
@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
protected void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToCategory());
registry.addConverter(new CategoryToString());
}
}
With all these drawbacks, why using Converters ? Am I missing something ? Are there other tricks that I am not aware of ?
I am tempted to go on using PropertyEditors... Binding is much easier and quicker.
With all these drawbacks, why using Converters ? Am I missing something ? Are there other tricks that I am not aware of ?
No, I think you have very comprehensively described both PropertyEditor and Converter, how each one is declared and registered.
In my mind, PropertyEditors are limited in scope - they help convert String to a type, and this string typically comes from UI, and so registering a PropertyEditor using @InitBinder and using WebDataBinder makes sense.
Converter on the other hand is more generic, it is intended for ANY conversion in the system - not just for UI related conversions(String to target type). For eg, Spring Integration uses a converter extensively for converting a message payload to a desired type.
I think for UI related flows PropertyEditors are still appropriate especially for the case where you need to do something custom for a specific command property. For other cases, I would take the recommendation from Spring reference and write a converter instead(for eg, to convert from a Long id to an entity say, as a sample).