Directory
- Objective
- Phenomenon
- SOURCE Analysis
- Handlermethodargumentresolver and Handlermethodreturnvaluehandler Interface Introduction
- Specific application of Handlermethodargumentresolver and Handlermethodreturnvaluehandler interface
- Common Handlermethodargumentresolver Introduction
- Common Handlermethodreturnvaluehandler Introduction
- At the beginning of this article explanation and solution
- Write your own custom handlermethodargumentresolver
- Summarize
- Resources
Objective
SPRINGMVC is currently one of the mainstream web MVC frameworks.
If a classmate is unfamiliar with it, then please refer to it's Getting started blog:http://www.cnblogs.com/fangjian0423/p/springmvc-introduction.html
The controller's method parameters in Springmvc can be integer,double, custom objects, Servletrequest,servletresponse,modelandview and so on, very flexible. This article will analyze how SPRINGMVC handles these parameters, allowing the reader to handle some of the custom parameters.
Phenomenon
The demo used in this article is based on Maven. Let's take a look at the corresponding phenomenon first.
@Controller@RequestMapping (value = "/test")PublicClassTestController {@RequestMapping ("/TESTRB")@ResponseBodyPublic EmployeeTESTRB ( @RequestBody Employee e) {return e;} @RequestMapping ("/testcustomobj") @ResponseBody public employee testcustomobj (employee e) { return e; } @RequestMapping ("/TESTCUSTOMOBJWITHRP") @ResponseBody Span class= "Hljs-keyword" >public Employee TESTCUSTOMOBJWITHRP ( @RequestParam Employee e) {return e;} @RequestMapping ("/testdate") @ResponseBody public date testdate (date date) {return date;}}
First this is a controller, there are 4 methods. Their corresponding parameters are custom objects with @requestbody, custom objects, custom objects with @requestparam, and date objects.
Next we visit one method to see how the corresponding phenomenon is.
First TESTRB First:
A second testcustomobj:
A third TESTCUSTOMOBJWITHRP:
Fourth TestDate:
Why the returned employee object will be automatically parsed into XML, please see the landlord of another blog: Poke Me
Why is the employee parameter parsed and the employee parameter with @requestparam not be parsed or even an error?
Why can't a date type be parsed?
What exactly does Springmvc do with the parameters of these methods?
What is the difference between the two annotations @RequestBody, @RequestParam?
With these few questions. Let's start with the analysis.
SOURCE Analysis
The source code analyzed in this article is the spring version 4.0.2
Before analyzing the source code, let's first look at two important interfaces in Springmvc.
The two interfaces correspond to the processing of the request method parameters, the response return value, respectively handlermethodargumentresolver and Handlermethodreturnvaluehandler, Both of these interfaces are joined after the Spring3.1 version.
The SPRINGMVC processing request is roughly the same:
First intercepted by Dispatcherservlet, dispatcherservlet through handlermapping get Handlerexecutionchain, and then get Handleradapter.
Handleradapter the internal for each request, will instantiate a servletinvocablehandlermethod to handle,servletinvocablehandlermethod when processing, The request and response will be processed in two separate sections .
After Handleradapter get Modelandview, then do the corresponding treatment.
This article will focus on Servletinvocablehandlermethod's handling of requests and responses.
1. When processing a request, it is processed according to the Servletinvocablehandlermethod attribute argumentresolvers (which is defined in its parent class Invocablehandlermethod). Where the Argumentresolvers attribute is a Handlermethodargumentresolvercomposite class (a variant of the combined pattern is used here), This class is a class that implements the Handlermethodargumentresolver interface, which has a list of various handlermethodargumentresolver implementations.
2. When the response is processed, it is processed according to the Servletinvocablehandlermethod attribute Returnvaluehandlers (its own property). The Returnvaluehandlers property is a Handlermethodreturnvaluehandlercomposite class (a variant of the combined pattern is used here), This class is a class that implements the Handlermethodreturnvaluehandler interface, which has a list of various Handlermethodreturnvaluehandler implementations.
The Returnvaluehandlers and Argumentresolvers properties of Servletinvocablehandlermethod are both assigned when the Servletinvocablehandlermethod is instantiated. (Use the Requestmappinghandleradapter property to assign a value).
The Argumentresolvers and Returnvaluehandlers properties of Requestmappinghandleradapter are injected by the spring container when the requestmappinghandleradapter is instantiated. 。
Where the default argumentresolvers:
The default returnvaluehandlers:
We have learned in the JSON, XML Auto-transformation article that With @responsebody annotations, the final return value is handled by the Requestresponsebodymethodprocessor Handlermethodreturnvaluehandler implementation class.
We discovered through the source, Requestresponsebodymethodprocessor This class actually implements both the Handlermethodreturnvaluehandler and the Handlermethodargumentresolver interfaces.
Requestresponsebodymethodprocessor supported request types are controller method parameters with @requestbody annotations, The supported response type is the Controller method with @responsebody annotations.
The specific handling of the requestresponsebodymethodprocessor response is to use a message converter.
Use the internal Readwithmessageconverters method when processing the request.
The Readwithmessageconverters method of the parent class (Abstractmessageconvertermethodargumentresolver) is then executed.
Let's take a look at the commonly used Handlermethodargumentresolver implementation classes (in this paper, interested readers can do their own research).
1. Requestparammethodargumentresolver
Support for parameters with @requestparam annotations or parameters with Multipartfile type
2. Requestparammapmethodargumentresolver
Support for parameters with @requestparam annotations && @RequestParam Annotation Properties value existence && parameter type is the attribute that implements the map interface
3. Pathvariablemethodargumentresolver
Supports parameters with @pathvariable annotations and if the parameter implements the map interface, the @PathVariable annotation takes the Value property
4. Matrixvariablemethodargumentresolver
Supports parameters with @matrixvariable annotations and if the parameter implements the map interface, the @MatrixVariable annotation takes the Value property
5. Requestresponsebodymethodprocessor
This article has analyzed
6. Servletrequestmethodargumentresolver
parameter types are implementation or inheritance or WebRequest, ServletRequest, Multipartrequest, HttpSession, Principal, Locale, TimeZone, InputStream, Reader, HttpMethod these classes.
(This is why we add a httpservletrequest parameter in the controller's method, and spring will automatically get the reason for the HttpServletRequest object)
7. Servletresponsemethodargumentresolver
Parameter types are implementations or inheritance, or Servletresponse, OutputStream, writer, and the like
8. Redirectattributesmethodargumentresolver
parameter is a class that implements the Redirectattributes interface
9. Httpentitymethodprocessor
The parameter type is httpentity
From the name we also see that the end of the resolver is the implementation of the Handlermethodargumentresolver interface class, Ending with processor is the class that implements the Handlermethodargumentresolver and Handlermethodreturnvaluehandler.
Let's take a look at the common Handlermethodreturnvaluehandler implementation class.
1. Modelandviewmethodreturnvaluehandler
The return value type is Modelandview or its subclasses
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 subclasses
5. Modelattributemethodprocessor
return value has @modelattribute annotations
6. Viewnamemethodreturnvaluehandler
The return value is void or string
Other readers who have not spoken can check the source code themselves.
Here's why these phenomena begin to appear at the beginning of this article:
1. First method TESTRB and address http://localhost:8888/SpringMVCDemo/test/testRb?name=1&age=3
The parameters of this method use @requestbody, previously analyzed, and processed by Requestresponsebodymethodprocessor. The contenttype then selects the appropriate message converter to read according to the HTTP request header.
Obviously, our message converters have only the default ones that follow the JSON as well as the XML converters, and the passed parameters are name=1&age=3, there is no content-type in the passed header, and Application/octet-stream is used by default. Thus triggering the httpmediatypenotsupportedexception exception
Emancipation Program: We will transfer the data to JSON, while the HTTP request Content-type to Application/json can be changed.
Perfect solution.
2. Testcustomobj method and Address http://localhost:8888/SpringMVCDemo/test/testCustomObj?name=1&age=3
This request will find servletmodelattributemethodprocessor this resolver. The default resolver has two servletmodelattributemethodprocessor, except that when instantiated, the property annotationnotrequired one is true,1 false. This servletmodelattributemethodprocessor processing parameter supports @modelattribute annotations, and the Annotationnotrequired property is true if the argument is not a simple type, Therefore, Servletmodelattributemethodprocessor is selected, and the Employee object is finally instantiated by DataBinder and the corresponding property is written.
3. Testcustomobjwithrp method and Address http://localhost:8888/SpringMVCDemo/test/testCustomObjWithRp?name=1&age=3
This request will find Requestparammethodargumentresolver (using the @requestparam annotation). Requestparammethodargumentresolver is obtained using Request.getparameter (parameter name), or Request.getparameter ("E") when processing parameters, It's obvious that our parameters are name=1&age=3. So getting null,requestparammethodargumentresolver processing missing value will trigger the missingservletrequestparameterexception exception. [Roughly speaking, interested readers please self-view source code]
Solution: Remove @requestparam annotations and let Servletmodelattributemethodprocessor handle them.
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 is support for non-simple types. The final step is the same as the third method, our parameter name is date, and the date string is found by Request.getparameter ("date") (where the parameter name is not date, then the final page is blank, Because there is no @requestparam annotation, the parameter is not required, and requestparammethodargumentresolver processing null value returns NULL). Finally, the appropriate property editor is found by DataBinder for type conversion. Finally, the constructor for the Java.util.Date object, public Date (String s), was found because the format we passed was not in the standard UTC time format, so the illegalargumentexception exception was eventually triggered.
Solution:
1. Format of passed parameters modified to standard UTC time Format: Http://localhost:8888/SpringMVCDemo/test/testDate?date=Sat, 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 the webdatabinderfactory when the Servletinvocablehandlermethod is instantiated, And Webdatabinderfactory is a property of Servletinvocablehandlermethod. In the requestmappinghandleradapter source of 803 lines getdatabinderfactory is to get webdatabinderfactory.
Requestparammethodargumentresolver then finds the appropriate property editor by using the custom property Editor in the Webdatabinder created by Webdatabinderfactory ( Our custom property editor uses Customdateeditor to process the date object, and the TestDate parameter is exactly date, and eventually customdateeditor converts the string object to a Date object.
Write your own custom handlermethodargumentresolver
From the previous analysis, we understand the SPRINGMVC process of handling the methods in the controller.
Now, if there are two parameters in the method and are all custom class parameters, what should be handled?
Obviously, to deal with this can only implement a self-implementation of the Handlermethodargumentresolver class.
Define 1 annotation formobj First:
@Target({ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)public @interface FormObj { //参数别名 String value() default ""; //是否展示, 默认展示 boolean show() default true;}
Then the Handlermethodargumentresolver:
PublicClassFormobjargumentresolverImplements Handlermethodargumentresolver {@OverridePublicBooleanSupportsparameter (methodparameter parameter) {Return Parameter.hasparameterannotation (formobj.Class); }@OverridePublic ObjectResolveargument (methodparameter parameter, Modelandviewcontainer mavcontainer, Nativewebrequest webRequest, Webdatabinderfactory binderfactory)Throws Exception {formobj formobj = parameter.getparameterannotation (formobj.Class); String alias = Getalias (formobj, parameter);Get obj, take it from Modelandviewcontainer first, if not new1 an instance of parameter type Object obj = (Mavcontainer.containsattribute (alias))? Mavcontainer.getmodel (). Get (alias): CreateAttribute (alias, parameter, Binderfactory, webRequest);Get Webdatabinder, here the concrete webdatabinder is extendedservletrequestdatabinder webdatabinder binder = Binderfactory.createbinder (webRequest, obj, alias); Object target = Binder.gettarget ();if (Target! =NULL) {Binding Parameters BindParameters (webRequest, Binder, alias);JSR303 Verification validateifapplicable (binder, parameter);if (Binder.getbindingresult (). HasErrors ()) {if (isbindexceptionrequired (binder, parameter)) {ThrowNew Bindexception (Binder.getbindingresult ()); } } }if (Formobj.show ()) {Mavcontainer.addattribute (alias, target);}return target; }Private ObjectCreateAttribute (String alias, methodparameter parameter, webdatabinderfactory binderfactory, Nativewebrequest WebRequest) {Return Beanutils.instantiateclass (Parameter.getparametertype ()); }private voidBindParameters (nativewebrequest request, Webdatabinder Binder, String alias) {ServletRequest ServletRequest = Request.getnativerequest (ServletRequest.Class); Mockhttpservletrequest newrequest =New Mockhttpservletrequest (); Enumeration<string> enu = servletrequest.getparameternames ();while (Enu.hasmoreelements ()) {String paramname = enu.nextelement ();if (Paramname.startswith (alias)) {Newrequest.setparameter (paramname.substring (Alias.length () +1), Request.getparameter (paramname)); }} ((Extendedservletrequestdatabinder) binder). bind (Newrequest); }protected voidValidateifapplicable (Webdatabinder binder, methodparameter parameter) {annotation[] annotations = Parameter.getparameterannotations ();for (Annotation annot:annotations) {if (Annot.annotationtype (). Getsimplename (). StartsWith ("Valid")) {Object hints = annotationutils.getvalue (annot); Binder.validate (Hintsinstanceof object[]? (object[]) hints:New object[] {hints});Break } } }ProtectedBooleanIsbindexceptionrequired (Webdatabinder binder, methodparameter parameter) {int i = Parameter.getparameterindex (); class<?>[] Paramtypes = Parameter.getmethod (). Getparametertypes (); boolean Hasbindingresult = (Paramtypes.length > (i + 1) && Errors.class.isassignablefrom (paramtypes[i + 1])); return!hasbindingresult;} private String getalias (formobj formobj, MethodParameter Parameter) {//gets the Formobj attribute value, which is the short name of the object argument String alias = Formobj.value (); if (alias = = null | | Stringutils.isblank (alias)) {//if the abbreviation is empty, take the first letter of the object abbreviation lowercase start String simplename = Parameter.getparametertype (). Getsimplename (); Alias = simplename.substring (0, 1). toLowerCase () + simplename.substring (1); } return alias;}
Corresponding Controller:
@Controller@RequestMapping (value = "/FOC")public class Formobjcontroller { @RequestMapping ("/test1") public String test1 (@FormObj Dept Dept, @FormObj Employee EMP) { return "index";} @RequestMapping ("/test2") public String test2 (@FormObj ("D") Dept Dept, @FormObj ("E") Employee emp) { Return "index"; } @RequestMapping ("/test3") public String test3 (@FormObj (value = "D", show = False) Dept Dept, @FormObj ("E" Employee EMP) { return "index";}}
The results are as follows:
Summarize
Write so much, mainly to consolidate their own SPRINGMVC on the request and response to a detailed summary of the process, I do not know if you have clear the procedure.
The main thing to be familiar with this part is to familiarize yourself with the Handlermethodargumentresolver and Handlermethodreturnvaluehandler interfaces as well as the property editor and data binding mechanism.
This article inevitably has the mistake, hoped that the reader can point out.
Resources
http://www.iteye.com/topic/1127676
http://jinnianshilongnian.iteye.com/blog/1717180
[Http://www.tuicool.com/articles/F7byQn
Http://www.2cto.com/kf/201405/301660.html]
Detailed description of the controller in the Springmvc method of the parameters of the working principle [with the source analysis] Good