1. 程式人生 > >SpringMVC原始碼--DispatcherServlet的載入和初始化

SpringMVC原始碼--DispatcherServlet的載入和初始化

    之前的文章介紹了Spring的初始化過程,Spring Web應用初始化依賴於Web容器的初始化,這在之前已經提到過了。那麼初始化完成後,SpringMVC又是怎樣發揮作用的呢?首先呢,Web容器初始化完成後會繼續讀取web.xml裡的節點,我們知道DispatcherServlet是配置在web.xml中的,所以DispatcherServlet是由Web容器主動去載入的。那麼,DispatcherServlet到底是個什麼呢?

     首先,DispatcherServlet仍是一個Servlet容器。在Web容器初始化的過程中,會建立每個Servlet容器的的例項物件。我們都知道,在Tomcat容器中,維護了一個執行緒池,每當有一個客戶請求抵達伺服器的時候,就分配一個執行緒來處理該請求。同時,Servlet是單例多執行緒的,也就是多個請求共用一個Servlet容器。所以呢,DispatcherServlet的

完整定義是:是一個具有唯一性的增強型Servlet容器。 明確了這一點,我們就可以想象的到,DispatcherServlet具有servlet的大多數特性,包括Servlet的完整生命週期。

   按照慣例,我們需要先找到DispatcherServlet的初始化入口,開啟原始碼,它的繼承關係如下:

       

   可以看到,DispatcherServlet擁有一系列的父類,頂級父類是GenericServlet,它是Servlet介面的最簡單實現。Web容器會直接呼叫DispatcherServlet擁有的init()方法,其實現在HttpServletBean類中,如下:

public final void init() throws ServletException {

        // Set bean properties from init parameters.         PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);         if (!pvs.isEmpty()) {             try {                 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;             }         }       

 initServletBean();    }

   我們需要重點關注initServletBean()方法的呼叫,它的具體實現在FrameworkServlet類中。    protected final void initServletBean() throws ServletException {             this.webApplicationContext = initWebApplicationContext();             initFrameworkServlet();     }

  protected WebApplicationContext initWebApplicationContext() {

       //ROOT上下文(ContextLoaderListener方式載入的)        WebApplicationContext rootContext =                 WebApplicationContextUtils.getWebApplicationContext(getServletContext());         WebApplicationContext wac = null;

        if (this.webApplicationContext != null) {             wac = this.webApplicationContext;

            // 1、在建立該Servlet注入的上下文            if (wac instanceof ConfigurableWebApplicationContext) {                 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;                 if (!cwac.isActive()) {                     if (cwac.getParent() == null) {                         cwac.setParent(rootContext);                     }                     configureAndRefreshWebApplicationContext(cwac);                 }             }         }         if (wac == null) {

            //2、查詢已經繫結的上下文            wac = findWebApplicationContext();         }         if (wac == null) {

            //3、如果沒有找到相應的上下文,並指定父親為ContextLoaderListener            wac = createWebApplicationContext(rootContext);         }

        if (!this.refreshEventReceived) {

            //4、重新整理上下文(執行一些初始化)onRefresh(wac);         }

        if (this.publishContext) {             String attrName = getServletContextAttributeName();             getServletContext().setAttribute(attrName, wac);         }

        return wac;     }

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {         if (ObjectUtils.identityToString(wac).equals(wac.getId())) {             // The application context id is still set to its original default value             // -> assign a more useful id based on available information             if (this.contextId != null) {                 wac.setId(this.contextId);             }             else {                 // Generate default id...                 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +                         ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());             }         }

        wac.setServletContext(getServletContext());         wac.setServletConfig(getServletConfig());         wac.setNamespace(getNamespace());         wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

        ConfigurableEnvironment env = wac.getEnvironment();         if (env instanceof ConfigurableWebEnvironment) {             ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());         }

        postProcessWebApplicationContext(wac);         applyInitializers(wac);         wac.refresh();     }

initWebApplicationContext()方法不用多說,獲取通過ContextLoader初始化完成的Spring容器的上下文,然後以此為父容器來載入DispatcherServlet。如果獲取失敗,則分情況間接建立一個上下文。追蹤程式碼可以發現最後都是間接呼叫了configureAndRefreshWebApplicationContext()方法來重新整理容器。重新整理容器可以類比Spring容器的啟動過程,實現過程在AbstractApplicationContext類中。容器重新整理工作後就準備完成了,通過onRefresh()方法來做一些初始化工作,它的實現在DispatcherServlet類中,如下:

   protected void onRefresh(ApplicationContext context) {         initStrategies(context);     }

protected void initStrategies(ApplicationContext context) {    

initMultipartResolver(context);//檔案上傳解析,如果請求型別是multipart將通過MultipartResolver進行檔案上傳解析 

initLocaleResolver(context);//本地化解析 

initThemeResolver(context);//主題解析 

initHandlerMappings(context);//通過HandlerMapping,將請求對映到處理器 

initHandlerAdapters(context);//通過HandlerAdapter支援多種型別的處理器 

initHandlerExceptionResolvers(context);//如果執行過程中遇到異常,將交給HandlerExceptionResolver來解析 

initRequestToViewNameTranslator(context);//直接解析請求到檢視名 

initViewResolvers(context);//通過viewResolver解析邏輯檢視到具體檢視實現 

initFlashMapManager(context);//flash對映管理器     }

 以上,就是所有內容了,可以看出這是一種很巧妙的設計。