1. 程式人生 > >層層遞進Struts1(四)之預載入ActionServlet

層層遞進Struts1(四)之預載入ActionServlet

Struts的執行相當於分為兩個階段,預載入階段和執行階段,預載入階段是指在Tomcat啟動之時就開始執行的內容,而此時我們並未真正進入跳轉邏輯,這篇部落格我們來分析一下預載入階段。

配置檔案

還記得web.xml中關於Struts的Servlet是如何配置的嗎?

    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <init-param>
      <param-name>debug</param-name>
      <param-value>2</param-value>
    </init-param>
    <init-param>
      <param-name>detail</param-name>
      <param-value>2</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>
  <!-- Standard Action Servlet Mapping -->
  <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
其中<load-on-startup>2</load-on-startup>指的就是在Tomcat啟動之時即執行,實際只要數字大於0,意思就是Tomcat啟動即執行,為了進入除錯,我們把這裡改為0。
<load-on-startup>0</load-on-startup>

主函式init()

經過除錯發現,當執行完此函式,Struts的預載入階段即結束,所以我們主要來看這個函式即可。

    /**
     * <p>Initialize this servlet.  Most of the processing has been factored into
     * support methods so that you can override particular functionality at a
     * fairly granular level.</p>
     *
     * @exception ServletException if we cannot configure ourselves correctly
     */
    public void init() throws ServletException {

        // Wraps the entire initialization in a try/catch to better handle
        // unexpected exceptions and errors to provide better feedback
        // to the developer
        try {
            initInternal();
            initOther();
            initServlet();
    
            getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
            initModuleConfigFactory();
            // Initialize modules as needed
            ModuleConfig moduleConfig = initModuleConfig("", config);
            initModuleMessageResources(moduleConfig);
            initModuleDataSources(moduleConfig);
            initModulePlugIns(moduleConfig);
            moduleConfig.freeze();
    
            Enumeration names = getServletConfig().getInitParameterNames();
            while (names.hasMoreElements()) {
                String name = (String) names.nextElement();
                if (!name.startsWith("config/")) {
                    continue;
                }
                String prefix = name.substring(6);
                moduleConfig = initModuleConfig
                    (prefix, getServletConfig().getInitParameter(name));
                initModuleMessageResources(moduleConfig);
                initModuleDataSources(moduleConfig);
                initModulePlugIns(moduleConfig);
                moduleConfig.freeze();
            }
    
            this.initModulePrefixes(this.getServletContext());
    
            this.destroyConfigDigester();
        } catch (UnavailableException ex) {
            throw ex;
        } catch (Throwable t) {

            // The follow error message is not retrieved from internal message
            // resources as they may not have been able to have been 
            // initialized
            log.error("Unable to initialize Struts ActionServlet due to an "
                + "unexpected exception or error thrown, so marking the "
                + "servlet as unavailable.  Most likely, this is due to an "
                + "incorrect or missing library dependency.", t);
            throw new UnavailableException(t.getMessage());
        }    
    }

很明顯init函式中執行有其它函式,為了簡明扼要,這些函式的程式碼不再完全照搬,碰到重要或是經典的程式碼片段我會放進來。

呼叫函式

initInternal()

此函式的作用是,初始化內部檔案ActionResources.properties。

initOther()

此函式的作用是:指定struts-config.xml的路徑,預設為/WEB-INF/struts-config.xml;同時註冊ConvertUtils的資料型別,例如:

            ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
            ConvertUtils.register(new BigIntegerConverter(null), BigInteger.class);

initServlet()

此函式的作用是:讀取struts在web.xml中的配置資訊,例如do/action、url等;獲取web程式部署資訊。

與ModuleConfig相關

getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
            initModuleConfigFactory();
            // Initialize modules as needed
            ModuleConfig moduleConfig = initModuleConfig("", config);
            initModuleMessageResources(moduleConfig);
            initModuleDataSources(moduleConfig);
            initModulePlugIns(moduleConfig);
            moduleConfig.freeze();
    
            Enumeration names = getServletConfig().getInitParameterNames();
            while (names.hasMoreElements()) {
                String name = (String) names.nextElement();
                if (!name.startsWith("config/")) {
                    continue;
                }
                String prefix = name.substring(6);
                moduleConfig = initModuleConfig
                    (prefix, getServletConfig().getInitParameter(name));
                initModuleMessageResources(moduleConfig);
                initModuleDataSources(moduleConfig);
                initModulePlugIns(moduleConfig);
                moduleConfig.freeze();
            }

這段程式碼的作用是,將web.xml中的Struts配置檔案初始化為ModuleConfig物件,將struts-config中的MessageResource、DataSource、PlugIn也都初始化為物件,儲存到ServletContext中。不同的是,while迴圈外操作的是web.xml裡 ActionServlet配置中config初始化引數指定的Struts配置檔案;while迴圈內操作的是web.xml裡 ActionServlet配置中以"config/"開頭的初始化引數指定的Struts配置檔案,這樣的區別造成前者會產生沒有字首的ModuleConfig物件到ServletConfig中,後者會產生帶字首的ModuleConfig物件放到ServletContext中,字首為“config/”後的字串。

initModulePrefixes(this.getServletContext());

此函式的作用是將迴圈產生的所有字首生成一個String陣列,放到ServletContext中。

destroyConfigDigester();

此函式的作用是將ActionServlet類的configDigester的值改為為null。

執行順序圖

這篇文章主要是分析了在Tomcat啟動時struts的預處理情況,在struts執行邏輯處理時是如何處理的,請看我的部落格《層層遞進Struts1(五)之處理流程》。