In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/02 Report--
This article introduces what is the principle of Controller search in SpringMVC. The content is very detailed. Interested friends can use it for reference. I hope it will be helpful to you.
SpringMVC request process
The process for Controller to find steps 1 through 2 corresponding to the figure above
Before the SpringMVC initialization process understands the initialization process, recognize two classes
RequestMappingInfo class, encapsulation of RequestMapping annotations. It contains information about the http request header. Such as uri, method, params, header and other parameters. An object corresponds to an RequestMapping comment
The HandlerMethod class is an encapsulation of Controller's request processing method. It contains the bean object to which the method belongs, the method object corresponding to the method, the parameters of the method, and so on.
The figure above shows the inheritance relationship of RequestMappingHandlerMapping. When SpringMVC initializes, it first executes the afterPropertiesSet method in RequestMappingHandlerMapping, and then enters the afterPropertiesSet method (line:93) of AbstractHandlerMethodMapping, which enters the initHandlerMethods method (line:103) of the current class. The job of this method is to scan the beans from applicationContext and then find and register the processor method from bean, as shown in the following code.
Protected void initHandlerMethods () {if (logger.isDebugEnabled ()) {logger.debug ("Looking for request mappings in application context:" >
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 whether the current bean definition has Controlller annotations or RequestMapping annotations. After reading here, the logic may wonder if only RequestMapping would take effect. The answer is no, because in this case, the class will not be registered as Spring bean when Spring initializes, and this class will not be traversed when traversing beanNames, so it is OK to replace Controller with Compoent annotation here, but it is not common to do so. When bean is determined to be handlers, the specific handler method (that is, the specific request handling method under the Controller class that we usually define) will be found from the bean. The lookup code is as follows
Protected void detectHandlerMethods (final Object handler) {/ / get the class object Class handlerType = (handler instanceof String) of the current Controller bean? GetApplicationContext () .getType ((String) handler): handler.getClass (); / / same as above, it is also the class object of this Controller bean final Class userType = ClassUtils.getUserClass (handlerType); / / gets all the handler method of the current bean. The search here is based on whether there are RequestMapping annotations according to the method definition. If you create a RequestMappingInfo object Set methods = HandlerMethodSelector.selectMethods (userType, new MethodFilter () {public boolean matches (Method method) {return getMappingForMethod (method, userType)! = null;}}) according to the annotations; / / traverse and register all handler method for (Method method: methods) {T mapping = getMappingForMethod (method, userType) of the current bean; / / register handler method, enter the following method registerHandlerMethod (handler, method, mapping) }}
The getMappingForMethod method is called in two places in the above code
Protected RequestMappingInfo getMappingForMethod (Method method, Class handlerType) {RequestMappingInfo info = null; / / get @ RequestMapping annotations for method RequestMapping methodAnnotation = AnnotationUtils.findAnnotation (method, RequestMapping.class); if (methodAnnotation! = null) {RequestCondition methodCondition = getCustomMethodCondition (method); info = createRequestMappingInfo (methodAnnotation, methodCondition); / / get @ RequtestMapping annotations for bean to which method belongs 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 this method is to create a RequestMappingInfo object based on the handler method method. First determine whether the mehtod contains RequestMpping annotations. If so, create a RequestMappingInfo object directly based on the contents of the annotation. After creation, determine whether the bean to which the current method belongs also contains RequestMapping annotations. If this comment is included, a RequestMappingInfo object is created based on the annotation on the class. Then merge the RequestMappingInfo object on method, and finally return the merged object. Now looking back at the detectHandlerMethods method, there are two places where the getMappingForMethod method is called, and I think it can be optimized here. When you determine whether the method is handler in the first place, the created RequestMappingInfo object can be saved and used directly later, thus missing the process of creating a RequestMappingInfo object. Then go into the registerHandlerMehtod method, as follows
Protected void registerHandlerMethod (Object handler, Method method, T mapping) {/ / create HandlerMethod HandlerMethod newHandlerMethod = createHandlerMethod (handler, method); HandlerMethod oldHandlerMethod = handlerMethods.get (mapping); / / check the configuration for ambiguity if (oldHandlerMethod! = null & &! oldHandlerMethod.equals (newHandlerMethod)) {throw new IllegalStateException ("Ambiguous mapping found. Cannot map'"+ newHandlerMethod.getBean () +" 'bean method\ n "+ newHandlerMethod +"\ nto "+ mapping +": There is 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 annotated by @ RequestMapping, and then add the value- > RequestMappingInfo mapping record to urlMap Set 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 the information about the RequestMapping annotations of the methods under the specific Controller encapsulated. A RequestMapping annotation corresponds to a RequestMappingInfo object. HandlerMethod is similar to RequestMappingInfo in that it encapsulates the specific processing methods under Controlelr. First, take a look at the first line of the method and create a HandlerMethod object based on handler and mehthod. The second line gets the HandlerMethod corresponding to the current mapping through handlerMethods map. Then determine if the same RequestMapping configuration exists. The following configuration will cause it to be thrown here
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") @ ResponseBody public String test1 () {return "method test1";} @ RequestMapping (value = "/ test1") @ ResponseBody public String test2 () {return "method test2";}}
Checking the RequestMapping configuration for ambiguity during the SpingMVC startup (initialization) phase is one of the places to check for ambiguity (a place to check for ambiguity at run time will be mentioned later). After confirming that the configuration is normal, the RequestMappingInfo and HandlerMethod objects are added to the handlerMethods (LinkedHashMap), and the RequestMapping annotated value and ReuqestMappingInfo objects are then added to the urlMap.
Simple summary of registerHandlerMethod method
There are three main responsibilities of this method
Check the RequestMapping annotation configuration for ambiguity.
Build a mapping map from RequestMappingInfo to HandlerMethod. The map is the member variable handlerMethods of AbstractHandlerMethodMapping. LinkedHashMap .
Build the member variable urlMap,MultiValueMap of AbstractHandlerMethodMapping. This data structure can be understood as Map. The key of type String holds the value of the RequestMapping annotations on the processing method. It's a specific uri.
Let's start with the following Controller
Controller@RequestMapping ("/ UrlMap") public class UrlMapController {@ RequestMapping (value = "/ test1", method = RequestMethod.GET) @ ResponseBody public String test1 () {return "method test1";} @ RequestMapping (value = "/ test1") @ ResponseBody public String test2 () {return "method test2";} @ RequestMapping (value = "/ test3") @ ResponseBody public String test3 () {return "method test3";}}
After initialization, the structure of the urlMap of the corresponding AbstractHandlerMethodMapping is as follows
This is the main process of initialization of SpringMVC
Search process
To understand the lookup process, with a question, the following Controller is available
@ Controller@RequestMapping ("/ LookupTest") public class LookupTestController {@ RequestMapping (value = "/ test1", method = RequestMethod.GET) @ ResponseBody public String test1 () {return "method test1";} @ RequestMapping (value = "/ test1", headers = "Referer= https://www.baidu.com") @ ResponseBody public String test2 () {return" method test2 ") @ RequestMapping (value = "/ test1", params = "id=1") @ ResponseBody public String test3 () {return "method test3";} @ RequestMapping (value = "/ *") @ ResponseBody public String test4 () {return "method test4";}}
Have the following request
Which method will this request enter?
After the web container (Tomcat, jetty) receives the request, it is handed over 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 processing, enter the doService method in line:936. Then enter the doDispatch method in Line856. Get the currently requested processor handler in line:896. Then enter the lookupHandlerMethod method of AbstractHandlerMethodMapping. The code is as follows
Protected HandlerMethod lookupHandlerMethod (String lookupPath, HttpServletRequest request) throws Exception {List matches = new ArrayList (); / / get direct matching RequestMappingInfos List directPathMatches = this.urlMap.get (lookupPath) based on uri; if (directPathMatches! = null) {addMatchingMappings (directPathMatches, matches, request);} / / there is no direct matching RequetMappingInfo, traversing all RequestMappingInfo if (matches.isEmpty ()) {/ / No choice but to go through all mappings addMatchingMappings (this.handlerMethods.keySet (), matches, request) } / / get the best matching RequestMappingInfo corresponding to HandlerMethod if (! matches.isEmpty ()) {Comparator comparator = new MatchComparator (getMappingComparator (request)); Collections.sort (matches, comparator); if (logger.isTraceEnabled ()) {logger.trace ("Found" >)
Enter the lookupHandlerMethod method, where lookupPath= "/ LookupTest/test1", according to lookupPath, that is, the requested uri. Find the urlMap directly and get the RequestMappingInfo list that matches directly. There will be 3 RequestMappingInfo matches here. As follows
Then enter the addMatchingMappings method
Private void addMatchingMappings (Collection mappings, List matches, HttpServletRequest request) {for (T mapping: mappings) {T match = getMatchingMapping (mapping, request); if (match! = null) {matches.add (new Match (match, handlerMethods.get (mapping));}
The responsibility of this method is to traverse whether the RequestMappingInfo in the currently requested uri and mappings can match, and if so, create the same RequestMappingInfo object. Then get the handlerMethod corresponding to RequestMappingInfo. Then create a Match object to add to the matches list. After executing the addMatchingMappings method, go back to lookupHandlerMethod. At this point, matches also has three RequestMappingInfo objects that can match. The next step is to sort the matchers list and get the first element of the list as the best match. Returns the HandlerMethod of Match. Go into the compareTo method of RequestMappingInfo and take a look at the specific sorting logic. The code is as follows
Public int compareTo (RequestMappingInfo other, HttpServletRequest request) {int result = patternsCondition.compareTo (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 order of matching is value > params > headers > consumes > produces > methods > custom. Seeing here, you can easily get the answer to the previous question. When value is the same, params is better able to match first. So that request goes into the test3 () method. Go back to lookupHandlerMethod and find HandlerMethod. SpringMVC also checks the configuration for ambiguity again, which is done by comparing the two RequestMappingInfo with the highest degree of match. It may be doubtful here that the initialization SpringMVC has ambiguity in checking the configuration, so why do you check it again? If there are two methods in Controller, the following configuration can pass the initialization ambiguity check.
@ 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 that the http://localhost:8080/SpringMVC-Demo/LookupTest/test5 request is executed, it will be thrown in the lookupHandlerMethod method
Java.lang.IllegalStateException: Ambiguous handler methods mapped for HTTP path 'http://localhost:8080/SpringMVC-Demo/LookupTest/test5' exception. The exception is thrown here because the compareTo method of RequestMethodsRequestCondition is the number of method of the comparison. The code is as follows
Public int compareTo (RequestMethodsRequestCondition other, HttpServletRequest request) {return other.methods.size ()-this.methods.size ();}
When do I match wildcards? Wildcard matching enters the addMatchingMappings method only when the RequestMappingInfo that directly matches the value cannot be obtained through urlMap.
What is the principle of Controller search in SpringMVC is shared here, I hope the above content can be of some help to you, can learn more knowledge. If you think the article is good, you can share it for more people to see.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.