WEB請(qǐng)求處理五:MVC框架請(qǐng)求處理

0 系列目錄#

為開發(fā)團(tuán)隊(duì)選擇一款優(yōu)秀的MVC框架是件難事兒,在眾多可行的方案中決擇需要很高的經(jīng)驗(yàn)和水平。你的一個(gè)決定會(huì)影響團(tuán)隊(duì)未來(lái)的幾年。要考慮方面太多:

  1. 簡(jiǎn)單易用,以提高開發(fā)效率。使小部分的精力在框架上,大部分的精力放在業(yè)務(wù)上。
  2. 性能優(yōu)秀,這是一個(gè)最能吸引眼球的話題。
  3. 盡量使用大眾的框架(避免使用小眾的、私有的框架),新招聘來(lái)的開發(fā)人員有一些這方面技術(shù)積累,減低人員流動(dòng)再適應(yīng)的影響。

如果你還在為這件事件發(fā)愁,本文最適合你了。選擇Spring MVC吧。本篇文章主要以Spring MVC為例,基本上市面上的MVC框架處理流程都大同小異,主流程都基本相同。Spring MVC比較成熟、使用也比較廣泛,設(shè)計(jì)理念也非常棒,所以,本文重點(diǎn)以Spring MVC講解為主。

先說(shuō)下在整個(gè)WEB請(qǐng)求處理過(guò)程中,本篇文章講述的是哪個(gè)流程模塊。為直觀明了,先上一張圖,紅色部分為本章所述模塊:

WEB請(qǐng)求處理過(guò)程

1 Spring MVC核心類與接口#

先來(lái)了解一下,幾個(gè)重要的接口與類。現(xiàn)在不知道他們是干什么的沒(méi)關(guān)系,先混個(gè)臉熟,為以后認(rèn)識(shí)他們打個(gè)基礎(chǔ)。

  1. DispatcherServlet -- 前置控制器
DispatcherServlet -- 前置控制器

Spring提供的前置控制器,所有的請(qǐng)求都經(jīng)過(guò)它來(lái)統(tǒng)一分發(fā)。在DispatcherServlet將請(qǐng)求分發(fā)給Spring Controller之前,需要借助于Spring提供的HandlerMapping定位到具體的Controller。

  1. HandlerMapping接口 -- 處理請(qǐng)求的映射

HandlerMapping接口的實(shí)現(xiàn)類:

SimpleUrlHandlerMapping 通過(guò)配置文件,把一個(gè)URL映射到Controller類上;

DefaultAnnotationHandlerMapping 通過(guò)注解,把一個(gè)URL映射到Controller類上;

HandlerMapping接口 -- 處理請(qǐng)求的映射
  1. HandlerAdapter接口 -- 處理請(qǐng)求的映射

AnnotationMethodHandlerAdapter類,通過(guò)注解,把一個(gè)URL映射到Controller類的方法上;

HandlerAdapter接口 -- 處理請(qǐng)求的映射
  1. Controller接口 -- 控制器

由于我們使用了@Controller注解,添加了@Controller注解的類就可以擔(dān)任控制器(Action)的職責(zé),所以我們并沒(méi)有用到這個(gè)接口。

Controller接口 -- 控制器

需要為并發(fā)用戶處理請(qǐng)求,因此實(shí)現(xiàn)Controller接口時(shí),必須保證線程安全并可重用。Controller將處理用戶請(qǐng)求,這和Struts Action扮演的角色是一致的。一旦Controller處理完用戶請(qǐng)求,則返回ModelAndView對(duì)象給DispatcherServlet前置控制器,ModelAndView中包含了模型(Model)和視圖(View)。從宏觀角度考慮,DispatcherServlet是整個(gè)Web應(yīng)用的控制器;從微觀考慮,Controller是單個(gè)Http請(qǐng)求處理過(guò)程中的控制器,而ModelAndView是Http請(qǐng)求過(guò)程中返回的模型(Model)和視圖(View)

  1. HandlerInterceptor接口 -- 攔截器

無(wú)圖,我們自己實(shí)現(xiàn)這個(gè)接口,來(lái)完成攔截的器的工作。

  1. ViewResolver接口的實(shí)現(xiàn)類

Spring提供的視圖解析器(ViewResolver)在Web應(yīng)用中查找View對(duì)象,從而將相應(yīng)結(jié)果渲染給客戶。

UrlBasedViewResolver類,通過(guò)配置文件,把一個(gè)視圖名交給到一個(gè)View來(lái)處理;

InternalResourceViewResolver類,比上面的類,加入了JSTL的支持;

ViewResolver接口的實(shí)現(xiàn)類
  1. View接口

JstlView類

View接口
  1. LocalResolver接口
LocalResolver接口
  1. HandlerExceptionResolver接口 -- 異常處理

SimpleMappingExceptionResolver實(shí)現(xiàn)類

HandlerExceptionResolver接口 -- 異常處理
  1. ModelAndView類

2 DispatcherServlet初始化過(guò)程#

當(dāng)Web項(xiàng)目啟動(dòng)時(shí),做初始化工作,所以我們大部分是配置在Web.xml里面,這樣項(xiàng)目一啟動(dòng),就會(huì)執(zhí)行相關(guān)的初始化工作,下面是Web.xml代碼:

    <servlet>  
        <servlet-name>SpringMVCDispatcher</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <init-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>  
                classpath:spring-mvc.xml  
            </param-value>  
        </init-param>  
        <load-on-startup>1</load-on-startup>  
    </servlet>  
    <servlet-mapping>  
        <servlet-name>SpringMVCDispatcher</servlet-name>  
        <url-pattern>*.jhtml</url-pattern>  
    </servlet-mapping>

    <servlet>  
        <servlet-name>HessianDispatcher</servlet-name>  
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
        <init-param>  
            <param-name>contextConfigLocation</param-name>  
            <param-value>  
                classpath:hessian-service.xml  
            </param-value>  
        </init-param>  
        <load-on-startup>1</load-on-startup>  
    </servlet>  
    <servlet-mapping>  
        <servlet-name>HessianDispatcher</servlet-name>  
        <url-pattern>/service/*</url-pattern>  
    </servlet-mapping>  

這里配置了兩個(gè)DispatcherServlet,后面會(huì)介紹到,怎么各自處理,有各自的上下文容器。

最早我們開始學(xué)習(xí)MVC結(jié)構(gòu)時(shí),就是學(xué)servlet,都是繼承了HttpServlet 類,也是重新了init、doGet、doPost、destroy方法,我這邊就不介紹HttpServlet類,DispatcherServlet也是間接最高繼承了HttpServlet,類繼承結(jié)構(gòu)如圖所示:

DispatcherServlet類繼承結(jié)構(gòu)

我們先了解項(xiàng)目啟動(dòng),DispatcherServlet和父類都做了什么事情呢?這是我們本節(jié)的重點(diǎn)。

2.1 第一步:HttpServletBean類init()方法##

DispatcherServlet繼承了FrameworkServlet,F(xiàn)rameworkServlet繼承了HttpServletBean,HttpServletBean繼承了HttpServlet 類,而HttpServletBean類有一個(gè)入口點(diǎn)就是重寫了init方法,如圖所示:

HttpServletBean重寫了init方法

init方法做了什么事情呢?接下來(lái)我們來(lái)具體分析:

  1. PropertyValues:獲取Web.xml里面的servlet的init-param(web.xml)
/** 
   * Create new ServletConfigPropertyValues. 
   * @param config ServletConfig we'll use to take PropertyValues from 
   * @param requiredProperties set of property names we need, where 
   * we can't accept default values 
   * @throws ServletException if any required properties are missing 
   */  
public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)  
      throws ServletException {  
      Enumeration en = config.getInitParameterNames();  
      while (en.hasMoreElements()) {  
          String property = (String) en.nextElement();  
          Object value = config.getInitParameter(property);  
          addPropertyValue(new PropertyValue(property, value));  
      }   
}  

說(shuō)明:

Enumeration en = config.getInitParameterNames();

獲取了init-param的param-name和param-value值,并設(shè)置配置參數(shù)到PropertyValue,如圖所示:

獲取init-param參數(shù)并設(shè)置到PropertyValue
  1. BeanWrapper:封裝了bean的行為,提供了設(shè)置和獲取屬性值,它有對(duì)應(yīng)的BeanWrapperImpl,如圖所示:
封裝了bean的行為,提供了設(shè)置和獲取屬性值
  1. ResourceLoader:接口僅有一個(gè)getResource(String location)的方法,可以根據(jù)一個(gè)資源地址加載文件資源。classpath:這種方式指定SpringMVC框架bean配置文件的來(lái)源。

ResourcePatternResolver擴(kuò)展了ResourceLoader接口,獲取資源:

ResourcePatternResolver resolver =new PathMatchingResourcePatternResolver();
resolver.getResources("classpath:spring-mvc.xml");

總結(jié):

  1. 先通過(guò)PropertyValues獲取web.xml文件init-param的參數(shù)值;
  2. 然后通過(guò)ResourceLoader讀取.xml配置信息;
  3. BeanWrapper對(duì)配置的標(biāo)簽進(jìn)行解析和將系統(tǒng)默認(rèn)的bean的各種屬性設(shè)置到對(duì)應(yīng)的bean屬性;

2.2 第二步:FrameworkServlet類initServletBean()方法##

在init方法里還調(diào)用了initServletBean();這里面又實(shí)現(xiàn)了什么。HttpServletBean在為子類提供模版、讓子類根據(jù)自己的需求實(shí)現(xiàn)不同的ServletBean的初始化工作,這邊是由HttpServletBean的子類FrameworkServlet來(lái)實(shí)現(xiàn)的,如圖所示:

initServletBean()在子類FrameworkServlet的實(shí)現(xiàn)

this.webApplicationContext = initWebApplicationContext();初始化SpringMVC 上下文容器,servlet的上下文容器是ServletContext。對(duì)initWebApplicationContext();進(jìn)行跟蹤,查看這個(gè)方法做了什么事情?

initServletBean()在子類FrameworkServlet的實(shí)現(xiàn)-1

initServletBean()在子類FrameworkServlet的實(shí)現(xiàn)-2
 protected WebApplicationContext initWebApplicationContext() {  
        //1. 根節(jié)點(diǎn)上下文,是通過(guò)ContextLoaderListener加載的,服務(wù)器啟動(dòng)時(shí),最先加載的  
        WebApplicationContext rootContext =  
                WebApplicationContextUtils.getWebApplicationContext(getServletContext());  
        if (this.webApplicationContext != null) {
            wac = this.webApplicationContext;  
            if (wac instanceof ConfigurableWebApplicationContext) {  
                ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;  
                //2. 要對(duì)上下文設(shè)置父上下文和ID等  
                if (!cwac.isActive()) {  
                    if (cwac.getParent() == null) {  
                        cwac.setParent(rootContext);  
                    }  
                    configureAndRefreshWebApplicationContext(cwac);  
                }  
            }  
        }  
        //3. Servlet不是由編程式注冊(cè)到容器中,查找servletContext中已經(jīng)注冊(cè)的WebApplicationContext作為上下文  
        if (wac == null) {  
            wac = findWebApplicationContext();  
        }  
        //4. 如果都沒(méi)找到時(shí),就用根上下文就創(chuàng)建一個(gè)上下文有ID  
        if (wac == null) {  
            wac = createWebApplicationContext(rootContext);  
        }  
        //5. 在上下文關(guān)閉的情況下調(diào)用refesh可啟動(dòng)應(yīng)用上下文,在已經(jīng)啟動(dòng)的狀態(tài)下,調(diào)用refresh則清除緩存并重新裝載配置信息  
        if (!this.refreshEventReceived) {  
            onRefresh(wac);  
        }  
       //6. 對(duì)不同的請(qǐng)求對(duì)應(yīng)的DispatherServlet有不同的WebApplicationContext、并且都存放在ServletContext中  
        if (this.publishContext) {  
            String attrName = getServletContextAttributeName();  
            getServletContext().setAttribute(attrName, wac);  
            if (this.logger.isDebugEnabled()) {  
                this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +  
                        "' as ServletContext attribute with name [" + attrName + "]");  
            }  
        }  
  
        return wac;  
  }  

總結(jié):

initWebApplicationContext初始化上下文,并作為值放到了ServletContext里,因?yàn)椴煌腄ispatherServlet有對(duì)應(yīng)的各自的上下文,而且上下文有設(shè)置父上下文和id屬性等。上下文項(xiàng)目啟動(dòng)時(shí)會(huì)調(diào)用createWebApplicationContext()方法,如下圖所示。

上下文項(xiàng)目啟動(dòng)時(shí)會(huì)調(diào)用createWebApplicationContext()方法

然后會(huì)初始化,設(shè)置父上下文和id屬性等,如圖所示:

初始化,設(shè)置父上下文和id屬性
  1. 獲取ContextLoaderListener加載的上下文并標(biāo)示為根上下文,如果是編程式傳入,沒(méi)初始化,以根節(jié)點(diǎn)為父上文,并設(shè)置ID等信息,然后初始化。

  2. 如果上下文是為空的,Servlet不是由編程式注冊(cè)到容器中,查找servletContext中已經(jīng)注冊(cè)的WebApplicationContext作為上下文,如果都沒(méi)找到時(shí),就用根上下文就創(chuàng)建一個(gè)上下文ID,在上下文關(guān)閉的情況下調(diào)用refesh可啟動(dòng)應(yīng)用上下文,在已經(jīng)啟動(dòng)的狀態(tài)下,調(diào)用refresh則清除緩存并重新裝載配置信息。

  3. 對(duì)不同的請(qǐng)求對(duì)應(yīng)的DispatherServlet有不同的WebApplicationContext、并且都存放在ServletContext中。以servlet-name為key保存在severtContext,前面有配置了兩個(gè)DispatherServlet,都有各自的上下文容器,如下圖所示。

不同請(qǐng)求對(duì)應(yīng)不同DispatherServlet和WebApplicationContext、并且都存放在ServletContext

2.3 第三步:DispatcherServlet類onRefresh()方法##

回調(diào)函數(shù)onRefresh還做了一些提供了SpringMVC各種編程元素的初始化工作, onRefresh在為子類提供模版、讓子類根據(jù)自己的需求實(shí)現(xiàn)不同的onRefresh的初始化工作,這邊是由FrameworkServlet的子類DispatcherServlet來(lái)實(shí)現(xiàn)的,如圖所示:

回調(diào)函數(shù)onRefresh()在子類DispatcherServlet的實(shí)現(xiàn)

我們現(xiàn)在來(lái)分析SpringMVC組件進(jìn)行初始化,并封裝到DispatcherServlet中:

// 初始化上傳文件解析器
initMultipartResolver(context);
// 初始化本地解析器
initLocaleResolver(context);
// 初始化主題解析器
initThemeResolver(context);
// 初始化映射處理器
initHandlerMappings(context);
// 初始化適配器處理器
initHandlerAdapters(context);
// 初始化異常處理器
initHandlerExceptionResolvers(context);
// 初始化請(qǐng)求到視圖名翻譯器
initRequestToViewNameTranslator(context);
// 初始化視圖解析器
initViewResolvers(context);
  1. initHandlerMappings初始化映射處理器:
private void initHandlerMappings(ApplicationContext context) {  
     this.handlerMappings = null;  
     if (this.detectAllHandlerMappings) {  
         Map<String, HandlerMapping> matchingBeans =  
               BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);  
         if (!matchingBeans.isEmpty()) {  
             this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());  
             // We keep HandlerMappings in sorted order.  
             OrderComparator.sort(this.handlerMappings);  
         }  
     }  
     else {  
         try {  
             HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);  
             this.handlerMappings = Collections.singletonList(hm);  
         }  
         catch (NoSuchBeanDefinitionException ex) {  
         }  
     }  
     if (this.handlerMappings == null) {  
         this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);  
         if (logger.isDebugEnabled()) {  
             logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");  
         }  
     }  
}  

說(shuō)明:

1)detectAllHandlerMappings默認(rèn)是true,根據(jù)類型匹配機(jī)制查找上下文及父容器上下文中所有類型為HandlerMapping的bean,將它們作為該類型組件,并放到ArrayList<HandlerMapping>中。

2)detectAllHandlerMappings如果是false時(shí),查找key為handlerMapping的HandlerMapping類型的bean為該類組件,而且 Collections.singletonList只有一個(gè)元素的集合。

3)List<HandlerMapping> 是為空的話,使用BeanNameUrlHandleMapping實(shí)現(xiàn)類創(chuàng)建該類的組件。

initHandlerMapping會(huì)初始化了handlerMethods請(qǐng)求方法的映射,HandlerMapping是處理請(qǐng)求的映射的如圖所示:

initHandlerMapping初始化handlerMethods請(qǐng)求方法的映射
  1. initHandlerAdapters適配器處理器:
private void initHandlerAdapters(ApplicationContext context) {  
       this.handlerAdapters = null;  
 
       if (this.detectAllHandlerAdapters) {  
           Map<String, HandlerAdapter> matchingBeans =  
                   BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);  
           if (!matchingBeans.isEmpty()) {  
               this.handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());  
               // We keep HandlerAdapters in sorted order.  
               OrderComparator.sort(this.handlerAdapters);  
           }  
       }  
       else {  
           try {  
               HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);  
               this.handlerAdapters = Collections.singletonList(ha);  
           }  
           catch (NoSuchBeanDefinitionException ex) {  
                           }  
       }  
       if (this.handlerAdapters == null) {  
           this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);  
           if (logger.isDebugEnabled()) {  
               logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default");  
           }  
       }  
 }

initHandlerAdapters適配器處理器初始化原理跟initHandlerMappings初始化映射處理器一樣。

當(dāng)DispatcherServlet初始化后,就會(huì)自動(dòng)掃描上下文的bean,根據(jù)名稱或者類型匹配的機(jī)制查找自定義的組件,找不到則使用DispatcherServlet。Properties定義了默認(rèn)的組件。

HttpServletBean、FrameworkServlet、DispatcherServlet三個(gè)不同的類層次,SpringMVC對(duì)三個(gè)以抽象和繼承來(lái)實(shí)現(xiàn)不用的功能,分工合作,實(shí)現(xiàn)了解耦的設(shè)計(jì)原則。

我們?cè)诨仡櫼幌拢髯宰隽耸裁词虑椋?/strong>

  1. HttpServletBean 主要做一些初始化的工作,將web.xml中配置的參數(shù)設(shè)置到Servlet中。比如servlet標(biāo)簽的子標(biāo)簽init-param標(biāo)簽中配置的參數(shù)。
  2. FrameworkServlet 將Servlet與Spring容器上下文關(guān)聯(lián)。其實(shí)也就是初始化FrameworkServlet的屬性webApplicationContext,這個(gè)屬性代表SpringMVC上下文,它有個(gè)父類上下文,既web.xml中配置的ContextLoaderListener監(jiān)聽器初始化的容器上下文。
  3. DispatcherServlet 初始化各個(gè)功能的實(shí)現(xiàn)類。比如異常處理、視圖處理、請(qǐng)求映射處理等。

3 DispatcherServlet處理請(qǐng)求過(guò)程#

Spring MVC請(qǐng)求處理流程圖

Spring MVC請(qǐng)求處理時(shí)序圖

Spring MVC請(qǐng)求處理流程描述:

  1. 用戶向服務(wù)器發(fā)送請(qǐng)求,請(qǐng)求被Spring 前置控制Servelt DispatcherServlet捕獲
  2. DispatcherServlet對(duì)請(qǐng)求URL進(jìn)行解析,得到請(qǐng)求資源標(biāo)識(shí)符(URI)。然后根據(jù)該URI,調(diào)用HandlerMapping獲得該Handler配置的所有相關(guān)的對(duì)象(包括Handler對(duì)象以及Handler對(duì)象對(duì)應(yīng)的攔截器),最后以HandlerExecutionChain對(duì)象的形式返回
  3. DispatcherServlet 根據(jù)請(qǐng)求獲得Handler,選擇一個(gè)合適的HandlerAdapter。(附注:如果成功獲得HandlerAdapter后,此時(shí)將開始執(zhí)行攔截器的preHandler(...)方法)
  4. 提取Request中的模型數(shù)據(jù),填充Handler入?yún)ⅲ_始執(zhí)行Handler(Controller)。 在填充Handler的入?yún)⑦^(guò)程中,根據(jù)你的配置,Spring將幫你做一些額外的工作:
    HttpMessageConveter:將請(qǐng)求消息(如Json、xml等數(shù)據(jù))轉(zhuǎn)換成一個(gè)對(duì)象,將對(duì)象轉(zhuǎn)換為指定的響應(yīng)信息;
    數(shù)據(jù)轉(zhuǎn)換:對(duì)請(qǐng)求消息進(jìn)行數(shù)據(jù)轉(zhuǎn)換。如String轉(zhuǎn)換成Integer、Double等;
    數(shù)據(jù)根式化:對(duì)請(qǐng)求消息進(jìn)行數(shù)據(jù)格式化。 如將字符串轉(zhuǎn)換成格式化數(shù)字或格式化日期等;
    數(shù)據(jù)驗(yàn)證: 驗(yàn)證數(shù)據(jù)的有效性(長(zhǎng)度、格式等),驗(yàn)證結(jié)果存儲(chǔ)到BindingResult或Error中;
  5. Handler執(zhí)行完成后,向DispatcherServlet 返回一個(gè)ModelAndView對(duì)象
  6. 根據(jù)返回的ModelAndView,選擇一個(gè)適合的ViewResolver(必須是已經(jīng)注冊(cè)到Spring容器中的ViewResolver)返回給DispatcherServlet;
  7. ViewResolver結(jié)合Model和View,來(lái)渲染視圖
  8. 將渲染結(jié)果返回給客戶端;

HttpServlet提供了service方法用于處理請(qǐng)求,service使用了模板設(shè)計(jì)模式,在內(nèi)部對(duì)于http get方法會(huì)調(diào)用doGet方法,http post方法調(diào)用doPost方法:

HttpServlet提供了service方法用于處理請(qǐng)求

進(jìn)入processRequest方法看下:

processRequest方法-1

processRequest方法-2

其中注冊(cè)的監(jiān)聽器類型為ApplicationListener接口類型。繼續(xù)看DispatcherServlet覆寫的doService方法:
DispatcherServlet覆寫的doService方法

最終就是doDispatch方法。doDispatch方法功能簡(jiǎn)單描述一下:

  1. 首先根據(jù)請(qǐng)求的路徑找到HandlerMethod(帶有Method反射屬性,也就是對(duì)應(yīng)Controller中的方法);
  2. 然后匹配路徑對(duì)應(yīng)的攔截器,有了HandlerMethod和攔截器構(gòu)造個(gè)HandlerExecutionChain對(duì)象。HandlerExecutionChain對(duì)象的獲取是通過(guò)HandlerMapping接口提供的方法中得到;
  3. 有了HandlerExecutionChain之后,通過(guò)HandlerAdapter對(duì)象進(jìn)行處理得到ModelAndView對(duì)象
  4. HandlerMethod內(nèi)部handle的時(shí)候,使用各種HandlerMethodArgumentResolver實(shí)現(xiàn)類處理HandlerMethod的參數(shù),使用各種HandlerMethodReturnValueHandler實(shí)現(xiàn)類處理返回值
  5. 最終返回值被處理成ModelAndView對(duì)象,這期間發(fā)生的異常會(huì)被HandlerExceptionResolver接口實(shí)現(xiàn)類進(jìn)行處理;
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);
                // 根據(jù)請(qǐng)求,獲取HandlerExecutionChain對(duì)象
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 獲取HandlerAdapter對(duì)象
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (logger.isDebugEnabled()) {
                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
                    }
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // 實(shí)際執(zhí)行行handle,返回ModelAndView對(duì)象
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                applyDefaultViewName(request, mv);
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            }
            catch (Exception ex) {
                dispatchException = ex;
            }
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        }
        catch (Exception ex) {
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        }
        catch (Error err) {
            triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
        }
        finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            }
            else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容