Directory
- Preface
- Symptom
- Source code analysis
- HandlerMethodArgumentResolver and HandlerMethodReturnValueHandler
- HandlerMethodArgumentResolver and HandlerMethodReturnValueHandler
- Introduction to HandlerMethodArgumentResolver
- Introduction to HandlerMethodReturnValueHandler
- Symptoms and solutions at the beginning of this article
- Write custom HandlerMethodArgumentResolver
- Summary
- References
Preface
SpringMVC is one of the mainstream Web MVC frameworks.
If you are not familiar with it, please refer to its entry blog: http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html
The Controller method parameters in SpringMVC can be Integer, Double, custom object, ServletRequest, ServletResponse, ModelAndView, and so on. This article will analyze how SpringMVC processes these parameters so that readers can process custom parameters.
Symptom
The demo used in this article is based on maven. Let's take a look at the corresponding phenomenon.
@Controller@RequestMapping(value = "/test")public class TestController { @RequestMapping("/testRb") @ResponseBody public Employee testRb(@RequestBody Employee e) { return e; } @RequestMapping("/testCustomObj") @ResponseBody public Employee testCustomObj(Employee e) { return e; } @RequestMapping("/testCustomObjWithRp") @ResponseBody public Employee testCustomObjWithRp(@RequestParam Employee e) { return e; } @RequestMapping("/testDate") @ResponseBody public Date testDate(Date date) { return date; } }
First, this is a Controller with four methods. Their corresponding parameters are custom objects with @ RequestBody, custom objects with @ RequestParam, and date objects.
Next, we will perform access one by one to see what the corresponding phenomenon is.
First, the first testRb:
The second testCustomObj:
The third testCustomObjWithRp:
Fourth testDate:
Why is the returned Employee object automatically parsed as xml? Please refer to another blog of the author: Stamp me
Why is the Employee parameter parsed, and the Employee parameter with @ RequestParam not parsed or even reported an error?
Why cannot the date type be parsed?
How does SpringMVC handle the parameters of these methods?
@ RequestBody and @ RequestParam are two annotations different?
With these questions. Let's start the analysis.
Source code analysis
The source code analyzed in this article is Spring 4.0.2.
Before analyzing the source code, let's take a look at two important interfaces in SpringMVC.
The two interfaces correspond to the processing of request method parameters and the processing of response return values respectively.HandlerMethodArgumentResolverAndHandlerMethodReturnValueHandlerThe two interfaces are added after Spring3.1.
SpringMVC processes requests like this:
DispatcherServlet intercepts HandlerExecutionChain first, and DispatcherServlet obtains HandlerExecutionChain through handlerMapping, and then obtains HandlerAdapter.
The HandlerAdapter instantiates a ServletInvocableHandlerMethod for processing each request internally,The ServletInvocableHandlerMethod processes the request and response in two parts..
Then HandlerAdapter gets ModelAndView and then processes it accordingly.
This article focuses on how ServletInvocableHandlerMethod processes requests and responses.
1. when processing a request, it will be processed according to the attribute argumentResolvers (defined in its parent class InvocableHandlerMethod) of ServletInvocableHandlerMethod, the argumentResolvers attribute is an aggregate class (a type of deformation in the combination mode is used here). This class implements the HandlerMethodArgumentResolver interface class, which contains various List sets that implement HandlerMethodArgumentResolver.
2. When processing the response, it will be processed according to the returnValueHandlers attribute of ServletInvocableHandlerMethod (its own attribute). The returnValueHandlers attribute is a HandlerMethodReturnValueHandlerComposite class(A deformation of the combination mode is used here)This class implements the HandlerMethodReturnValueHandler interface class, which has various List sets that implement HandlerMethodReturnValueHandler.
The returnValueHandlers and argumentResolvers attributes of ServletInvocableHandlerMethod are assigned values when ServletInvocableHandlerMethod is instantiated (the RequestMappingHandlerAdapter attribute is used to assign values ).
The argumentResolvers and returnValueHandlers attributes of RequestMappingHandlerAdapter are injected by the Spring container when RequestMappingHandlerAdapter is instantiated.
The default ArgumentResolvers is as follows:
Default returnValueHandlers:
We have already learned in the article on automatic conversion of json and xml. If the @ ResponseBody annotation is used, the final return value will be processed by the HandlerMethodReturnValueHandler class of RequestResponseBodyMethodProcessor.
We found through the source code that the RequestResponseBodyMethodProcessor class actually implements the HandlerMethodReturnValueHandler and HandlerMethodArgumentResolver interfaces at the same time.
The request type supported by RequestResponseBodyMethodProcessor is the @ RequestBody annotation in the Controller method parameter. The supported response type is the Controller method with the @ ResponseBody annotation.
The message converter is used to process the response of RequestResponseBodyMethodProcessor.
Use the internal readWithMessageConverters method to process requests.
Then the readWithMessageConverters method of the parent class (AbstractMessageConverterMethodArgumentResolver) is executed.
Next, let's take a look at the commonly used HandlerMethodArgumentResolver implementation class (in this article, readers who are interested can study it on their own ).
1. RequestParamMethodArgumentResolver
Support Parameters with @ RequestParam annotation or parameters with MultipartFile type
2. RequestParamMapMethodArgumentResolver
Support attribute values with @ RequestParam annotation & @ RequestParam annotation exist & the parameter type is the attribute that implements the Map interface
3. PathVariableMethodArgumentResolver
Supports parameters with the @ PathVariable annotation. If the parameter implements the Map interface, the @ PathVariable annotation must have the value attribute.
4. MatrixVariableMethodArgumentResolver
Supports parameters with the @ MatrixVariable annotation. If the parameter implements the Map interface, the @ MatrixVariable annotation must have the value attribute.
5. RequestResponseBodyMethodProcessor
Analyzed in this article
6. ServletRequestMethodArgumentResolver
Parameter types are implemented or inherited or WebRequest, ServletRequest, MultipartRequest, HttpSession, Principal, Locale, TimeZone, InputStream, Reader, and HttpMethod.
(This is why we add an HttpServletRequest parameter to the Controller method, and Spring will automatically obtain the HttpServletRequest object for us)
7. ServletResponseMethodArgumentResolver
Parameter types are implemented or inherited or ServletResponse, OutputStream, and Writer classes.
8. RedirectAttributesMethodArgumentResolver
The parameter is a class that implements the RedirectAttributes interface.
9. HttpEntityMethodProcessor
The parameter type is HttpEntity.
We can also see from the name that the class ending with Resolver implements the HandlerMethodArgumentResolver interface, and the class ending with Processor implements the HandlerMethodArgumentResolver and HandlerMethodReturnValueHandler classes.
Next let's take a look at the commonly used HandlerMethodReturnValueHandler implementation class.
1. ModelAndViewMethodReturnValueHandler
The return value type is ModelAndView or its subclass.
2. ModelMethodProcessor
The return value type is Model or its subclass.
3. ViewMethodReturnValueHandler
The return value type is View or its subclass.
4. HttpHeadersReturnValueHandler
The return value type is HttpHeaders or its subclass.
5. ModelAttributeMethodProcessor
The returned values are annotated with @ ModelAttribute.
6. ViewNameMethodReturnValueHandler
The return value is void or String.
You can view the source code by yourself.
The following explains why these phenomena occur at the beginning of this article:
1. The first method is testRb and the address is http: // localhost: 8888/SpringMVCDemo/test/testRb? Name = 1 & age = 3
The parameter of this method uses @ RequestBody, which has been analyzed before and is processed by RequestResponseBodyMethodProcessor. Then, based on the contentType in the http Request Header, select the appropriate message converter for reading.
Obviously, our message converter only has the default json and xml converters, and the passed parameter name = 1 & age = 3. The transmitted header does not contain content-type, application/octet-stream is used by default, so an exception occurs in HttpMediaTypeNotSupportedException.
Liberation solution: Change the transmitted data to json, and change the Content-Type of the http request to application/json.
Perfect solution.
2. testCustomObj method and address http: // localhost: 8888/SpringMVCDemo/test/testCustomObj? Name = 1 & age = 3
This request will find the resolver ServletModelAttributeMethodProcessor. The default resolver contains two ServletModelAttributeMethodProcessor, but the annotationNotRequired attribute is true and the default resolver is false. This ServletModelAttributeMethodProcessor processing parameter supports @ ModelAttribute annotation. If the annotationNotRequired attribute is true, the parameter is not a simple type. Therefore, ServletModelAttributeMethodProcessor is selected, and the Employee object is instantiated through DataBinder and the corresponding attribute.
3. The testCustomObjWithRp method and the address http: // localhost: 8888/SpringMVCDemo/test/testCustomObjWithRp? Name = 1 & age = 3
This request will find RequestParamMethodArgumentResolver (the @ RequestParam annotation is used ). RequestParamMethodArgumentResolver uses request when processing parameters. getParameter (parameter name) is request. getParameter ("e") is obtained. It is obvious that our parameter is passed by name = 1 & age = 3. Therefore, if null is obtained, RequestParamMethodArgumentResolver will trigger the MissingServletRequestParameterException when processing the missing value. [For more information, see the source code.]
Solution: remove the @ RequestParam annotation and let ServletModelAttributeMethodProcessor process the annotation.
4. testDate method and address http: // localhost: 8888/SpringMVCDemo/test/testDate? Date = 2014-05-15
This request will find RequestParamMethodArgumentResolver. Because this method is the same as the second method, there are two RequestParamMethodArgumentResolver, and the attribute useDefaultResolution is different. RequestParamMethodArgumentResolver supports simple types. ServletModelAttributeMethodProcessor supports non-simple types. The final step is the same as the third method. Our parameter name is date, so the request is passed. getParameter ("date") finds the date string (if the parameter name is not date, the final page is blank, because there is no @ RequestParam annotation, and the parameter is not required, requestParamMethodArgumentResolver returns null when processing null values ). Finally, use DataBinder to find an appropriate Attribute Editor for type conversion. Finally, the constructor public Date (String s) of the java. util. Date object is found. Because the format we passed is not the standard UTC time format, the IllegalArgumentException exception is triggered.
Solution:
1. Change the format of the passed parameter to the standard UTC time format: http: // localhost: 8888/SpringMVCDemo/test/testDate? Date = Sat, 17 May 2014 16:30:00 GMT
2. Add the custom attribute editor to the Controller.
@InitBinderpublic void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));}
This @ InitBinder annotation is injected into WebDataBinderFactory when ServletInvocableHandlerMethod is instantiated, while WebDataBinderFactory is an attribute of ServletInvocableHandlerMethod. In the first line of the RequestMappingHandlerAdapter source code, getDataBinderFactory is the obtained WebDataBinderFactory.
Then, RequestParamMethodArgumentResolver uses the custom attribute editor in the WebDataBinderFactory created in WebDataBinder to find the appropriate Attribute Editor (our custom attribute editor uses CustomDateEditor to process Date objects, the testDate parameter is exactly Date), and CustomDateEditor converts the String object to the Date object.
Write custom HandlerMethodArgumentResolver
Through the above analysis, we understand the parameter process of SpringMVC's method in Controller processing.
Now, if there are two parameters in the method and all are custom class parameters, what should we do?
@RequestMapping("/save")public ModelAndView saveAll(@FormModel Employee employee, @FormModel Dept dept, ModelAndView view) {
view.setViewName("test/success"); view.addObject("employee", employee);
view.addObject("dept", dept); return view;}
Let's try it.
Obviously, you can implement only one class to implement HandlerMethodArgumentResolver.
The author has referenced the implementation in this blog. The implementation details will be detailed in the subsequent blog.
The result is as follows:
Summary
After writing so much, I want to consolidate my summary of SpringMVC's handling of requests and responses. I don't know whether this process is clear.
To get familiar with this part of content, you should be familiar with HandlerMethodArgumentResolver and HandlerMethodReturnValueHandler interfaces, as well as the Attribute Editor and data binding mechanism.
This article will inevitably cause errors. I hope readers can point it out.
References
Http://www.iteye.com/topic/1127676
Http://jinnianshilongnian.iteye.com/blog/1717180