Struts1 - 源碼學習 - ActionServlet

Struts 源碼新版本爲 struts-1.3.8-src.zip ( 12-Mar-2007 00:06 )

學習筆記使用struts-1.3.5-src.zip 的源碼,

下載地址:http://archive.apache.org/dist/struts/source/

1.  在web.xml中通過下面定義把所有的*.do交給ActionServlet處理

 

<!-- Standard Action Servlet Configuration (with debugging) -->

<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,

          /WEB-INF/struts-config-Wildcard.xml

       </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>

 

2.  下面研究一下struts的源碼,由於servlet設置了load-on-startup,所以tomcat啓動時會加載ActionServlet,也就是會執行ActionServlet中的init()方法,Struts 的初始化實現就是在這裏實現的

 

注: 由於servlet的生命週期爲 web容器加載和實例化類/init()初始化/service()請求處理/destroy()四個階段,而init()方法在tomcat啓動後只執行一次,所以如果想在tomcat啓動後用debug模式查看ActionServlet中init()方法的執行,可以把上面的<load-on-startup>2</load-on-startup>註釋掉就可以了(不過真正開發時還是需要的)。

 

3.  ActionServlet中定義了一些常量,如下:

    // 默認的struts配置文件爲/WEB-INF/struts-config.xml

    protected String config = "/WEB-INF/struts-config.xml"; // initOther(); initModuleConfig ();

 

    // 默認的鏈(定義了一個按順序執行的處理流程)配置文件

protected String chainConfig = "org/apache/struts/chain/chain-config.xml";

// initChain();

 

protected Digester configDigester = null; // initModuleConfig ();

// 如convertNull 爲true,Java包裝類(如java.lang.Integer)的初始值爲null

protected boolean convertNull = false; // initOther();

 

protected MessageResources internal = null; // initInternal();

// 默認的 struts-core-1.3.5.jar 中資源文件爲ActionResources.properties

protected String internalName = "org.apache.struts.action.ActionResources";

// initInternal();

 

// 一些文檔類型定義,用來驗證相應的配置文件如struts-config.xml是否正確

    protected String[] registrations =

        {

            "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN",

            "/org/apache/struts/resources/struts-config_1_0.dtd",

            "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN",

            "/org/apache/struts/resources/struts-config_1_1.dtd",

            "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN",

            "/org/apache/struts/resources/struts-config_1_2.dtd",

            "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN",

            "/org/apache/struts/resources/struts-config_1_3.dtd",

            "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",

            "/org/apache/struts/resources/web-app_2_3.dtd"

        }; // initServlet();

    protected String servletMapping = null; // initServlet();

protected String servletName = null; // initServlet();

 

4.  ActionServlet 中的init()方法執行流程如下

 

struts_src.jpg 

① 內部資源文件 ActionResources.properties 的初始化 initInternal();

protected MessageResources internal = null; // initInternal();

protected String internalName = "org.apache.struts.action.ActionResources"; // initInternal();

 

// initInternal 方法中通過下面得到一個MessageResources對象

internal = MessageResources.getMessageResources(internalName);

此資源文件主要包括一些消息信息的定義,具體可參考org.apache.struts.action下的ActionResources.properties文件

 

在MessageResources.java中的getMessageResources方法,

        if (defaultFactory == null) {

            defaultFactory = MessageResourcesFactory.createFactory(); //

        }

        return defaultFactory.createResources(config); // 傳入internalName //

 

MessageResourcesFactory.createFactory() 所做的工作:

protected static transient Class clazz = null;

protected static String factoryClass =

        "org.apache.struts.util.PropertyMessageResourcesFactory";

clazz = RequestUtils.applicationClass(factoryClass);

而RequestUtils.applicationClass通過classLoader加載一個

org.apache.struts.util.PropertyMessageResourcesFactory

 

defaultFactory.createResources(config) 所做的工作:

this.factory = factory;

("org.apache.struts.util.PropertyMessageResourcesFactory")

this.config = config;("org.apache.struts.action.ActionResources")

this.returnNull = returnNull;(true)

PropertyMessageResourcesFactory extends MessageResourcesFactory

返回一個MessageResources對象

 

 

② 調用 initOther(); 從web.xml中加載ActionServlet的初始化參數,包括config/ convertNull

protected String config = "/WEB-INF/struts-config.xml"; // initOther();

protected boolean convertNull = false; // initOther();

 

// 得到web.xml中"config"參數

String value;

value = getServletConfig().getInitParameter("config");

if (value != null) {

    config = value;

}

    <servlet>

        <servlet-name>action</servlet-name>

        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

        <init-param>

            <param-name>config</param-name>   <!-- 得到"config"參數-->

            <param-value>/WEB-INF/struts-config.xml</param-value>

        </init-param>

        <init-param>

            <param-name>convertNull</param-name>   <!-- 得到"convertNull"參數-->

            <param-value>true</param-value>

        </init-param>

        .......

    </servlet>   

 

// 獲得convertNull的值(true/yes/on/y/1)

getServletConfig().getInitParameter("convertNull");

如果這個參數的值爲 true (true/yes/on/y/1) , 數值型(BigDecimal/BigInteger/Boolean/Byte/Character/Double/Float/Integer/Long/Short)的Java 包裝類(比如java.lang.Integer)的初始值爲null,而非0。缺省值[false]

 

使其初始值爲null的方法如下:

// 將所有的轉換器註銷掉

ConvertUtils.deregister();

// 爲指定類型clazz註冊轉換器converter

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

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

.......

 

注: ConvertUtils 用法如下

deregister () deregister (java.lang.Class clazz)

註銷轉換器,前者將所有的轉換器註銷掉,後者只註銷對應於clazz的轉換器register( Converter  converter, java.lang.Class clazz)

爲指定類型clazz註冊轉換器converter。如果clazz已經存在一個對應的轉換器,那麼converter覆蓋原來的轉換器。

 

 

③ 調用 initServlet(); 從web.xml中加載ActionServlet的初始化參數如servlet-name,加載DTD文件並把其放入HashMap緩存,讀取並解析web.xml的內容

// Remember our servlet name

getServletConfig().getServletName();

<servlet>

        <servlet-name>action</servlet-name>

        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

        <init-param>

            <param-name>config</param-name>   <!-- 得到"config"參數-->

            <param-value>/WEB-INF/struts-config.xml</param-value>

        </init-param>

        <init-param>

            <param-name>convertNull</param-name>   <!-- 得到"convertNull"參數-->

            <param-value>true</param-value>

        </init-param>

        .......

    </servlet>

 

// Prepare a Digester to scan the web application deployment descriptor

Digester digester = new Digester();

// 把當前的 ActionServlet 對象放入到解析堆棧中

digester.push(this);

// 指明要考慮命名空間

digester.setNamespaceAware(true);

// 缺省值[false] ,解析器只是檢查XML是否格式良好(well formed)

digester.setValidating(false);

 

// Register our local copy of the DTDs that we can find

// struts 可使用 struts-core-1.3.5.jar 中的DTD中來處理struts配置文件,這樣可適用於那些沒有連接到internet的應用環境

        for (int i = 0; i < registrations.length; i += 2) {

            URL url = this.getClass().getResource(registrations[i + 1]);

 

            if (url != null) {

                // 讀取DTD文件並把其放入 HashMap 緩存

                digester.register(registrations[i], url.toString());

            }

        }

 

/************************************************************

// 一些文檔類型定義,用來驗證相應的配置文件如struts-config.xml是否正確

    protected String[] registrations =

        {

            "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN",

            "/org/apache/struts/resources/struts-config_1_0.dtd",

            "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN",

            "/org/apache/struts/resources/struts-config_1_1.dtd",

            "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN",

            "/org/apache/struts/resources/struts-config_1_2.dtd",

            "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN",

            "/org/apache/struts/resources/struts-config_1_3.dtd",

            "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",

            "/org/apache/struts/resources/web-app_2_3.dtd"

        }; // initServlet();

************************************************************/

 

// Configure the processing rules that we need

// 運行時,digester 就會調用 ActionServlet中的 addServletMapping() 方法,並傳入兩個參數

digester.addCallMethod("web-app/servlet-mapping", "addServletMapping", 2);

digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);

digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);

得到

<servlet-mapping>

    <servlet-name>action</servlet-name>

    <url-pattern>*.do</url-pattern>

</servlet-mapping>

 

/************************************************************

// 來判斷當前 servlet 名稱是否爲正在運行的 servlet 名稱,如是,就把 url-pattern 作爲 servletMapping

public void addServletMapping(String servletName, String urlPattern) {

        if (servletName == null) {

            return;

        }

        if (servletName.equals(this.servletName)) {

            if (log.isDebugEnabled()) {

                log.debug("Process servletName=" + servletName

                    + ", urlPattern=" + urlPattern);

            }

            this.servletMapping = urlPattern;

        }

}

************************************************************/

 

// 讀取配置文件web.xml的內容

InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml");

// 如找不到/WEB-INF/web.xml文件,則報錯

if (input == null) {

    log.error(internal.getMessage("configWebXml"));

    throw new ServletException(internal.getMessage("configWebXml"));

}

/************************************************************

// 報錯信息定義在org\apache\struts\action\ActionResources.properties

configWebXml=The /WEB-INF/web.xml was not found.

************************************************************/

 

// 解析input流文件,每讀到一個節點元素就觸發一個事件

digester.parse(input);

 

注: Digester 是一個基於 DOM 的 SAX 實現的類,它是事件觸發的,可以將XML文件轉換爲任意的Java對象,支持規則的對任意XML文檔的處理。原先是struts項目的一部分,後因其通用性而劃歸Commons子項目。

 

// 把servletMapping存儲到servletContext中,屬性名爲Globals.SERVLET_KEY " org.apache.struts.action.SERVLET_MAPPING "

if (servletMapping != null) {

    getServletContext().setAttribute(Globals.SERVLET_KEY,servletMapping);

}

④ 調用 initChain(); 讀取web.xml中命令鏈文件初始值chainConfig

protected String chainConfig = "org/apache/struts/chain/chain-config.xml";

// initChain();

 

// 如沒有chainConfig參數,則使用默認 "org/apache/struts/chain/chain-config.xml"

String value;

value = getServletConfig().getInitParameter("chainConfig");

if (value != null) {

    chainConfig = value;

}

 

ConfigParser parser = new ConfigParser();

List urls = splitAndResolvePaths(chainConfig);

URL resource;

 

// chainConfig 替換了原來傳統的在 RequestProcessor 類中執行的 HTTP 請求處理

for (Iterator i = urls.iterator(); i.hasNext();) {

     resource = (URL) i.next();

     log.info("Loading chain catalog from " + resource);

     parser.parse(resource);

}

 

/************************************************************

// org.apache.struts.action. RequestProcessor .java 的process方法中,一些方法如

processLocale (request, response);

processContent (request, response);

processNoCache (request, response);

.......

"org/apache/struts/chain/chain-config.xml" 中下列配置所取代

<command

          className="org.apache.struts.chain.commands.servlet.SelectLocale"/>

<command

          className="org.apache.struts.chain.commands.servlet.SetContentType"/>

<command

          className="org.apache.struts.chain.commands.servlet.RequestNoCache"/>

.......

好處是充分降低了代碼內部方法與方法之間的耦合度

************************************************************/

 

 

/之間

// 把servlet對象存儲到servletContext中,屬性名爲Globals.ACTION_SERVLET_KEY

" org.apache.struts.action.ACTION_SERVLET "

getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);

 

 

⑤ 調用 initModuleConfigFactory(); initModuleConfig("", config); 創建 ModuleConfig 對象。Struts中的MessageResource、PlugIn、數據源等,都是通過ModuleConfig來實現的。

// 初始化ModuleConfig配置工廠

initModuleConfigFactory(); //

// 由配置工廠實例化一個ModuleConfig的對象

ModuleConfig moduleConfig = initModuleConfig("", config); //

 

initModuleConfigFactory(); 所做的工作:

// 得到web.xml中"configFactory"參數,如果找不到,則使用 默認工廠

String configFactory = getServletConfig().getInitParameter("configFactory");

if (configFactory != null) {

     ModuleConfigFactory.setFactoryClass(configFactory);

}

<servlet>

        <servlet-name>action</servlet-name>

        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

        <init-param>

        <param-name>configFactory</param-name>   <!-- 得到"configFactory"參數-->

      <param-value>com.lively.base.webapp.UserModuleConfigFactory</param-value>

        </init-param>

        .......

    </servlet>

 

在ModuleConfigFactory.java中的setFactoryClass方法,

public static void setFactoryClass(String factoryClass) {

        ModuleConfigFactory.factoryClass = factoryClass;

        ModuleConfigFactory.clazz = null;

}

其中

protected static Class clazz = null;

protected static String factoryClass =

        "org.apache.struts.config.impl.DefaultModuleConfigFactory";

 

initModuleConfig ("", config); 所做的工作:

// Parse the configuration for this module

ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory(); //

ModuleConfig config = factoryObject.createModuleConfig(prefix); //

 

ModuleConfigFactory.createFactory(); 方法中:

protected static Class clazz = null;

protected static String factoryClass =

        "org.apache.struts.config.impl.DefaultModuleConfigFactory";

if (clazz == null) {

     clazz = RequestUtils.applicationClass(factoryClass);

}

factory = (ModuleConfigFactory) clazz.newInstance();

 

而RequestUtils.applicationClass通過classLoader加載一個

org.apache.struts.config.impl.DefaultModuleConfigFactory

 

ModuleConfig config = factoryObject.createModuleConfig(prefix); 方法中:

// 通過new ModuleConfigImpl(prefix);設置一些變量的初始值,在 initModuleConfig ("", config); 方法的最後會 把ModuleConfig對象放置到servletContext中 (參見

protected String prefix = null;

protected HashMap actionConfigs = null;

protected List actionConfigList = null;

protected String actionFormBeanClass = "org.apache.struts.action.ActionFormBean";

protected String actionMappingClass = "org.apache.struts.action.ActionMapping";

protected String actionForwardClass = "org.apache.struts.action.ActionForward";

protected boolean configured = false;

protected ControllerConfig controllerConfig = null;

protected HashMap exceptions = null;

protected HashMap formBeans = null;

protected HashMap forwards = null;

protected HashMap messageResources = null;

protected ArrayList plugIns = null;

public ModuleConfigImpl(String prefix) {

        super();

        this.prefix = prefix;

        this.actionConfigs = new HashMap();

        this.actionConfigList = new ArrayList();

        this.actionFormBeanClass = "org.apache.struts.action.ActionFormBean";

        this.actionMappingClass = "org.apache.struts.action.ActionMapping";

        this.actionForwardClass = "org.apache.struts.action.ActionForward";

        this.configured = false;

        this.controllerConfig = null;

        this.exceptions = new HashMap();

        this.formBeans = new HashMap();

        this.forwards = new HashMap();

        this.messageResources = new HashMap();

        this.plugIns = new ArrayList();

}

 

protected String config = "/WEB-INF/struts-config.xml"; // initOther(); initModuleConfig ();

protected Digester configDigester = null; // initModuleConfig ();

 

// 初始化Digester

Digester digester = initConfigDigester();

 

initConfigDigester(); 所做的工作:

// Create a new Digester instance with standard capabilities

configDigester = new Digester();

configDigester.setNamespaceAware(true);

configDigester.setValidating(this.isValidating());

configDigester.setUseContextClassLoader(true);

// 解析struts配置文件之前,首先添加默認的解析規則

configDigester.addRuleSet(new ConfigRuleSet());

for (int i = 0; i < registrations.length; i += 2) {

    URL url = this.getClass().getResource(registrations[i + 1]);

    if (url != null) {

         configDigester.register(registrations[i], url.toString());

    }

}

// 通過getServletConfig().getInitParameter("rulesets");從web.xml中讀取用戶自定義的解析規則(用","分開的org.apache.commons.digester.RuleSet列表)

this.addRuleSets();

.......

 

/* 循環struts配置文件(用","分開的多個struts配置文件)並解析, parseModuleConfigFile 執行之後可以 從struts-config.xml等配置文件中得到 actionConfigs/actionConfigListexceptions formBeansforwardsmessageResourcesplugIns等的配置,並把得到的所有值封裝到對象ModuleConfig對象(config)中 */

List urls = splitAndResolvePaths(paths);

URL url;

for (Iterator i = urls.iterator(); i.hasNext();) {

    url = (URL) i.next();

    digester.push(config);

    this.parseModuleConfigFile(digester, url);

}

 

// 把config存儲到servletContext中 ,屬性名爲Globals.MODULE_KEY

" org.apache.struts.action.MODULE "

getServletContext().setAttribute(Globals.MODULE_KEY

            + config.getPrefix(), config);

 

 

⑥ 用戶資源文件的初始化 initModuleMessageResources(moduleConfig);

在上面第步的中我們已經創建了ModuleConfig對象並在從struts-config.xml等配置文件中得到得到一些配置且封裝到ModuleConfig對象中,所以在下面可以直接使用initModuleMessageResources(moduleConfig);

 

initModuleMessageResources(moduleConfig); 所做的工作:

/* 從moduleConfig中讀取所有的資源文件(包括 ① 內部資源文件 利用Digester讀取的struts配置文件指定的用戶資源文件) */

MessageResourcesConfig[] mrcs = config.findMessageResourcesConfigs();

注: 此時moduleConfig中默認只含有① 內部資源文件ActionResources.properties

 

// 把resources(包括 ① 內部資源文件 ⑥ 用戶資源文件 )存儲到servletContext// 屬性名爲mrcs[i].getKey() + config.getPrefix()

        for (int i = 0; i < mrcs.length; i++) {

            if ((mrcs[i].getFactory() == null)

                || (mrcs[i].getParameter() == null)) {

                continue;

            }

 

            if (log.isDebugEnabled()) {

                log.debug("Initializing module path '" + config.getPrefix()

                    + "' message resources from '" + mrcs[i].getParameter()

                    + "'");

            }

 

            String factory = mrcs[i].getFactory();

 

            MessageResourcesFactory.setFactoryClass(factory);

 

            MessageResourcesFactory factoryObject =

                MessageResourcesFactory.createFactory();

 

            factoryObject.setConfig(mrcs[i]);

 

            MessageResources resources =

                factoryObject.createResources(mrcs[i].getParameter());

 

            resources.setReturnNull(mrcs[i].getNull());

            resources.setEscape(mrcs[i].isEscape());

            getServletContext().setAttribute(mrcs[i].getKey()

                + config.getPrefix(), resources);

        }

 

 

⑦ 用戶插件的初始化 initModulePlugIns(moduleConfig);

在上面第步的中我們已經創建了ModuleConfig對象並在從struts-config.xml等配置文件中得到得到一些配置且封裝到ModuleConfig對象中,所以在下面可以直接使用initModulePlugIns(ModuleConfig config);

 

initModulePlugIns(moduleConfig); 所做的工作:

// 從moduleConfig中讀取所有的插件文件

PlugInConfig[] plugInConfigs = config.findPlugInConfigs();

PlugIn[] plugIns = new PlugIn[plugInConfigs.length];

 

// 把所有plugIns存儲到servletContext中

// 屬性名爲Globals.PLUG_INS_KEY + config.getPrefix()

.......

getServletContext().setAttribute(Globals.PLUG_INS_KEY

            + config.getPrefix(), plugIns);

.......

 

⑧ 把struts配置文件中的其他配置 存儲到servletContext,包括

initModuleFormBeans(moduleConfig);

initModuleForwards(moduleConfig);

initModuleExceptionConfigs(moduleConfig);

initModuleActions(moduleConfig);

 

⑨ 調用 moduleConfig.freeze(); 固定組件配置

/* 使ModuleConfig中的 actionConfigs/actionConfigListexceptions formBeansforwardsmessageResourcesplugIns等的配置等變得不可改變 */

moduleConfig.freeze();

 

 

⑩ 解析以"config/"開頭的其他struts配置文件

// 遍歷web.xml中servletConfig配置的 initParameterNames

// 如發現以" config/ " 開始的parameter,則根據此值初始化其它的ModuleConfig

Enumeration names = getServletConfig().getInitParameterNames();

while (names.hasMoreElements()) {

    String name = (String) names.nextElement();

    if (!name.startsWith(configPrefix)) {

        continue;

    }

    String prefix = name.substring(configPrefixLength);

    moduleConfig =

        initModuleConfig(prefix,

            getServletConfig().getInitParameter(name));

    initModuleMessageResources(moduleConfig);

    initModulePlugIns(moduleConfig);

    initModuleFormBeans(moduleConfig);

    initModuleForwards(moduleConfig);

    initModuleExceptionConfigs(moduleConfig);

    initModuleActions(moduleConfig);

    moduleConfig.freeze();

}

<servlet>

        <servlet-name>action</servlet-name>

        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

        <init-param>

            <param-name>config</param-name>   <!-- 得到"config"參數-->

            <param-value>/WEB-INF/struts-config.xml</param-value>

        </init-param>

        <init-param>

            <param-name>config/admin</param-name> <!-- 得到"config/admin"參數-->

            <param-value>/WEB-INF/struts-config-admin.xml</param-value>

        </init-param>

        .......

    </servlet>

 

 

⑩ 解析以"config/"開頭的其他struts配置文件之後ActionServletinit()方法還需要做

// 初始化其他模塊的前綴

this.initModulePrefixes(this.getServletContext());

 

initModulePrefixes(this.getServletContext()); 所做的工作:

/* 把其他模塊prefixes存儲到servletContext中,屬性名爲

Globals.MODULE_PREFIXES_KEY " org.apache.struts.globals.MODULE_PREFIXES " */

context.setAttribute(Globals.MODULE_PREFIXES_KEY, prefixes);

 

// 設置configDigester = null,釋放內存

this.destroyConfigDigester();

 

 

至此struts 的核心類ActionServletinit()方法完成servlet的初始化工作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章