Spring MVC type conversion : PropertyEditor or Converter?
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).
What is the difference between PropertyEditor, Formatter and Converter in Spring?
To help to understand these concepts I would differentiate first the Spring specific functionality from that exposed from Java.
PropertyEditor
s and the related stuff are defined by the JavaBeans Specification.
The specification defines an API, mechanisms and conventions for dealing with objects, objects properties, and everything related to their changes, as events.
PropertyEditor
s are typically used in GUIs to handle the interaction between an UI and the underlying objects model, typically handling the conversion between properties values from/to its String
representation.
Spring itself actually uses different PropertyEditor
implementations and Java Beans conventions in many different situations. For example, from the docs:
A couple of examples where property editing is used in Spring:
Setting properties on beans is done by using
PropertyEditor
implementations. When you use String as the value of a property of some
bean that you declare in an XML file, Spring (if the setter of the
corresponding property has aClass
parameter) usesClassEditor
to try
to resolve the parameter to aClass
object.Parsing HTTP request parameters in Spring’s MVC framework is done by
using all kinds ofPropertyEditor
implementations that you can manually
bind in all subclasses of theCommandController
.
In summary, PropertyEditor
s allow you for a broader number of use cases.
Now, in the Spring world you need to do a differentiation as well between Spring MVC and Spring Core.
Please, note that both the Convert and the Formatter stuff are defined as core technologies, of relevance to any use case and not limited to the web framework.
The Spring documentation, when describing Spring Field Formatting, provides a great explanation about the purpose of every API/SPI and how they are related to PropertyEditor
s as well:
As discussed in the previous section,
core.convert
is a general-purpose
type conversion system. It provides a unifiedConversionService
API as
well as a strongly typed Converter SPI for implementing conversion logic
from one type to another. A Spring container uses this system to bind bean
property values. In addition, both the Spring Expression Language (SpEL)
andDataBinder
use this system to bind field values. For example, when
SpEL needs to coerce aShort
to aLong
to complete anexpression.setValue(Object bean, Object value)
attempt, thecore.convert
system performs the coercion.Now consider the type conversion requirements of a typical client
environment, such as a web or desktop application. In such environments,
you typically convert fromString
to support the client postback process,
as well as back toString
to support the view rendering process. In
addition, you often need to localizeString
values. The more general
core.convert Converter SPI does not address such formatting requirements
directly. To directly address them, Spring 3 introduced a convenient
Formatter SPI that provides a simple and robust alternative toPropertyEditor
implementations for client environments.In general, you can use the Converter SPI when you need to implement
general-purpose type conversion logic — for example, for converting between
ajava.util.Date
and aLong
. You can use the Formatter SPI when you
work in a client environment (such as a web application) and need to parse
and print localized field values. TheConversionService
provides a
unified type conversion API for both SPIs.
In the specific use case of Spring MVC the framework itself is able to handle simple types when handling the HTTP requests.
Type conversion is automatically applied based on the configured set of converters, although that behavior can be tweaked using DataBinder
s and the aforementioned Formatting system. Please, see the relevant docs.
In a typical use case in which you deal with reading and writing the body of HTTP requests and responses, when using the @RequestBody
, for example, Spring will use a bunch of different pre-configured HttpMessageConverter
implementations: the actual ones registered will depend on your configuration and the libraries imported in your project - say Jackson, for example. I was unable to find that point in the documentation but here is the link to the actual source code.
Please, consider review this related SO question, it could be of help.
How does Spring PropertyEditor know which Class propert to convert
When you register a PropertyEditor
in application context, you are providing the converter from String
to some type, in your case the JodaTime
type. The bean holding the type, (Contact
) don't matter. The application context will use your ContactPropertyEditor
editor any time that need to set a property of type JodaTime
as String
on any bean.
So ContactPropertyEdit
it's a bad name. It should be JodaTimePropertyEditor.
If you want a real ContactPropertyEditor
, it should convert Strings to Contacts. For example:
<bean id="someBeanHoldingAContact" class="someBeanClass">
<property name="contact" value="Hendrix, Jimi, 1942-11-27, http://www.jimihendrix.com" />
</bean>
and the ContactPropertyEditor should use the string value to create the contact.
The magic is in org.springframework.beans.BeanWrapperImp
class. See the javadoc
Spring MVC - PropertyEditor not called during ModelAttribute type conversion
Spring's ConversionService
and Converter
s are replacement for standard Java Beans PropertyEditor
s.
You need to implement Converter
instead of PropertyEditor
if this feature is based purely on conversion service.
To register your custom converters in WebDataBinder
you might use ConfigurableWebBindingInitializer
or @InitBinder
method.
Single Converter for multiple types in Spring (MVC)
The solution is using a ConverterFactory
such as
public final class EntityIdConverterFactory implements ConverterFactory<String, EntityId> {
@Override
public <T extends EntityId> Converter<String, T> getConverter(final Class<T> targetType) {
return new StringToEntityIdConverter<>(targetType);
}
private static final class StringToEntityIdConverter<T extends EntityId> implements Converter<String, T> {
private final Class<? extends T> targetType;
StringToEntityIdConverter(final Class<? extends T> targetType) {
this.targetType = targetType;
}
@Nullable
@Override
public T convert(final String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
try {
final T t = targetType.newInstance();
t.setId(Long.parseLong(source));
return t;
} catch (final Exception e) {
ReflectionUtils.handleReflectionException(e);
}
throw new AssertionError("Never reach here!");
}
}
}
What's the difference between Converter and HandlerMethodArgumentResolver?
A converter simply converts between two different types. An HttpMessageConverter
converts a request message having a defined media type to an instance of a defined class. Converters are usually called by argument resolvers.
An argument resolver provides a value for an argument. E.g. there is a resolver that creates the value based an a request parameter (@RequestParam
) or one that converts the request body (@RequestBody
). Both use converters.
But the value doesn't have to be related to the request. You could create a resolver that returns the current time, something like
public void foo(@CurrentTime Date) {
How request parameter binding and type conversion works in spring-mvc?
When you annotate a method with
@RequestMapping
, aRequestMappingHandlerAdapter
will register built-in argument resolvers.The arguments of the handler method will be scanned, and based on the type or annotation an appropriate argument resolver will kick-in. For example, for the
@RequestParam
annotation, anRequestParamMethodArgumentResolver
will be calledAn argument resolver guides further the conversion and binding if applicable. The conversion is handled by an appropriate implementation of the
Converter
orPropertyEditor
. The binding is handled by an appropriate instance ofDataBinder
. For the case ofRequestParamMethodArgumentResolver
and e.g.@RequestParam("age")
, aNumberEditor
guides the conversion, and aWebDataBinder
guides the binding.
Different cases that you have in your example, are all a subject of the above flow, but different argument resolvers kick in e.g. ModelAttributeMethodProcessor
, different converters of editors kick in (you can register custom ones for custom types, or for the User
a default constructor will be called etc.)
About the singleton dilemma, it refers to a single instance of the component, and it concerns only the instance variables of the component. Methods, and arguments within are OK to be new instance it plays no role from the singleton perspective
How to config a appropriate converter to spring MVC controller method?
Basically one of the options is Accept
Header, but spring has a built-in mechanism to deal with requirement like this called Content negotiation
, you can config your own config or use the standard spring config, checkout this article for further information: https://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc
Related Topics
Creating Classes Dynamically with Java
How to Stop Selenium from Creating Temporary Firefox Profiles Using Web Driver
Using Locales with Java's Tolowercase() and Touppercase()
Moving Items Around in an Arraylist
How to Create a Folder in Java
How to Create a Hashmap with Two Keys (Key-Pair, Value)
Hibernate One-To-One: Getid() Without Fetching Entire Object
Is Memory Leak? Why Java.Lang.Ref.Finalizer Eat So Much Memory
How to Add a New Sourceset to Gradle
Java Method Invocation VS Using a Variable
Calling Virtual Method in Base Class Constructor
How to Get a Unicode Character's Code
Can't a Swing Component Be Added to Multiple Containers
Are There Inline Functions in Java
Do I Need to Store the Salt with Bcrypt