SpringMVC——DispatcherServlet的IoC容器(Web應用的IoC容器的子容器)創建過程


在上一篇《Spring——Web應用中的IoC容器創建(WebApplicationContext根應用上下文的創建過程)》中說到了Web應用中的IoC容器創建過程.這一篇主要講SpringMVC的核心DispatcherServlet.

從web.xml中簡要回顧一下WebApplicationContext根應用上下文的創建過程.具體過程詳見上篇博客.

1   <!--WebApplicationContext配置參數-->
2   <context-param>
3       <param-name>contextConfigLocation</param-name>
4       <param-value>classpath*:applicationContext.xml</param-value>
5   </context-param>
6    <!--注冊ContextLoaderListener,加載根應用上下文-->
7   <listener>
8       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
9   </listener>

DispatcherServlet實際上就是一個Servlet所以它在web.xml中的配置和普通的servlet沒有區別.

 1     <!--注冊DispatcherServlet,加載應用上下文-->
 2     <servlet>
 3         <servlet-name>springmvc</servlet-name>
 4         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
 5         <init-param>
 6             <param-name>contextConfigLocation</param-name>
 7             <param-value>classpath*:spring-servlet.xml</param-value>    <!--若不顯示添加配置文件路徑,則會默認加載servlt-name的名字+"-servlet.xml"-->
 8         </init-param>
 9         <load-on-startup>1</load-on-startup>
10     </servlet>
11     <!--servlet映射-->
12     <servlet-mapping>
13         <servlet-name>springmvc</servlet-name>
14         <url-pattern>/</url-pattern>
15     </servlet-mapping>

用過原生Servlet寫過web都知道自定義的Servlet需要繼承HttpServlet類實現doPost和doGet方法.DispatcherServlet類的主要繼承關系如下:

在這篇博客中從不細講Servlet,從HttpServletBean的開始講起.


 

DispatcherServlet是什么?它為什么在SpringMVC中起到核心作用?原因很簡單:所有來自客戶端的請求都會經過DispatcherServlet,由DispatcherServlet將不同的請求分發至不同的Controller,所以DispatcherServlet是一個前置控制器起的是分發來自客戶端請求的作用.根據不同的配置會接收不同的請求,這在web.xml中servlet映射中可體現.如果配置的是"/"則是所有請求都會經過DispatcherServlet,但通常不會這么做,比如一些靜態資源就不必經過DispatcherServlet.

首先大致了解一下Servlet.Web容器接收到來自客戶端不同類型(post,get等)的時候,實際上是所有的請求都是訪問Servlet接口的service方法,在HttpServlet抽象類中實現了service方法,在service方法中判斷是哪種具體的請求,再將不同的請求分發至不同的處理方法.

用原生的Servlet編寫的Web應用通常是繼承HttpServlet方法,重寫doGet和doPost方法.由於DispatcherServlet在SpringMVC中責任重大,作為一個前端控制器,所有的Web請求都需要通過它處理,進行轉發,匹配,數據處理后,並轉由頁面進行展現.可以看到DispatcherServlet並沒有直接繼承HttpServlet,而是HttpServletBean.在Servlet初始化過程中,Servlet的init方法會被調用,而Servlet提供的API中init方法沒有做任何事,也就是說我們可以通過重寫init方法來實現我們自己的業務邏輯.

//GenericServlet.java
public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}
...
public void init() throws ServletException {

}
...

在HttpServletBean重寫了init方法,並且不能被其子類所重寫.

//HttpServletBean.java
public
final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters.從初始化參數中設置bean屬性 try { PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); } catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } // Let subclasses do whatever initialization they like.調用子類的initServletBean進行具體的初始化 initServletBean(); if (logger.isDebugEnabled()) { logger.debug("Servlet '" + getServletName() + "' configured successfully"); } } ... //具體的初始化交由子類去完成,即FrameworkServlet protected void initServletBean() throws ServletException { }

順着初始化這條線我們來到FrameworkServlet.照貓畫虎,它重寫了父類的initServletBean,但同樣將它置為不能被其子類所重寫.

//FrameServlet.java
protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
    if (this.logger.isInfoEnabled()) {
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
        this.webApplicationContext = initWebApplicationContext();   //在這里不是初始化Spring根應用上下文(Web應用的IoC容器),而是初始化SpringMVC的Servlet上下文創建自己所持有的IoC容器.如果沒有則調用createWebApplicationContext方法進行創建.並將根應用上下文作為它的雙親上下文
        initFrameworkServlet(); //此方法也沒有給出具體實現,再其子類DispatcherServlet也沒有對它重寫.
    }
    catch (ServletException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }
    catch (RuntimeException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }

    if (this.logger.isInfoEnabled()) {
        long elapsedTime = System.currentTimeMillis() - startTime;
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                elapsedTime + " ms");
    }
}
...
//在所有的bean配置參數和WebApplicationContext被加載后會調用此方法,默認實現為空,它的子類可以重寫此方法來實現需要的初始化操作.子類DispatcherServlet並沒有重寫.
protected void initFrameworkServlet() throws ServletException {
}

簡單回顧一下整個初始化過程(一個不規范的圖)


 

以上部分只是簡要的說明了一下DispatcherServlet的IoC容器初始化過程,但還是沒有說明一個請求是如何在DispatcherServlet做到分發到不同Controller的.

在DispatcherServlet類中有一個initStrategies方法,在這個方法中初始化整個SpringMVC框架的初始化,包括其中的http請求映射關系:

//DispatcherServlet.java
protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    initHandlerMappings(context);    //這里就是為http請求找到相應的Controller控制器
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}

容易得知,initStrategies方法是在onRefresh方法中調用的,FrameworkServlet沒有對onRefresh做任何有意義的實現,而是交由它的子類DispatcherServlet去完成.在FramworkServlet的initWebApplicationContext方法中完成了對它的調用.所以再次回到FramworkServlet的initWebApplicationContext方法,只截取其中一段:

//FrameworkServlet.java
protected WebApplicationContext initWebApplicationContext() {
    ......
    if (this.webApplicationContext != null) {
        ......
        configureAndRefreshWebApplicationContext(cwac);     //在此方法中調用的onRefresh
        ......
    }
    if (wac == null) {
        wac = createWebApplicationContext(rootContext);     //此方法中最后也是調用的configureAndRefreshWebApplicationContext方法
    }

    if (!this.refreshEventReceived) {
        onRefresh(wac);
    }
    ......
    return wac;
}

更為具體的SpringMVC處理http分發請求,我們再下一篇中再來詳細講解initStrategies中的initHandlerMappings.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM