Springmvc Source parameter parsing binding principle

Source: Internet
Author: User
Tags reflection

Summary
    • This paper simply explains the principle of SPRINGMVC parameter binding from the source level.
SPRINGMVC initialization of dependent components for parameter binding
    • Before understanding initialization, first recognize an interface
Handlermethodargumentresolver
    • Method parameter resolver interface, which is the core interface of the SPRINGMVC parameter resolution binding. Different parameter type bindings are implemented by implementing this interface. You can also customize the parameter parser by implementing this interface. This interface has the following two methods
public interface HandlerMethodArgumentResolver {    //该解析器是否支持parameter参数的解析    boolean supportsParameter(MethodParameter parameter);    //将方法参数从给定请求(webRequest)解析为参数值并返回    Object resolveArgument(MethodParameter parameter,                          ModelAndViewContainer mavContainer,                          NativeWebRequest webRequest,                          WebDataBinderFactory binderFactory) throws Exception;}
Initialization
    • The Afterpropertiesset (line:481) method of the Requestmappinghandleradapter.java class initializes the correlation method parameter parser. The code is as follows
public void Afterpropertiesset () {if (this.argumentresolvers = = null) {//Initialize SPRINGMVC default method parameter parser and add to Argumentre Solvers (handlermethodargumentresolvercomposite) list
    • Now we go to the Getdefalutargumentresolvers method, the code is as follows
Default parameter resolution, create default 24 parameter parser, and add to resolvers//the 24 parameter resolvers here are private list
    • The parameter parser is added to the Handlermethodargumentresolvercomposite class, which also implements the Handlermethodargumentresolver interface. In this case, the composite pattern in the schema (combined mode) is used, and all the parameters of the request are parsed into the Handlermethodargumentresolvercomposite class in Springmvc. It has two member variables, as follows
//它的元素在RequestMappingHandlerAdapter类的afterPropertiesSet方法中被添加,存放的是SpringMVC一些默认的HandlerMethodArgumentResolver参数解析器private final List<HandlerMethodArgumentResolver> argumentResolvers =            new LinkedList<HandlerMethodArgumentResolver>();//存放已经解析过的参数,已经对应的HandlerMethodArgumentResolver解析器。加快查找过程private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =        new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>(256);
    • The introduction of so much, words not much to say. Look directly at a detailed parsing binding process.
Binding process
    • Let's look at a simple parameter binding, with the following controller and request, the code is as follows.
@Controller@RequestMapping("/ParameterBind")public class ParameterBindTestController {    @ResponseBody    @RequestMapping("/test1")    public String test1(int id){        System.out.println(id);        return "test1";    }}

    • After requesting entry into Dispatcherservlet's Dodispatch, obtain the Handlermethod. Then, according to the Handlermethod to confirm the Handlerapapter, confirm and execute Handleradapter handle method. This confirms that Handlerapater is requestmappinghandleradapter and needs to handle the binding of the parameters before executing the handlermethod. Then look at the detailed parameter binding process
    • After executing the handler method of Handleradapter, enter the Invokehandlemethod method of Requestmappinghandleradapter (line:711)
Private Modelandview Invokehandlemethod (httpservletrequest request, httpservletresponse response, Handlermethod    Handlermethod) throws Exception {servletwebrequest webRequest = new Servletwebrequest (request, response);    Webdatabinderfactory binderfactory = getdatabinderfactory (Handlermethod);    Modelfactory modelfactory = Getmodelfactory (Handlermethod, binderfactory); Create a Servletinvocablehandlermethod based on Handlermethod and Binderfactory.    Follow the request directly to Servletinvocablehandlermethod execution. The Createrequestmappingmethod method is relatively simple,    Add Argumentresolvers and returnvaluehandlers that were previously requestmappinghandleradapter initialized to Servletinvocablehandlermethod    Servletinvocablehandlermethod Requestmappingmethod = Createrequestmappingmethod (HandlerMethod, binderFactory);    Modelandviewcontainer Mavcontainer = new Modelandviewcontainer ();    Mavcontainer.addallattributes (Requestcontextutils.getinputflashmap (request));    Modelfactory.initmodel (WebRequest, Mavcontainer, Requestmappingmethod); Mavcontainer.setIgnoredefaultmodelonredirect (This.ignoredefaultmodelonredirect);    Asyncwebrequest asyncwebrequest = webasyncutils.createasyncwebrequest (request, response);    Asyncwebrequest.settimeout (this.asyncrequesttimeout);    Final Webasyncmanager Asyncmanager = Webasyncutils.getasyncmanager (request);    Asyncmanager.settaskexecutor (This.taskexecutor);    Asyncmanager.setasyncwebrequest (asyncwebrequest);    Asyncmanager.registercallableinterceptors (this.callableinterceptors);    Asyncmanager.registerdeferredresultinterceptors (this.deferredresultinterceptors);        if (Asyncmanager.hasconcurrentresult ()) {Object result = Asyncmanager.getconcurrentresult ();        Mavcontainer = (Modelandviewcontainer) asyncmanager.getconcurrentresultcontext () [0];        Asyncmanager.clearconcurrentresult ();        if (logger.isdebugenabled ()) {Logger.debug ("Found Concurrent result value [" + Result + "]"); } Requestmappingmethod = Requestmappingmethod.wrapconcurrentresult (Result);    } requestmappingmethod.invokeandhandle (WebRequest, Mavcontainer);    if (asyncmanager.isconcurrenthandlingstarted ()) {return null; } return Getmodelandview (Mavcontainer, Modelfactory, webRequest);}
    • then enter the Invokeandhanldle method and enter the Invokeforrequest method, which is responsible for resolving the parameters required by the Handlermethod method from the request. The method in Handlermethod is then called through reflection. The code is as follows
  Public Final Object invokeforrequest (nativewebrequest request, MODELANDV Iewcontainer Mavcontainer, Object ... providedargs) throws Exception {//from Req The parameters required by the Handlermethod method are parsed in uest and returned object[] object[] args = getmethodargumentvalues (Request, Mavcontainer, Providedar        GS);            if (logger.istraceenabled ()) {StringBuilder builder = new StringBuilder ("invoking [");            Builder.append (This.getmethod (). GetName ()). Append ("] method with arguments");            Builder.append (Arrays.aslist (args));        Logger.trace (Builder.tostring ()); }//The method in Handlemethod is executed by reflection, and the parameter is args.        and returns the return value of the method execution Object returnvalue = Invoke (args); if (logger.istraceenabled ()) {logger.trace ("Method [" + This.getmethod (). GetName () + "] returned [" + Returnva        Lue + "]");    } return returnvalue; }
    • go directly to the Getmethodargumentvalues method to see its procedure, the code is as follows
/*** Gets the method parameter value for the current request. */private object[] Getmethodargumentvalues (nativewebrequest request, Modelandviewcontainer MavContainer, Ob    Ject Providedargs) throws Exception {//Get method parameter array methodparameter[] Parameters = Getmethodparameters ();    Create a parameter array, save the method parameter parsed from request object[] args = new Object[parameters.length];        for (int i = 0; i < parameters.length; i++) {Methodparameter parameter = Parameters[i];        Parameter.initparameternamediscovery (Parameternamediscoverer);        Generictyperesolver.resolveparametertype (parameter, Getbean (). GetClass ());        Args[i] = resolveprovidedargument (parameter, Providedargs);        if (args[i]! = null) {continue; }//To determine the 24 handlermethodargumentresolver (parameter parser) before Requestmappinghandleradapter initialization, if there are parsers that support this parameter resolution if (Argumen Tresolvers.supportsparameter (parameter)) {try {args[i] = argumentresolvers.resolveargument (par Ameter, Mavcontainer, request, DatabinderfActory);            Continue } catch (Exception ex) {if (logger.istraceenabled ()) {Logger.trace (Getargumentresoluti                Onerrormessage ("Error resolving Argument", I), ex);            } throw ex; }} if (args[i] = = null) {String msg = getargumentresolutionerrormessage ("No suitable resolver F            Or argument ", I);        throw new IllegalStateException (msg); }} return args;
    • Resolveargument method for entering Handlermethodargumentresolvercomposite
public Object resolveArgument(            MethodParameter parameter, ModelAndViewContainer mavContainer,            NativeWebRequest webRequest, WebDataBinderFactory binderFactory)            throws Exception {        //首先获取参数解析器,这里获取的逻辑是首先从argumentResolverCache缓存中获取该MethodParameter匹配的HandlerMethodArgumentResolver。如果为空,遍历初始化定义的那24个。查找匹配的HandlerMethodArgumentResolver,然后添加至argumentResolverCache缓存中        HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);        Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");        //解析参数        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);    }
    • Then enter the Resolverargument method of Handlermethodargumentresolver
Public final Object resolveargument (methodparameter parameter, Modelandviewcontainer Mavcontainer,        Nativewebrequest webRequest, Webdatabinderfactory binderfactory) throws Exception {//Get the class object of int        class<?> Paramtype = Parameter.getparametertype ();        Creates a Namedvalueinfo object based on the parameter definition namedvalueinfo namedvalueinfo = getnamedvalueinfo (parameter);        Resolves the value of an object based on the name of the parameter object arg = ResolveName (namedvalueinfo.name, parameter, webRequest); if (arg = = null) {if (namedvalueinfo.defaultvalue! = null) {arg = Resolvedefaultvalue (namedval            Ueinfo.defaultvalue);            } else if (namedvalueinfo.required) {handlemissingvalue (namedvalueinfo.name, parameter);        } arg = Handlenullvalue (Namedvalueinfo.name, ARG, paramtype); } else if ("". Equals (ARG) && (namedvalueinfo.defaultvalue! = null)) {arg = Resolvedefaultvalue ( NAmedvalueinfo.defaultvalue);  }//The above step gets the args that is the string type, and then converts the type required by the method parameter (int) if (binderfactory! = null) {Webdatabinder Binder =            Binderfactory.createbinder (webRequest, NULL, namedvalueinfo.name);        arg = Binder.convertifnecessary (ARG, paramtype, parameter);        } handleresolvedvalue (ARG, namedvalueinfo.name, parameter, Mavcontainer, webRequest);    return arg; }
    • The job of this method is to get paramtype first. The class object that corresponds to int. Then create a Namedvalueinfo object based on the Parameter object. This object holds the parameter name, whether it must be, the default value of the parameter 3 member variables. Then enter the Resolvername method parsing parameters, the logic is actually very simple, is based on the parameter name of the method to obtain the parameters in the request. The key code is as follows
String[] paramValues = webRequest.getParameterValues(name);if (paramValues != null) {    arg = paramValues.length == 1 ? paramValues[0] : paramValues;}

So the value returned here is 9999, and the value returned here is the string type. The required parameters are of type int. The string is then converted to the int type by the Binder.coverifnecessary method.

Object binding
    • Add a new method, the code is as follows

      @ResponseBody@RequestMapping("/test2")public String test2(User u){System.out.println(u.toString());return "test1";}

    • The parameter resolution bindings for this request will be given to the Servletmodelattributemethodprocessor class when initializing Argumentresolvers. is to create two different servletmodelattributemethodprocessor objects.

resolvers.add(new ServletModelAttributeMethodProcessor(false));resolvers.add(new ServletModelAttributeMethodProcessor(true));
    • What's the difference between these two? Enter the Supportsparameter method to see
/***带有@ModelAttribute注解返回true* parameter不是简单类型也返回true.*/public boolean supportsParameter(MethodParameter parameter) {        if (parameter.hasParameterAnnotation(ModelAttribute.class)) {            return true;        }        else if (this.annotationNotRequired) {            return !BeanUtils.isSimpleProperty(parameter.getParameterType());        }        else {            return false;        }    }
    • Although the test2 (User u) method does not have modelattribute annotations, User.class is not a simple type. So the Methodhandler parameter u will still be handed to servletmodelatttributemethodprocessor for processing.
    • Look at the Resolveargument method of Servletmodelattributemethodprocessor. Its resolveargument is implemented specifically by the parent class Modelattributemethodprocessor, as shown in the code below.
/*** parses a parameter in the model and, if not found from Modelandviewcontainer, instantiates an object directly from reflection. The specific instantiation is instantiated by calling the Beanutils.instantiateclass method through the CreateAttribute method of the parent class. This object is the object that is subsequently passed to the Test2 (User u) method, but the values in the created object are still empty, and the injected value is implemented by the Bindrequestparameters method.            */public final Object resolveargument (methodparameter parameter, Modelandviewcontainer Mavcontainer, Nativewebrequest request, Webdatabinderfactory binderfactory) throws Exception {String name = Modelfa        Ctory.getnameforparameter (parameter);                Object attribute = (Mavcontainer.containsattribute (name))?        Mavcontainer.getmodel (). Get (name): CreateAttribute (name, parameter, binderfactory, request);        Webdatabinder Binder = Binderfactory.createbinder (request, attribute, name);            if (binder.gettarget () = null) {//binds the request to the target object of the destination binder, which is the attribute object that was just created.            Bindrequestparameters (binder, request);            If there is validation, the Validation parameter validateifapplicable (binder, parameter); if (binder.geTbindingresult (). HasErrors ()) {if (isbindexceptionrequired (binder, parameter)) {throw                New Bindexception (Binder.getbindingresult ()); }}}//Add resolved attribute and Bindingresult at the end of the model map<string, O        bject> Bindingresultmodel = Binder.getbindingresult (). Getmodel ();        Mavcontainer.removeattributes (Bindingresultmodel);        Mavcontainer.addallattributes (Bindingresultmodel);    return Binder.gettarget (); }
    • The function of this method is to instantiate an ParameterType object, and then create a Webdatabinder object, based on request and attribute, name. It then enters the Bindrequestparameters method binding, creating a Mutablepropertyvalues object based on the parameters in the Reqeust. The mutablepropertyvalues contains one or more propertyvalue, where propertyvalue is used to hold information about the properties of a single bean, such as parameter names and parameter values. It is important to note that PropertyValue does not hold all parameter attribute information for the request object. Instead, a parameter property corresponds to a propertyvalue. For example, the Reqeust object here, with two parameters, name and age, will create two PropertyValue objects respectively. The corresponding mutablepropertyvalues structures such as
    • After creating the Mutablepropertyvalues object, enter Databinder.applypropertyvalues (Databinder.java line737). Based on the user object you just created. Creates a Beanwrapperimpl object, Beanwrapperimpl implements the PropertyAccessor (property accessor) interface. This is a class under Spring-bean, and in sping, the access to bean properties is achieved through the Beanwrapperimpl class. The function of Beanwarapperimpl is to inject into the properties of the Beanwarapperimpl corresponding Java object by means of the attribute-related description in the PropertyValue. The method of concrete injection is setpropertyvalues, this method is slightly complicated. Its responsibilities are simply summed up by invoking the corresponding set according to the property name ... Method. For example, when injecting the name property of the user object, the SetName method is obtained through reflection. Called if the method is available. This is also why defining the SPRINGMVC model object requires set ... Method. If there is no set method, the parameter injection fails.
Summary of parameter resolution bindings
    1. When SPRINGMVC is initialized, the Requestmappinghandleradapter class adds some default parameter parsers to the argumentresolvers. When SPRINGMVC receives the request, it first finds the corresponding handlermethod based on the URL.
    2. Traversing the methodparameter array of Handlermethod
    3. Find out which handlermethodargumentresolver to use based on the type of methodparameter, traverse all argumentresolvers supportsparameter ( Methodparameter parameter) Method: If true, indicates that the lookup succeeded, currently Methodparameter, using the handlermethodargumentresolver. Most of the confirmation here is based on the parameter's type of the parameter's annotation.
    4. Parse the parameter, parse out the methodparameter corresponding parameter from request, the result of parsing here is string type.
    5. The conversion parameter, which converts the corresponding string to the type required by the specific method, includes the base type, object, List, Set, Map.
Summarize
    • Parsing the code used has been uploaded to Github,https://github.com/wycm/springmvc-demo
    • The above source code is based on the Springmvc 3.2.2.RELEASE version. The above is the main process of SPRINGMVC parameter parsing binding, we hope to help you. There may be errors in this article, which I hope readers can point out.

Springmvc Source parameter parsing binding principle

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.