Spring HandlerMapping ,Handleradapter ,ViewResolver
  C5bk9rm9TFUo 2023年11月02日 65 0


HandlerMapping是用来找到url对应的处理handler对象(beans),而不是找到url对应的处理函数。Handleradapter则是用来匹配到handler的某个具体的处理函数上,然后调度执行这个函数。

ViewResolver:这个是用来解析视图,并返回资源到前端用到。

 

    1. import
    2. import
    3.   
    4. import
    5. import
    6.   
    7. public class HomeAction implements
    8.   
    9. @Override
    10. public
    11. throws
    12. return new ModelAndView("hello");  
    13.     }  
    14. }

     

    下面就要说说原理了。 

    用过python Django框架的都知道Django对于访问方式的配置就是,一个url路径和一个函数配对,你访问这个url,就会直接调用这个函数,简单明了。对于java的面向对象来说,就要分两步走。第一步首先要找到是哪个对象,即handler,本工程的handler则是HomeAction对象。第二步要找到访问的函数,即HomeAction的handleRequest方法。所以就出现了两个源码接口 HandlerMapping和HandlerAdapter,前者负责第一步,后者负责第二步。借用网上的SpringMVC架构图。 

    Spring HandlerMapping ,Handleradapter ,ViewResolver_测试


    HandlerMapping接口的实现(只举了我认识的几个) : 


    • BeanNameUrlHandlerMapping :通过对比url和bean的name找到对应的对象 
      SimpleUrlHandlerMapping :也是直接配置url和对应bean,比BeanNameUrlHandlerMapping功能更多 
      DefaultAnnotationHandlerMapping : 主要是针对注解配置@RequestMapping的,已过时 
      RequestMappingHandlerMapping :取代了上面一个 

    HandlerAdapter 接口实现: 


    • HttpRequestHandlerAdapter : 要求handler实现HttpRequestHandler接口,该接口的方法为                                                             void handleRequest(HttpServletRequest request, HttpServletResponse response)也就是  handler必须有一个handleRequest方法 

      SimpleControllerHandlerAdapter:要求handler实现Controller接口,该接口的方法为ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response),也就是本工程采用的 

      AnnotationMethodHandlerAdapter :和上面的DefaultAnnotationHandlerMapping配对使用的,也已过时 

      RequestMappingHandlerAdapter : 和上面的RequestMappingHandlerMapping配对使用,针对@RequestMapping 

    先简单的说下这个工程的流程,访问http://localhost:8080/index首先由DispatcherServlet进行转发,通过BeanNameUrlHandlerMapping(含有 /index->HomeAction的配置),找到了HomeAction,然后再拿HomeAction和每个adapter进行适配,由于HomeAction实现了Controller接口,所以最终会有SimpleControllerHandlerAdapter来完成对HomeAction的handleRequest方法的调度。然后就顺利的执行了我们想要的方法,后面的内容不在本节中说明。 

    了解了大概流程,然后就需要看源代码了。 
    首先就是SpringMVC的入口类,DispatcherServlet,它实现了Servlet接口,不再详细说DispatcherServlet的细节,不然又是一大堆的内容。每次请求都会调用它的doService->doDispatch,我们关注的重点就在doDispatch方法中。 

    1. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws
    2.         HttpServletRequest processedRequest = request;  
    3. null;  
    4. boolean multipartRequestParsed = false;  
    5.   
    6.         WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);  
    7.   
    8. try
    9. null;  
    10. null;  
    11.   
    12. try
    13.                 processedRequest = checkMultipart(request);  
    14.                 multipartRequestParsed = (processedRequest != request);  
    15. //这个是重点,第一步由HandlerMapping找到对应的handler
    16. // Determine handler for the current request.
    17.                 mappedHandler = getHandler(processedRequest);  
    18. if (mappedHandler == null || mappedHandler.getHandler() == null) {  
    19.                     noHandlerFound(processedRequest, response);  
    20. return;  
    21.                 }  
    22.   
    23. // Determine handler adapter for the current request.
    24. //这是第二步,找到合适的HandlerAdapter,然后由它来调度执行handler的方法
    25.                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  
    26.   
    27. // Process last-modified header, if supported by the handler.
    28.                 String method = request.getMethod();  
    29. boolean isGet = "GET".equals(method);  
    30. if (isGet || "HEAD".equals(method)) {  
    31. long
    32. if
    33. "Last-Modified value for [" + getRequestUri(request) + "] is: "
    34.                     }  
    35. if (new
    36. return;  
    37.                     }  
    38.                 }  
    39.   
    40. if
    41. return;  
    42.                 }  
    43.   
    44. try
    45. // Actually invoke the handler.
    46.                     mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
    47.                 }  
    48. finally
    49. if
    50. return;  
    51.                     }  
    52.                 }  
    53.   
    54.                 applyDefaultViewName(request, mv);  
    55.                 mappedHandler.applyPostHandle(processedRequest, response, mv);  
    56.             }  
    57. catch
    58.                 dispatchException = ex;  
    59.             }  
    60.             processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  
    61.         }  
    62. catch
    63.             triggerAfterCompletion(processedRequest, response, mappedHandler, ex);  
    64.         }  
    65. catch
    66.             triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);  
    67.         }  
    68. finally
    69. if
    70. // Instead of postHandle and afterCompletion
    71.                 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);  
    72. return;  
    73.             }  
    74. // Clean up any resources used by a multipart request.
    75. if
    76.                 cleanupMultipart(processedRequest);  
    77.             }  
    78.         }  
    79.     }

    第一步详细查看: 

    1. protected HandlerExecutionChain getHandler(HttpServletRequest request) throws
    2. for (HandlerMapping hm : this.handlerMappings) {  
    3. if
    4.                 logger.trace(  
    5. "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");  
    6.             }  
    7.             HandlerExecutionChain handler = hm.getHandler(request);  
    8. if (handler != null) {  
    9. return
    10.             }  
    11.         }  
    12. return null;  
    13.     }


    可以看到就是通过遍历所有已注册的HandlerMapping来找到对应的handler,然后构建出一个HandlerExecutionChain,它包含了handler和HandlerMapping本身的一些拦截器,如下 

    1. public class
    2.   
    3. private final
    4.   
    5. private
    6.   
    7. private
    8.           
    9. //其他代码省略
    10. }

    其中HandlerMapping的getHandler实现: 

    1. public final HandlerExecutionChain getHandler(HttpServletRequest request) throws
    2.         Object handler = getHandlerInternal(request);  
    3. if (handler == null) {  
    4.             handler = getDefaultHandler();  
    5.         }  
    6. if (handler == null) {  
    7. return null;  
    8.         }  
    9. // Bean name or resolved handler?
    10. if (handler instanceof
    11.             String handlerName = (String) handler;  
    12.             handler = getApplicationContext().getBean(handlerName);  
    13.         }  
    14. return
    15.     }


    这里的getHandlerInternal(request)是个抽象方法,由具体的HandlerMapping来实现,获取到的handler如果为空,则获取默认配置的handler,如果handler为String类型,则表示这个则会去Spring容器里面去找这样名字的bean。 
    再看下BeanNameUrlHandlerMapping的getHandlerInternal(request)的具体实现(通过一系列的接口设计,之后再好好看看这个设计,到BeanNameUrlHandlerMapping这只用实现该方法中的一部分),如下 

    1. public class BeanNameUrlHandlerMapping extends
    2.   
    3. /**
    4.      * Checks name and aliases of the given bean for URLs, starting with "/".
    5.      */
    6. @Override
    7. protected
    8. new
    9. if (beanName.startsWith("/")) {  
    10.             urls.add(beanName);  
    11.         }  
    12.         String[] aliases = getApplicationContext().getAliases(beanName);  
    13. for
    14. if (alias.startsWith("/")) {  
    15.                 urls.add(alias);  
    16.             }  
    17.         }  
    18. return
    19.     }  
    20.   
    21. }


    这里面注释说,bean的name必须以/开头,它才处理,将信息存储在Map<String, Object> handlerMap中,对于本工程来说就是{'/index':HomeAction对象}。 
    至此这里完成了第一步,下面开始第二步,即方法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());的具体实现: 

    1. protected HandlerAdapter getHandlerAdapter(Object handler) throws
    2. for (HandlerAdapter ha : this.handlerAdapters) {  
    3. if
    4. "Testing handler adapter [" + ha + "]");  
    5.             }  
    6. if
    7. return
    8.             }  
    9.         }  
    10. throw new ServletException("No adapter for handler ["
    11. "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");  
    12.     }


    遍历所有的HandlerAdapter,判断他们是否支持这个handler。 
    我们来看下HttpRequestHandlerAdapter的supports(handler)方法: 

    1. public class HttpRequestHandlerAdapter implements
    2.   
    3. @Override
    4. public boolean
    5. //就是判断handler是否实现了HttpRequestHandler接口
    6. return (handler instanceof
    7.     }  
    8.   
    9. @Override
    10. public
    11. throws
    12. //若handler实现了HttpRequestHandler接口,则调用该接口的方法,执行我们在该方法中写的业务逻辑
    13.         ((HttpRequestHandler) handler).handleRequest(request, response);  
    14. return null;  
    15.     }  
    16.   
    17. @Override
    18. public long
    19. if (handler instanceof
    20. return
    21.         }  
    22. return
    23.     }  
    24.   
    25. }


    同理SimpleControllerHandlerAdapter也是这样类似的逻辑 

    1. public class SimpleControllerHandlerAdapter implements
    2.   
    3. @Override
    4. public boolean
    5. return (handler instanceof
    6.     }  
    7.   
    8. @Override
    9. public
    10. throws
    11.   
    12. return
    13.     }  
    14.   
    15. @Override
    16. public long
    17. if (handler instanceof
    18. return
    19.         }  
    20. return
    21.     }  
    22.   
    23. }


    剩余两个AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter就比较复杂,我也没看。 
    按照本工程的配置,则SimpleControllerHandlerAdapter是支持HomeAction的,然后就会执行SimpleControllerHandlerAdapter的handle(processedRequest, response, mappedHandler.getHandler())方法。本质上就会调用HomeAction实现Controller接口的方法。至此就分析完了。 
    了解过程了之后,然后就是最重要的也是经常配置出问题的地方。DispatcherServlet的handlerMappings和handlerAdapters的来源问题。 

    DispatcherServlet初始化的时候,会调用一个方法如下: 

    1. protected void
    2.         initMultipartResolver(context);  
    3.         initLocaleResolver(context);  
    4.         initThemeResolver(context);  
    5. //初始化一些HandlerMapping
    6.         initHandlerMappings(context);  
    7. //初始化一些HandlerAdapter
    8.         initHandlerAdapters(context);  
    9.         initHandlerExceptionResolvers(context);  
    10.         initRequestToViewNameTranslator(context);  
    11.         initViewResolvers(context);  
    12.         initFlashMapManager(context);  
    13.     }

    这里可以看到,它会初始化一些HandlerMapping和HandlerAdapter,这两个方法非常重要,理解了这两个方法你就会知道,配置不对问题出在哪里,下面具体看下这两个方法: 

    1. private void
    2. this.handlerMappings = null;  
    3.   
    4. if (this.detectAllHandlerMappings) {  
    5. // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
    6.             Map<String, HandlerMapping> matchingBeans =  
    7. class, true, false);  
    8. if
    9. this.handlerMappings = new
    10. // We keep HandlerMappings in sorted order.
    11. this.handlerMappings);  
    12.             }  
    13.         }  
    14. else
    15. try
    16. class);  
    17. this.handlerMappings = Collections.singletonList(hm);  
    18.             }  
    19. catch
    20. // Ignore, we'll add a default HandlerMapping later.
    21.             }  
    22.         }  
    23.   
    24. // Ensure we have at least one HandlerMapping, by registering
    25. // a default HandlerMapping if no other mappings are found.
    26. if (this.handlerMappings == null) {  
    27. this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);  
    28. if
    29. "No HandlerMappings found in servlet '" + getServletName() + "': using default");  
    30.             }  
    31.         }  
    32.     }


    detectAllHandlerMappings是DispatcherServlet的一个属性,你是可以在web.xml中配置的,默认是true,如果为true,则会去从本工程mvc-servlet.xml文件中去探测所有实现了HandlerMapping的bean,如果有,则加入DispatcherServlet的handlerMappings中。如果detectAllHandlerMappings为false,则直接去容器中找id="handlerMapping"且实现了HandlerMapping的bean.如果以上都没找到,则会去加载默认的HandlerMapping。 

    1. /** Detect all HandlerMappings or just expect "handlerMapping" bean? */
    2. private boolean detectAllHandlerMappings = true;

    本工程由于没有配置HandlerMapping,所以它会去加载默认的,下面看看默认的配置是什么 

    1. protected
    2.         String key = strategyInterface.getName();  
    3. //defaultStrategies存储了默认的配置
    4.         String value = defaultStrategies.getProperty(key);  
    5. if (value != null) {  
    6.             String[] classNames = StringUtils.commaDelimitedListToStringArray(value);  
    7. new
    8. for
    9. try
    10. class.getClassLoader());  
    11.                     Object strategy = createDefaultStrategy(context, clazz);  
    12.                     strategies.add((T) strategy);  
    13.                 }  
    14. catch
    15. throw new
    16. "Could not find DispatcherServlet's default strategy class ["
    17. "] for interface [" + key + "]", ex);  
    18.                 }  
    19. catch
    20. throw new
    21. "Error loading DispatcherServlet's default strategy class ["
    22. "] for interface [" + key + "]: problem with class file or dependent class", err);  
    23.                 }  
    24.             }  
    25. return
    26.         }  
    27. else
    28. return new
    29.         }  
    30.     }


    继续看看defaultStrategies是如何初始化的: 

    1. private static final
    2.   
    3. static
    4. // Load default strategy implementations from properties file.
    5. // This is currently strictly internal and not meant to be customized
    6. // by application developers.
    7. try
    8. //这里的DEFAULT_STRATEGIES_PATH就是DispatcherServlet.properties
    9. new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);  
    10.             defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);  
    11.         }  
    12. catch
    13. throw new IllegalStateException("Could not load 'DispatcherServlet.properties': "
    14.         }  
    15.     }

    这里使用静态代码块来加载配置文件DispatcherServlet.properties,它所在位置就是和DispatcherServlet同一目录下面的,如下图所示: 


    Spring HandlerMapping ,Handleradapter ,ViewResolver_前端_02


    该默认的配置文件的内容如下: 

    1. # Default implementation classes for
    2. # Used as fallback when no matching beans are found in the DispatcherServlet context.  
    3. # Not meant to be customized by application developers.  
    4.   
    5. org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver  
    6.   
    7. org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver  
    8.   
    9. #这里就是默认的HandlerMapping的配置  
    10. org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\  
    11.     org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping  
    12. #这里就是默认的HandlerAdapter的配置  
    13. org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\  
    14.     org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\  
    15.     org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter  
    16.   
    17. org.springframework.web.servlet.HandlerExceptinotallow=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\  
    18.     org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\  
    19.     org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver  
    20.   
    21. org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator  
    22.   
    23. org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver  
    24.   
    25. org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

    也就是说,当你什么都没有配置时,默认会加载以上的配置。正是由于有了上述默认配置的BeanNameUrlHandlerMapping(它要求name必须是以/开头的),它才会存储我们在mvc-servlet.xml中配置的<bean name="/index" class="com.lg.mvc.HomeAction"></bean>,同样正是由于有了SimpleControllerHandlerAdapter(由于handler实现了Controller接口,所以它的support方法支持我们的handler),才会调度执行HomeAction的handleRequest方法。

    【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

    1. 分享:
    最后一次编辑于 2023年11月08日 0

    暂无评论

    推荐阅读
      2Vtxr3XfwhHq   2024年05月17日   55   0   0 Java
      Tnh5bgG19sRf   2024年05月20日   111   0   0 Java
      8s1LUHPryisj   2024年05月17日   47   0   0 Java
      aRSRdgycpgWt   2024年05月17日   47   0   0 Java
    C5bk9rm9TFUo