Spring MVC之DispatcherServlet解析

一. DispatcherServlet上下文的形成

                 web.xml

[html] 
view plain
 copy

  1. <?xml version=“1.0” encoding=“UTF-8”?>  
  2. <web-app version=“2.5”   
  3.     xmlns=“http://java.sun.com/xml/ns/javaee”   
  4.     xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”   
  5.     xsi:schemaLocation=”http://java.sun.com/xml/ns/javaee   
  6.     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd”>  
  7.   <display-name></display-name>   
  8.   <welcome-file-list>  
  9.     <welcome-file>index.jsp</welcome-file>  
  10.   </welcome-file-list>  
  11.   <servlet>  
  12.               <servlet-name>springMVC</servlet-name>  
  13.               <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
  14.               <init-param>  
  15.                       <param-name>contextConfigLocation</param-name>  
  16.                       <param-value>classpath*:springController.xml</param-value>  
  17.               </init-param>  
  18.   </servlet>  
  19.     
  20.   <servlet-mapping>  
  21.                 <servlet-name>springMVC</servlet-name>  
  22.                 <url-pattern>/</url-pattern>  
  23.   </servlet-mapping>  
  24.   
  25. <span style=“color:#ff6666;”> <context-param>  
  26.                  <param-name>contextConfigLocation</param-name>  
  27.                  <param-value>classpath*:springController.xml</param-value>  
  28.   </context-param>   
  29.   <listener>  
  30.                  <listener-class>  
  31.                        org.springframework.web.context.ContextLoaderListener  
  32.                  </listener-class>  
  33.   </listener>  
  34.   
  35. </web-app></span>  

     
当我们单独使用SpringMVC这个框架的时候我们可以不需要配置web.xml文件中红色的部分,但一般很少单独使用SpringMVC,SpringMVC一般要和mybatis或hibernate集成的。在我们启动服务生成容器的时候,容器中除了SpringMVC一些自带的bean以外,还要加载我们自己所写的bean,例如dao,service等,ContextLoaderListener的作用就是加载这些bean的,也许有人会问DispatcherServlet在加载配置文件生成容器的时候,能不能将dao,service同时加载进来,我刚刚试了一下,在DispatcherServlet的配置文件中自己写了一个bean,发现在访问的时候会报错。下面是DispatcherServlet初始上下文和ContextLoaderListener初始上下文的关系图

《Spring MVC之DispatcherServlet解析》
从图中可以看出DispatcherServlet初始的上下文加载的bean是对SpringMVC有效的bean,例如HandlerMapping,HandlerAdapter,ViewResoler,ContextLoaderListener初始的上下文是对整个应用程序共享的bean,例如DAO,serivce等。最后DispatcherServlet初始的上下文会继承ContextLoaderListener的上下文。

二. DispatcherServlet的初始化

           我们启动服务器的时候,程序只是初始化了DispatcherServlet的上下文,还没有对DispatcherServlet进行初始化,就像我们去买台式机,买回来的放在一个箱子里的有显示器,机箱,主板,显卡等,要想用电脑我们还得将电脑组装起来,初始化就好比要将我们配置文件中配置了的HandlerMapping,HandlerAdapter,ViewResoler组装起来,然后我们才可以真正的使用。下面看一下DispatcherServlet初始化的源码

[java] 
view plain
 copy

  1. protected void initStrategies(ApplicationContext context) {  
  2.         initMultipartResolver(context);  
  3.         initLocaleResolver(context);  
  4.         initThemeResolver(context);  
  5.         initHandlerMappings(context);  
  6.         initHandlerAdapters(context);  
  7.         initHandlerExceptionResolvers(context);  
  8.         initRequestToViewNameTranslator(context);  
  9.         initViewResolvers(context);  
  10.     }  

我们这里就详细看一下DispatcherServlet对HandlerMapping的初始化

[java] 
view plain
 copy

  1. /** 
  2.      * Initialize the HandlerMappings used by this class. 
  3.      * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace, 
  4.      * we default to BeanNameUrlHandlerMapping. 
  5.      */  
  6.     private void initHandlerMappings(ApplicationContext context) {  
  7.         this.handlerMappings = null;  
  8.   
  9.         if (this.detectAllHandlerMappings) {  
  10.             // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.  
  11.             Map<String, HandlerMapping> matchingBeans =  
  12.                     BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.classtruefalse);  
  13.             if (!matchingBeans.isEmpty()) {  
  14.                 this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());  
  15.                 // We keep HandlerMappings in sorted order.  
  16.                 OrderComparator.sort(this.handlerMappings);  
  17.             }  
  18.         }  
  19.         else {  
  20.             try {  
  21.                 HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);  
  22.                 this.handlerMappings = Collections.singletonList(hm);  
  23.             }  
  24.             catch (NoSuchBeanDefinitionException ex) {  
  25.                 // Ignore, we’ll add a default HandlerMapping later.  
  26.             }  
  27.         }  

我们可以看出DispatcherServlet对HandlerMapping的初始化就是从WebApplicationContext上下文中取出已经配置了的HandlerMapping,当我们没有在springController.xml中配置HandlerMapping的时候,它会取出默认的HandlerMapping即BeanNameUrlHandlerMapping 。来看一下在springController.xml中对HandlerMapping配置

[html] 
view plain
 copy

  1. <bean class=“org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping”/>   
  2. <bean id=“urlMapping” class=“org.springframework.web.servlet.handler.SimpleUrlHandlerMapping”>  
  3.       <property name=“mappings”>  
  4.             <props>  
  5.                   <prop key=“/springMVC.d”>HelloWorld</prop>  
  6.             </props>  
  7.       </property>  
  8. </bean>  

在这里对HandlerMapping进行了两个两个声明,分别是BeanNameUrlHandlerMapping和SimpleUrlHandlerMapping,当HandlerMapping初始化的时候,Dispatcher的handlerMappings属性就包含有这两个对象。同理当对HandlerAdapter和ViewResolver进行初始化的时候也是这样的,如果在配置文件中配置了,Dispatcher就会在WebApplicationContext上下文中取出这些配置对象,如果没有就会取默认的。这里就不再一一叙述了。

三. 
DispatcherServlet的工作流程

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

《Spring MVC之DispatcherServlet解析》

看一下Dispatcher中的doDispatcher源码

[java] 
view plain
 copy

  1. //前端控制器分派方法  
  2. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  3.         HttpServletRequest processedRequest = request;  
  4.         HandlerExecutionChain mappedHandler = null;  
  5.         int interceptorIndex = –1;  
  6.   
  7.         try {  
  8.             ModelAndView mv;  
  9.             boolean errorView = false;  
  10.   
  11.             try {  
  12.                    //检查是否是请求是否是multipart(如文件上传),如果是将通过MultipartResolver解析  
  13.                 processedRequest = checkMultipart(request);  
  14.                    //步骤2、请求到处理器(页面控制器)的映射,通过HandlerMapping进行映射  
  15.                 mappedHandler = getHandler(processedRequest, false);  
  16.                 if (mappedHandler == null || mappedHandler.getHandler() == null) {  
  17.                     noHandlerFound(processedRequest, response);  
  18.                     return;  
  19.                 }  
  20.                    //步骤3、处理器适配,即将我们的处理器包装成相应的适配器(从而支持多种类型的处理器)  
  21.                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  
  22.   
  23.                   // 304 Not Modified缓存支持  
  24.                 //此处省略具体代码  
  25.   
  26.                 // 执行处理器相关的拦截器的预处理(HandlerInterceptor.preHandle)  
  27.                 //此处省略具体代码  
  28.   
  29.                 // 步骤4、由适配器执行处理器(调用处理器相应功能处理方法)  
  30.                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
  31.   
  32.                 // Do we need view name translation?  
  33.                 if (mv != null && !mv.hasView()) {  
  34.                     mv.setViewName(getDefaultViewName(request));  
  35.                 }  
  36.   
  37.                 // 执行处理器相关的拦截器的后处理(HandlerInterceptor.postHandle)  
  38.                 //此处省略具体代码  
  39.             }  
  40.             catch (ModelAndViewDefiningException ex) {  
  41.                 logger.debug(“ModelAndViewDefiningException encountered”, ex);  
  42.                 mv = ex.getModelAndView();  
  43.             }  
  44.             catch (Exception ex) {  
  45.                 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);  
  46.                 mv = processHandlerException(processedRequest, response, handler, ex);  
  47.                 errorView = (mv != null);  
  48.             }  
  49.   
  50.             //步骤5 步骤6、解析视图并进行视图的渲染  
  51. //步骤5 由ViewResolver解析View(viewResolver.resolveViewName(viewName, locale))  
  52. //步骤6 视图在渲染时会把Model传入(view.render(mv.getModelInternal(), request, response);)  
  53.             if (mv != null && !mv.wasCleared()) {  
  54.                 render(mv, processedRequest, response);  
  55.                 if (errorView) {  
  56.                     WebUtils.clearErrorRequestAttributes(request);  
  57.                 }  
  58.             }  
  59.             else {  
  60.                 if (logger.isDebugEnabled()) {  
  61.                     logger.debug(“Null ModelAndView returned to DispatcherServlet with name ‘” + getServletName() +  
  62.                             “‘: assuming HandlerAdapter completed request handling”);  
  63.                 }  
  64.             }  
  65.   
  66.             // 执行处理器相关的拦截器的完成后处理(HandlerInterceptor.afterCompletion)  
  67.             //此处省略具体代码  
  68.   
  69.   
  70.         catch (Exception ex) {  
  71.             // Trigger after-completion for thrown exception.  
  72.             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);  
  73.             throw ex;  
  74.         }  
  75.         catch (Error err) {  
  76.             ServletException ex = new NestedServletException(“Handler processing failed”, err);  
  77.             // Trigger after-completion for thrown exception.  
  78.             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);  
  79.             throw ex;  
  80.         }  
  81.   
  82.         finally {  
  83.             // Clean up any resources used by a multipart request.  
  84.             if (processedRequest != request) {  
  85.                 cleanupMultipart(processedRequest);  
  86.             }  
  87.         }  
  88.     }  

核心架构的具体流程步骤如下:

1、  首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;

2、  DispatcherServlet——>HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象,通过这种策略模式,很容易添加新的映射策略;

3、  DispatcherServlet——>HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;

4、  HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);

5、  ModelAndView的逻辑视图名——> ViewResolver, ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;

6、  View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;

7、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。

 

此处我们只是讲了核心流程,没有考虑拦截器、本地解析、文件上传解析等。

    原文作者:Spring MVC
    原文地址: https://blog.csdn.net/a18716374124/article/details/78397552
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞