SPRINGMVC Source Controller Find the principle

Source: Internet
Author: User

Summary
    • This article from the source level simple explanation of SPRINGMVC processor mapping link, that is, to find the controller detailed process.
SPRINGMVC Request Process

    • The controller finds the procedure in the corresponding steps 1 through 2
SPRINGMVC initialization process before understanding initialization process, first recognize two classes
    1. Requestmappinginfo class, encapsulation of requestmapping annotations. Contains information about the HTTP request header. such as Uri, method, params, header and other parameters. One object corresponds to one requestmapping annotation
    2. The Handlermethod class is the encapsulation of the Controller's processing request method. It contains the Bean object to which the method belongs, the method object that corresponds to it, the parameters of the method, and so on.
  • The

    is an inheritance relationship of requestmappinghandlermapping. When the SPRINGMVC is initialized, the Afterpropertiesset method in Requestmappinghandlermapping is executed first, It then enters the abstracthandlermethodmapping Afterpropertiesset method (line:93), which goes into the Inithandlermethods method (line:103) of the current class. The function of this method is to scan the beans from ApplicationContext and then find and register the processor method from the bean, the code is as follows.

      protected void Inithandlermethods () {if (logger.isdebugenabled ()) {Logger.debug ("Looking for request Mappin  GS in Application Context: "+ getapplicationcontext ());    }//Get all the beans in ApplicationContext name string[] Beannames = (this.detecthandlermethodsinancestorcontexts?    Beanfactoryutils.beannamesfortypeincludingancestors (Getapplicationcontext (), Object.class):  Getapplicationcontext (). Getbeannamesfortype (Object.class)); Traversing the Beanname array for (String beanname:beannames) {//ishandler Determines whether the bean definition has a controller annotation or requestmapping annotation if (is) based on the Bean.  Handler (Getapplicationcontext (). GetType (Beanname))) {detecthandlermethods (beanname); }} handlermethodsinitialized (Gethandlermethods ());}  
  • The Ishandler method is actually very simple, as follows

    @Overrideprotected boolean isHandler(Class<?> beanType) {  return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||    (AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));}
  • is to determine if the current bean definition has controlller annotations or requestmapping annotations, and see here the logic might think that if only requestmapping will take effect? The answer is no, because in this case spring does not register the class as a spring bean when it is initialized, and it does not traverse this class when traversing beannames, so it is also possible to switch controllers to compoent annotations, but this is not usually done. When the bean is determined to be handlers, the specific handler method (that is, the specific definition of the request processing method under the Controller class that we typically define) is looked up from the bean, and the lookup code is as follows

      protected void Detecthandlermethods (final Object handler) {//Gets the Class object to the current controller bean class<?> ha    Ndlertype = (handler instanceof String)?  Getapplicationcontext (). GetType ((String) handler): Handler.getclass ();  Ditto, also the controller Bean's Class object final class<?> usertype = Classutils.getuserclass (Handlertype); Gets all handler method for the current bean. The search here is based on whether the method defines a requestmapping annotation. If a Requestmappinginfo object is created based on annotations set<method> methods = Handlermethodselector.selectmethods (usertype, new  Methodfilter () {Public Boolean matches (method method) {return Getmappingformethod (method, usertype)! = NULL;  }  });  Traverse and register all handler method for the current bean (method method:methods) {T mapping = Getmappingformethod (method, usertype);  Register handler method, enter the following methods Registerhandlermethod (handler, method, mapping); }}
  • The code above has two places called the Getmappingformethod method

      protected Requestmappinginfo Getmappingformethod (   Method method, Class<?> handlertype) {Requestmappinginfo info = null; Get the @requestmapping annotation for method requestmapping Methodannotation = Annotationutils.findannotation (method,  Requestmapping.class);  if (methodannotation! = null) {requestcondition<?> methodcondition = Getcustommethodcondition (method);   info = Createrequestmappinginfo (methodannotation, methodcondition); Gets the @requtestmapping annotation for the bean that the method belongs to requestmapping typeannotation = Annotationutils.findannotation (Handlertype,  Requestmapping.class);    if (typeannotation! = null) {requestcondition<?> typecondition = getcustomtypecondition (Handlertype);  Merge two @requestmapping annotations info = createrequestmappinginfo (typeannotation, typecondition). Combine (info); }} return info;  
  • The purpose of the

    method is to create Requestmappinginfo objects based on the handler method. First, determine whether the Mehtod contains requestmpping annotations. If any, the Requestmappinginfo object is created directly based on the content of the annotation. Determines whether the bean that the current method belongs to also contains requestmapping annotations after creation. If this annotation is included, a Requestmappinginfo object is created based on the annotations on that class. The Requestmappinginfo object on the method is then merged, and finally the merged object is returned. Now go back to see the Detecthandlermethods method, there are two calls to the Getmappingformethod method, the personal feel that this can be optimized, in the first place to determine whether the method is handler, The created Requestmappinginfo object can be saved and used directly behind it, and the process of creating the Requestmappinginfo object is less. Then immediately enter the Registerhandlermehtod method, as follows

    protected void Registerhandlermethod (Object handler, method, T mapping) {//create Handlermethod Handlermethod Newhand  Lermethod = Createhandlermethod (handler, method);  Handlermethod Oldhandlermethod = handlermethods.get (mapping); Check if the configuration is ambiguous if (oldhandlermethod! = null &&!oldhandlermethod.equals (Newhandlermethod)) {throw new Illegalsta Teexception ("Ambiguous mapping found. Cannot map ' + Newhandlermethod.getbean () + "Bean method \ n" + Newhandlermethod + "\nto" + mapping + ": there I  S already ' "+ oldhandlermethod.getbean () +" ' Bean method\n "+ Oldhandlermethod +" mapped. ");  This.handlerMethods.put (mapping, Newhandlermethod);  if (logger.isinfoenabled ()) {logger.info ("Mapped \" "+ Mapping +" \ "onto" + Newhandlermethod); }//Get the value of the @requestmapping annotation and add the Value->requestmappinginfo mapping record to urlmap set<string> patterns =  Getmappingpathpatterns (mapping);    for (String pattern:patterns) {if (!getpathmatcher (). Ispattern (pattern)) {This.urlMap.add (pattern, mapping); }  }}
  • The type of T here is requestmappinginfo. This object is information about the requestmapping annotations of the methods that are encapsulated under the specific controller. A requestmapping annotation corresponds to a Requestmappinginfo object. Handlermethod is similar to Requestmappinginfo, and is the encapsulation of specific processing methods under CONTROLELR. Start by looking at the first line of the method, creating Handlermethod objects based on handler and Mehthod. The second line obtains the handlermethod corresponding to the current mapping by Handlermethods map. Then determine if the same requestmapping configuration exists. The following configuration causes this to be thrown
    Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map...
    Abnormal

    @Controller@RequestMapping("/AmbiguousTest")public class AmbiguousTestController {@RequestMapping(value = "/test1")@ResponseBodypublic String test1(){    return "method test1";}@RequestMapping(value = "/test1")@ResponseBodypublic String test2(){    return "method test2";}}
  • Checking the requestmapping configuration for ambiguity at the SPINGMVC startup (initialization) stage is one of the ambiguities (and later, a place to check ambiguity at runtime). Then verify that the Requestmappinginfo and Handlermethod objects are added to the handlermethods after the configuration is correct (linkedhashmap

Simple summary of Registerhandlermethod method

There are 3 main responsibilities of this method

    1. Check if the requestmapping annotation configuration is ambiguous.
    2. Build a mapping map of Requestmappinginfo to Handlermethod. The map is the member variable handlermethods of abstracthandlermethodmapping. Linkedhashmap
    3. Building member variables for abstracthandlermethodmapping Urlmap,multivaluemap

      @Controller@RequestMapping("/UrlMap")public class UrlMapController {@RequestMapping(value = "/test1", method = RequestMethod.GET)@ResponseBodypublic String test1(){    return "method test1";}@RequestMapping(value = "/test1")@ResponseBodypublic String test2(){    return "method test2";}@RequestMapping(value = "/test3")@ResponseBodypublic String test3(){    return "method test3";}}
  • After initialization is complete, the structure of the urlmap corresponding to the abstracthandlermethodmapping is as follows
  • The above is the main process of SPRINGMVC initialization

    Find process
  • To understand the search process, with a problem, the following controller is available

    @Controller@RequestMapping("/LookupTest")public class LookupTestController {@RequestMapping(value = "/test1", method = RequestMethod.GET)@ResponseBodypublic String test1(){    return "method test1";}@RequestMapping(value = "/test1", headers = "Referer=https://www.baidu.com")@ResponseBodypublic String test2(){    return "method test2";}@RequestMapping(value = "/test1", params = "id=1")@ResponseBodypublic String test3(){    return "method test3";}@RequestMapping(value = "/*")@ResponseBodypublic String test4(){    return "method test4";}}
  • Have the following request
  • Which method does this request go into?
  • After the Web container (Tomcat, jetty) receives the request, it is handed to dispatcherservlet for processing. Frameworkservlet calls the corresponding request method (Eg:get calls Doget), and then calls the ProcessRequest method. After entering the ProcessRequest method, after a series of treatments, the line:936 enters the Doservice method. Then enter the Dodispatch method in the Line856. Gets the processor handler for the current request in line:896. Then enter the Lookuphandlermethod method of the abstracthandlermethodmapping. The code is as follows

    Protected Handlermethod Lookuphandlermethod (String Lookuppath, httpservletrequest request) throws Exception {list<   match> matches = new arraylist<match> ();  Obtain a direct match based on the URI Requestmappinginfos list<t> directpathmatches = This.urlMap.get (Lookuppath);  if (directpathmatches! = null) {addmatchingmappings (directpathmatches, matches, request); }//There is no direct matching requetmappinginfo, traverse all Requestmappinginfo if (Matches.isempty ()) {//no choice but to go through all Mappin  GS addmatchingmappings (This.handlerMethods.keySet (), matches, request); }//Get best match for requestmappinginfo corresponding Handlermethod if (!matches.isempty ()) {comparator<match> Comparator = new match  Comparator (Getmappingcomparator (request));  Collections.sort (matches, comparator); if (logger.istraceenabled ()) {Logger.trace ("Found" + matches.size () + "matching mapping (s) for [" + Lookuppath + "]  : "+ matches);  }//Check the ambiguity of the configuration again Match Bestmatch = matches.get (0); if (Matches.size () > 1) {Match SeconDbestmatch = Matches.get (1);        if (Comparator.compare (Bestmatch, secondbestmatch) = = 0) {Method m1 = BestMatch.handlerMethod.getMethod ();        Method m2 = SecondBestMatch.handlerMethod.getMethod ();  throw new IllegalStateException ("ambiguous handler methods mapped for HTTP path '" + Request.getrequesturl ()    + "': {" + m1 + "," + M2 + "}");  }} handlematch (Bestmatch.mapping, Lookuppath, request);  return bestmatch.handlermethod;  } else {return Handlenomatch (Handlermethods.keyset (), Lookuppath, request); }}
  • Enter the Lookuphandlermethod method, where lookuppath= "/lookuptest/test1", according to Lookuppath, is the URI of the request. Find UrlMap directly, get the Requestmappinginfo list that matches directly. This will match up to 3 requestmappinginfo. As follows

  • Then enter the Addmatchingmappings method

    private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {  for (T mapping : mappings) {  T match = getMatchingMapping(mapping, request);  if (match != null) {    matches.add(new Match(match, handlerMethods.get(mapping)));  }  }}
  • The function of this method is to traverse the URI of the current request and mappings in the Requestmappinginfo can match on, if can match on, create an identical Requestmappinginfo object. Then get the requestmappinginfo corresponding to the Handlermethod. Then create a match object to add to the matches list. After executing the Addmatchingmappings method, go back to Lookuphandlermethod. At this time matches also has 3 Requestmappinginfo objects that can be matched. The next process is to sort the matchers list and get the first element of the list as the best match. Returns the match's Handlermethod. Here go to Requestmappinginfo's CompareTo method and look at the specific sort logic. The code is as follows

      public int compareTo (Requestmappinginfo other, httpservletrequest request) {int result = PATTERNSCONDITION.C  Ompareto (Other.getpatternscondition (), request);  if (Result! = 0) {return result;  } result = Paramscondition.compareto (Other.getparamscondition (), request);  if (Result! = 0) {return result;  } result = Headerscondition.compareto (Other.getheaderscondition (), request);  if (Result! = 0) {return result;  } result = Consumescondition.compareto (Other.getconsumescondition (), request);  if (Result! = 0) {return result;  } result = Producescondition.compareto (Other.getproducescondition (), request);  if (Result! = 0) {return result;  } result = Methodscondition.compareto (Other.getmethodscondition (), request);  if (Result! = 0) {return result;  } result = Customconditionholder.compareto (other.customconditionholder, request);  if (Result! = 0) {return result; } return 0;  
  • As you can see in the
  • Code, the sequence of matches is Value>params>headers>consumes>produces>methods>custom, see here, The question ahead can easily be answered. In the same case as value, the params can match first. So that request goes into the Test3 () method. Go back to Lookuphandlermethod and find Handlermethod. SPRINGMVC will be here again check the ambiguity of the configuration, the principle here is compared by comparing the two requestmappinginfo with the highest matching degree. There may be some doubt about the ambiguity of the check configuration in the initialization Springmvc, why this is checked again. If the controller now has the following two methods, the following configuration can be checked by the initial ambiguity.

      @RequestMapping (value = "/test5", method = {requestmethod.get, requestmethod.post}) @ Responsebodypublic String Test5 () {return "method Test5";} @RequestMapping (value = "/test5", method = {requestmethod.get, requestmethod.delete}) @ResponseBodypublic String Test6 ( {return "method Test6";}  
  • Now execution of the HTTP://LOCALHOST:8080/SPRINGMVC-DEMO/LOOKUPTEST/TEST5 request will be thrown in the Lookuphandlermethod method
    java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path ‘http://localhost:8080/SpringMVC-Demo/LookupTest/test5‘Abnormal. This exception is thrown because Requestmethodsrequestcondition's CompareTo method is the number of methods compared. The code is as follows

    public int compareTo(RequestMethodsRequestCondition other, HttpServletRequest request) {  return other.methods.size() - this.methods.size();}
  • When do I match a wildcard character? The wildcard match goes into the Addmatchingmappings method when the requestmappinginfo is not obtained by UrlMap directly matching the value.

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 SPRINGMVC request to find the main process, we hope to help. There may be errors in this article, which I hope readers can point out.

SPRINGMVC Source Controller Find the 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.