What's the difference between Converter and HandlerMethodArgumentResolver? - java

In Spring MVC, I can use PropertyEditor(Converter) or WebArgumentResolver(HandlerMethodArgumentResolver) to make customized command and form-backing objects. I'm puzzled which one should I use and what's the difference between them? Thanks a lot!
p.s. I know Converter and HandlerMethodArgumentResolver is recommended now. I've read the posts below:
Spring MVC type conversion : PropertyEditor or Converter?
Upgrading to spring-3.1 seems to break my CustomWebArgumentResolver

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) {

Related

Is any additional attributes required to force the java type in spring integration

We are using spring-integation (xml based configuration), In which we are performing below steps
Convert the payload (java-object) to json
Make the rest api call
Convert back to java-object
<int:object-to-json-transformer content-type="test.Request" input-channel="objectToJsonChannel" output-channel="apiChannel"/>
<int-http:outbound-gateway id="apiChannel"
request-channel="apiChannel"
reply-channel="jsonToObjectChannel"
....
/>
<int:json-to-object-transformer type="test.Response" input-channel="jsonToObjectChannel" output-channel="responseChannel"/>
Above code works till spring-integration version 5.1. When I upgrade to 5.2. It starts to throw the exception as
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [test.Request] to type [test.Response].
I have noticed that object-to-json-transformer add class type on the header with key json__TypeId__. Then it uses that class type for json-to-object-transformer.
But it is expected that type attribute mentioned on json-to-object-transformer should be used if mentioned.
Please suggest on fixing this issue or Is it really bug on spring integration (5.2).
Consider to add a <header-filter header-names="json_*" /> before calling your REST service. The <int:object-to-json-transformer> populates JsonHeaders to let downstream to know what the real type of JSON we curry in the payload.
A <int:json-to-object-transformer> prefers those headers instead of static type option.
But since the payload is already a different representation than those request headers it does a wrong thing.
I would suggest an option on the <int:json-to-object-transformer> to make a preference, but that would not be fully logical. Since we have changed a payload, it would be better to change its respective headers. Otherwise we just lying to ourselves.
On the other hand a HTTP Outbound Gateway can take care for your to convert request into a JSON for network and back from JSON response to some POJO type.
See https://docs.spring.io/spring-integration/docs/5.2.3.RELEASE/reference/html/http.html#http-outbound and its expected-response-type. As long as a contentType header is an application/json, you are good to avoid those <int:object-to-json-transformer> & <int:json-to-object-transformer>.

Can REST POST endpoint defined with java spring accept both XML and JSON

I found that this way i can make the service to accept both XML and JSON, but how do i have to set up the data class for it? Does it need both the XML and JSON annotation? Or does it need something special to work?
You can use #Consumes annotation to pass an array of accepted MIME types:
The value of #Consumes is an array of String of acceptable MIME types. For example:
#Consumes({"text/plain, text/html"})
from https://docs.oracle.com/cd/E19798-01/821-1841/gipzh/index.html

Respond to html, json and xml with Spring MVC content negotiation

All examples that I have found around internet were about to use content negotiation with json, xml etc. Is there any chance to have only one method producing all kind of contents, like:
#RequestMapping(produces={"text/html","application/json"})
public List<Product> list() {
return productDAO.list();
}
I tried to use the ContentNegotiationManager, but nothing worked for me.
One method can return responses of different content types. Some you can get with default settings, some you have to additionally configure. Take for example a following method, quite similar to yours,
#RequestMapping(value="/response", method=RequestMethod.GET, produces={MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public #ResponseBody Foo multipleTypes() {
return new Foo();
}
this method is capable of returning both XML and JSON, even more Spring MVC will automatically configure the converters if you have JAXB2 and Jackson libs on the classpath.
When reasoning whether it will return an XML or JSON its where content negotiation comes to play. If the request is suffixed with a path e.g. /response.json or /response.xml the response will be set based on it. The resolution can be based on a parameter as well, so /response?format=xml. Finally, if the request has an Accept header set to XML or JSON a response will be converted to the respective type. This constitutes a PPA strategy (Path, Parameter, Accept).
In other words, if you provide a proper converter implementations and configure them properly (some are available out of the box), you can get a single method that returns different representations, that you can control based on the PPA strategy.
Content Negotiation Using Spring MVC is a great post on Spring's Blog site with working examples.

Auto detection Jackson for Spring

I am working on a project that uses Spring framework and Jackson. However, I was not able to find a place, where it is being plugged. I looked at many examples on the web and most of them use bean of class org.springframework.http.converter.json.MappingJacksonHttpMessageConverter to allow deserialization of #ResponseBody.
So, I was not able to find any references to MappingJacksonHttpMessageConverter.
My question: Will spring framework automatically use Jackson if it will find it on its classpath to convert JSON into #ResponseBody object?
What are other ways how Jackson can be enabled?
If you wire up your spring project using #EnableWebMvc or via XML by using the tag <mvc:annotation-driven /> you enable a bunch of features. You can read the detailed list of features in the original Spring docs.
One of the features that are enabled is the support for #RequestBody method parameters and #ResponseBody method return values. This is done via the HttpMessageConverter component and the feature is enabled for methods that are annotated with #RequestMapping or #ExceptionHandler.
The following lists the converters that are registered by default:
ByteArrayHttpMessageConverter converts byte arrays.
StringHttpMessageConverter converts strings.
ResourceHttpMessageConverter converts to/from org.springframework.core.io.Resource for all media types.
SourceHttpMessageConverter converts to/from a javax.xml.transform.Source.
FormHttpMessageConverter converts form data to/from a MultiValueMap.
Jaxb2RootElementHttpMessageConverter converts Java objects to/from XML — added if JAXB2 is present on the classpath.
MappingJackson2HttpMessageConverter (or MappingJacksonHttpMessageConverter) converts to/from JSON — added if Jackson 2 (or Jackson) is present on the classpath.
AtomFeedHttpMessageConverter converts Atom feeds — added if Rome is present on the classpath.
RssChannelHttpMessageConverter converts RSS feeds — added if Rome is present on the classpath.
So, if you have a web enabled project with Jackson available on the classpath, Spring will automatically convert return values from a controller-method that is annotated with #ResponseBody (if the client caller accepts JSON that is which means that the accept header typically must be set to application/json).
If you wish to override the HttpMessageConverters you can implement the following:
#Configuration
#EnableWebMvc
public class YourConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
// Do your magic, override your stuff
}
}
For a good introduction on how to customize e.g. the Jackson converter you can read this article from DZone about Customizing HttpMessageConverters with Spring Boot and Spring MVC.

Passing an object from JSP to Spring controller

I created a class named Person. Then I pass an object of this class via Spring controller to JSP page, say abc.htm.
Now I want it to transfer back from abc.htm to another controller.
How could I do that?
Also tell me if any other class object (say Address class object) uses that person object as parameter, then how would I pass that Address class object to the controller.
I am very confused, please help me.
After the page is rendered you are no longer in the "Java realm", so you don't have your objects. You can rebuild them based on the parameters that are sent back in the next request.
This is called "binding". In Spring MVC this is done automatically (more or less) if you are using the <form:x> tags. Then in your controller your objects will be accessible as method attributes:
#RequestMapping(..)
public String foo(YourObject object) {..}
You might need a #ModelAttribute annotation if the name of your param and the one in the JSP are not the same. The MVC docs write:
Command or form objects to bind parameters to: as bean properties or fields, with customizable type conversion, depending on #InitBinder methods and/or the HandlerAdapter configuration. See the webBindingInitializer property on AnnotationMethodHandlerAdapter. Such command objects along with their validation results will be exposed as model attributes by default, using the non-qualified command class name in property notation. For example, "orderAddress" for type "mypackage.OrderAddress". Specify a parameter-level ModelAttribute annotation for declaring a specific model attribute name.
I'd suggest you review the PetClinic Sample Application to see how this works in practice.

Categories