Struts1 源码深度解析

1.   服务器启动,加载Web.xml文件。该配置告诉我们所有*.do的客户端请求将由ActionServlet类来处理,所以我们着重研究ActionServlet类。

 

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

    <load-on-startup>2</load-on-startup>

  </servlet>

 

<servlet-mapping>

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

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

  </servlet-mapping>

 

ActionServlet是个典型得Servlet类,所以它有几个Servlet典型方法,init(),destroy(),doGet(),doPost()方法。而init()方法是一个初始方法,在ActionServlet类doGet(),doPost ()方法执行之前,将调用init()方法。如下图所示。

initInternal()方法 中internal=MessageResources.getMessageResources(internalName); 初始化MessageResources 实现原理是工厂模式,首先得到一个工厂MessageResourcesFactory defaultFactory = MessageResourcesFactory.createFactory()该方法得到PropertyMessageResourcesFactory工厂类,该工厂类的createResources()

        PropertyMessageResources messageResources =

               new PropertyMessageResources(this, config, this.returnNull);

        String mode = null;

        if (getConfig() != null) {

            mode = getConfig().getProperty("mode");

        }

        messageResources.setMode(mode);

        return messageResources;

    将factory类 和config=”org.apache.struts.action.ActionResources”到PropertyMessageResources中。

initOther()方法主要功能是如果在web.xml 读取struts-config.xml文件具体位置。

register一些转化器

initServlet()方法主要功能是利用Digester对web.xml解析然后调用addServletMapping 方法将servlet-name元素的text和url-pattern的text读出来作为addServletMapping的参数传进去,并将url-pattern 赋给ActionServlet属性servletMapping,并将servletMapping存到servletContext中

initChain()方法主要功能是解析在web.xml中chainConfig的text值,如果没有chainConfig配置,则读取默认的配置文件chain-config.xml,该配置文件是在struts1.3版本新增对Http请求的处理方式配置文件,在后续的文字会详细介绍到。

initModuleConfigFactory()方法主要功能是解析在web.xml中configFactory的text值。如果configFactory有配置,则将设置ModuleConfigFactory中得factoryClass值,否则默认得为DefaultModuleConfigFactory。该方法其实宗旨是让开发人员自己开发出ModuleConfigFactory,从而得到自己所需要的ModuleConfig类。

ModuleConfig moduleConfig = initModuleConfig("", config);方法主要功能是解析struts-config.xml。首先,得到继承ModuleConfigFactory的实现类,如果在initModuleConfigFactory()中能设置factoryClass属性,则能生成客户化得factory,否则得到得是默认得DefaultModuleConfigFactory类,该工厂得到 ModuleConfigImpl

类。然后调用initConfigDigester()该方法为解析配置文件做准备,初始化Digest类

主要有设置nameSpace,validating,rule,dtd等。其中configDigester.addRuleSet(new ConfigRuleSet());中的ConfigRuleSet()是为了解析配置文件得规则类,在addRuleSet()方法中将调用ConfigRuleSet规则类的addRuleInstances()方法。该方法为解析xml添加所需要的继承了Rule接口的类到digester中 。

1.digester.addRule("struts-config/action-mappings",new SetActionMappingClassRule());中得SetActionMappingClassRule()类查看action-mappings 元素是否有type属性,如有则set到ModuleConfigImpl中的actionMappingClass.      2.digester.addFactoryCreate("struts-config/action-mappings/action", new ActionMappingFactory(cl))的ActionMappingFactory()工厂类查看action元素是否有className 属性,如果有则根据这个className的Value值创建类,否则从ModuleConfigImpl中得到actionMappingClass属性值来创建类,默认得创建ActionMapping类。

3.digester.addSetProperties("struts-config/action-mappings/action");方法将new SetPropertiesRule()这个Rule类加入到Digest中,而SetPropertiesRule类主要作用是将action元素中得属性值set到ActionMapping对应的属性中去。如

<action input="/adminlogin.jsp"name="adminLoginForm"path="/adminLogin" scope="request"type="com.laoer.bbscs.web.action.AdminLogin" validate="true" />中得input的Value就set到ActionMapping中的input属性中去,其他得依次类推。

4.digester.addSetNext("struts-config/action-mappings/action", "addActionConfig","org.apache.struts.config.ActionConfig");方法的主要功能是将调用digester类push第一个push进的那个类的addActionConfig方法,并有参数为ActionConfig类型 为在这里这个类是ModuleConfigImpl,同时传进去参数为ActionMapping类

5.digester.addRule("struts-config/action-mappings/action/set-property", new BaseConfigSetPropertyRule());而BaseConfigSetPropertyRule类主要作用是如果action元素下有子元素set-property并且没有拥有属性Key 则将set-property元素的属性 property =”XXX”中的XXX为ActionMapping类的属性,Value=”YYYY”为XXX得Value值 set进ActionMapping中。如果拥有Key属性的话则存到Properties中去。

6.digester.addObjectCreate("struts-config/action-mappings/action/exception","org.apache.struts.config.ExceptionConfig", "className");方法主要功能是在元素action中有子元素exception,并以元素exception得属性className中的valueXXXX值为基础生成XXXX类,如果className属性值为空,则默认生ExceptionConfig类。

我们再回到调用ConfigRuleSet类addRuleInstances()方法的得initConfigDigester()方法,接下来是configDigester.register(registrations[i], url.toString())方法,该方法将关于struts-config.xml得DTD文件set到Digester中得entityValidator属性中去。以便以后解析配置文件的时候对文件有效性进行校验。

  在addRuleInstances()方法中主要是这几个方法,其他得处理逻辑与以上方法相似。

 

this.addRuleSets()类则从web.xml中如果配置了rulesets属性则初始化客户化的RuleSet。

好到此为之,Digest得准备工作已经完成,接下来就是应该解析配置文件了。

 

Struts 源码中接下来是看web.xml是否配置了多了struts-congfig.xml文件,如果是则解析多个。解析的代码就在this.parseModuleConfigFile(digester, url)中,该方法就是解析配置文件得关键,其实该方法就一个方法,即    digester.parse(url);但是它得作用确是非凡的,主要工作流程是以下几点

1.   调用Digester的public void startElement(String namespaceURI, String localName,String qName, Attributes list)方法。至于为什么会调用这个方法是因为Digester继承了ContentHandler接口而接口是sax在解析配置文件会调startElement()方法,该方法把上面提及得digester实例中在ConfigRuleSet类中addRuleInstances()保存到digester中得Rule类找出来并且调用其Rule类的begin()方法。现在我们拿3个Rule来解释说明

在ConfigRuleSet中的 addRuleInstances()方法中有

digester.addFactoryCreate("struts-config/form-beans/form-bean", new ActionFormBeanFactory(cl));

digester.addSetProperties("struts-config/form-beans/form-bean");

digester.addSetNext("struts-config/form-beans/form-bean", "addFormBeanConfig","org.apache.struts.config.FormBeanConfig");

在这些方法中把FactoryCreateRule,SetPropertiesRule,SetNextRule三个继承Rule接口得类加入到以struts-config/form-beans/form-bean字符串为Key得Map中去,所以在startElement()中如果执行到form-bean元素的时候,将找出该3个Rule并执行他们得begin()方法。接下来我们为这个3个Rule类做详细解释;

首先是FactoryCreateRule类:该类实际上调用得是新创建得ActionFormBeanFactory()的createObject()方法。如果在form-bean中没有className属性值的话,则将创建默认得org.apache.struts.action.ActionFormBean类。并将该类push到digster中去。

其次是SetPropertiesRule类:该类的begin方法主要功能是将<form-bean name="XXXX" type="YYYYY" />中的name属性和type属性的value做为ActionFormBean类的name 和 type属性注入。

再次是SetNextRule类,该类的执行是在Digest类的endElement()方法中调用的。其中Rule接口中有body()方法,该方法是处理XML 元素是否有Body Text 具体逻辑由继承Rule得子类来处理,但是如果XML元素没有body Text 默认会有个空字符串做完该XML元素得body Text.接着调用Rule类的end()方法,主要功能是调用ModuleConfigImpl的addFormBeanConfig()方法该方法将以xml中得name属性和value属性值以key-value对得方式存到HashMap 的formBeans中去。

以上3个rule类型的介绍完毕,其实针对与struts-config.xml其他元素节点的处理大致上也是如此都有相应的继承Rule,XXXBeanConfig接口的类相应子类进行处理,最终目的就是以key-value的形式存到ModuleConfigImpl的Map中。至于其他类型节点的处理希望读者自己查看源码来理解。

自此关于struts-config.xml解析过程就告一段落。

接下来介绍struts工作原理,

当struts-config.xml解析完毕后,单一个线程访问ActionServlet类的时候,ActionSerVlet就将该线程访问分配到对应的继承Action类中去。其实这是一个典型的中介者模式。

我们发现在ActionServlet的doGet方法和doPost方法都调用的是pocess方法,所以所有得对客户端得控制都在process生成的RequestProcessor类或者其子类中。在struts1.3之前是由 RequestProcessor来处理,而1.3是由ComposableRequestProcessor这个RequestProcessor子类来处理。

关于ComposableRequestProcessor这个类关于request得处理。我不得不在此之前介绍下一个开源项目Commons Chain了这个开源jar包, 它的出现让struts摆脱了硬编码来处理HTTP请求处理,而是使用XML配置的方式来灵活得增加处理的Command。而这些配置的一系列责任链命令则是处理HTTP请求关键。我们先看看这个配置文件

 

 

<define>定义一个lookup 的标签,属性 catalogName指定编目明,name指定责任链名 optional 指定即使没有该子链组也不结束父链。

我们看看 编目名为struts 责任链名为servlet-standard得这个为struts处理HTTP请求的责任链。在介绍之前我先简单介绍下关于这个jar包实现原理,在进一步深入之前我们先看看其类图

 

从类图中我们可以很清楚看到Command 类和Chain类的关系就是组合模式(Composite pattern)[GoF]的例子:Chain不仅由多个Command组成,而且自己也是Command。这使你可以非常简单得将单个命令(Command)替换成由多个命令(Command)组成的链(Chain)。这个由Command对象唯一操作定义的方法代表了一个直接的命令:
public boolean execute(Context context);

所以我们在配置文件里面就可以配置出相当灵活的一个处理链流程,如父链嵌套子链,子链在嵌套子链等等。

而所有对Command调用并处理都来自ChainBase这个类的excute()这个方法,它将调用所有在chain链中的Command类,如果其中一个还回得是true,则跳出链结构。

 

 

而当有命令抛出错误时链就会非正常结束。在Commons Chain中,如果有命令抛出错误,链的执行就会中断。不论是运行时错误(runtime exception)还是应用错误(application exception),都会抛出给链的调用者。但是许多应用都需要对在命令之外定义的错误做明确的处理。Commons Chain提供了Filter接口来满足这个要求。Filter继承了Command,添加了一个名为postprocess的方法。
public boolean postprocess(Context context, Exception exception);
只要Filter的execute方法被调用,不论链的执行过程中是否抛出错误,Commons Chain都将保证Filter的postprocess方法被调用。和servlet的过滤器(filter)相同,Commons Chain的Filter按它们在链中的顺序依次执行。同样,Filter的postprocess方法按倒序执行。你可以使用这个特性实现自己的错误处理。下面是struts中的错误的Filter:

 


Filter 的execute方法按定义的序列调用。然而,它的postprocess方法将在链执行完毕或抛出错误后执行。当一个错误被抛出时, postprocess方法处理完后会返回true,表示错误处理已经完成。链的执行并不会就此结束,但是本质上来说这个错误被捕捉而且不会再向外抛出。如果postprocess方法返回false,那错误会继续向外抛出,然后链就会非正常结束。

<command   className="org.apache.struts.chain.commands.ExceptionCatcher"

        catalogName="struts"

   exceptionCommand="servlet-exception"/>

在chain-config.xml中为struts处理异常的类便是ExceptionCatcher,它在postprocess中是调用另外一个chainName 为servlet-exception的链对异常进行处理。

那我们在看看servlet-exception链对异常的处理方式:首先是ExceptionHandler类的execute方法,在该方法中,如果没有捕捉到异常,则结束处理,如果有异常则从ActionConfig中或者ModuleConfig中找寻ExceptionConfig,这主要取决我们在

Struts-config.xml中如何配置。如果在

<action>

           <exception key="" type=""></exception>

</action>

则可以在ActionConfig中找到ExceptionConfig,

而如果在配置文件中配置全局异常处理机制,则可以在ModuleConfig中找到异常处理ExceptionConfig

<global-exceptions>

  <exception key="" type=""></exception>

  </global-exceptions>           

而ModuleConfig得异常处理机制是大于ActionConfig的。所以如果配置了前者,后者将失去功效。这些配置主要是针对具体异常类型进行处理。比如如果程序运行中抛出了java.io.IOException

那我们就配置成

<global-exceptions>
<exception
key="some.key"
type="java.io.IOException"
handler="com.yourcorp.ExceptionHandler"

path=”/error.jsp”

>
</global-exceptions>

这样对IOException类型的异常类型用ExceptionHandler类进行处理,当然这个类是必须要继承默认的org.apache.struts.action. ExceptionHandler类的,如果我们没有定制特殊要求我们就可以利用默认的类即可,path=”/error.jsp”为异常转发路径。

至于Struts如实现对异常处理的原理机制,感兴趣得读者读者可以查看org.apache.struts.chain.commands.servlet.ExceptionHandler类的源码,我在这里不再累述。

当异常处理经过control完成后,就就应该转发到View 来显示了。

看到Chain-config.xml文件看到一个PerformForward类,该类不仅是对异常进行转发,同时对正常得Action类执行完毕后的转发也起作用。它决定你是转发到jsp,还是另外一个Servlet。

 

 

而关于Commons Chain解析配置文件和struts解析配置文件其实是一致的,都是用Digest,如果大家有兴趣可以查看源码。解析完生成了2个类重要类CatalogBase和ChainBase。 CatalogBase将所有在该Catalog下的chain都包括到一个map中去,ChainBase将在该链下得command或者子链包括到该类得一个数组中去。

 

介绍完Commons Chain的工作原理,我们接下来介绍Struts如何使用Commons Chain。ActionServlet中得getRequestProcessor(ModuleConfig config) 中得到ComposableRequestProcessor类并且初始化它,调用init();这里调用的是子类ComposableRequestProcessor 得init()方法

在该方法中

 

主要是得到需要用到命令责任链结构Chain,而这些命令责任脸都来自XML配置文件

 

 

初始化后接着就是调用process()方法来处理Http 的请求了。 Command.excute(Context)方法将调用一系列的Command类来处理请求。下面我将拿出几个主要Command来具体介绍。

<command  className="org.apache.struts.chain.commands.ExceptionCatcher"

        catalogName="struts"

   exceptionCommand="servlet-exception"/>

ExceptionCatcher类是一个异常处理类,它将执行chain 名称为“servlet-exception”所定义的Command

在chain-config.xml定义可以看到:

  <chain     name="servlet-exception">

      <command      className="org.apache.struts.chain.commands.servlet.ExceptionHandler"/>

      <command>  className="org.apache.struts.chain.commands.servlet.PerformForward"/>

    </chain>

而关于这2个command已经已经在前面详细介绍过,在这里不再累述。

而在 <chain name="process-action"></chain>链中所定义的Command们就是对Request请求进行处理处理的核心类:

1.SelectLocale类主要功能是为选择Locale存到ActionContext

2.SetOriginalURI将servlet path存到ActionConext中

3.RequestNoCache为HttpServletResponse设置

response.setHeader("Pragma", "No-cache");

response.setHeader("Cache-Control","no-cache,no-store,max-age=0");

response.setDateHeader("Expires", 1);

4.SelectAction根据访问的path得到具体的ActionMapping 放到ActionContext中去,

5.CreateActionForm 根据ActionMapping的name 属性 创建ActionForm,这里值得注意的就是ActionForm可以有3种生命周期:request,session,application,所以如果配置成request的话,框架将对每个新request重新生成ActionForm,如果配置为Session的话将在一个session中都会还回同一个ActionForm。(关于Struts如何实现ActionForm3种生命周期的详解,我将在另一篇文章中详细解释)如果ActionForm 都不在上述3种生命周期中,struts将生成新的ActionForm。

6.PopulateActionForm类根据request.getParameter(“args”) 注入到参数到ActionForm中去。

7.ValidateActionForm类为调用ActionForm的validate方法对ActionForm属性值进行有效性校验。

8.SelectInput类

根据<action input="/XXX.jsp" name="" path="" scope="" type=""validate=""/>中配置的input属性值创建一个ActionForward类,并存到ActionContext当中。但是如果在使用客户化的ControllerConfig类同时设置inputForward为true,那么将会根据input属性值去寻找<action>元素内的<forward name=””>name属性值与之相等的ActionForward,如果没有name属性与之匹配则去寻找全局<global-forwards>中name属性与之相等的ActionForward

9.ExecuteCommand类

如果我们在

      <action catalog ="XXX" command="XXX" >设置catalog,command属性的话,那么在ExecuteCommand中将执行该catalog下的责任链

10.CreateAction类 根据在

<action path="" type="" />中配置的path属性值而与每次Request请求Path相对应时,创建以type属性值的继承Action类的Action子类,并将之存入ApplicationScope,所以在整个应用期间,都只有一个path路径对应的Action类。

 

11.ExecuteAction 类调用由10.CreateAction创建的Action类子类的execute(ActionMapping actionMapping, ActionForm actionForm, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)方法并将返回的ActionForward 存入ActionContext.

12.ExecuteForwardCommand类为ExecuteCommand之类,如果我们在配置文件中配置成

<forward name=" " path="" catalog="" command=""/>将执行catalog属性配置的责任链

13. PerformForward类根据保存在ActionContext的ActionForward类中的path属性进行跳转到视图或者是另外一个Action进行处理。

14.ExceptionHandler 对链中任意一个执行步骤中产生的Exception进行处理,我们可以客户化异常处理类。

到此为之Struts的处理流程解析就完成了。在以后的文章中我将介绍Struts的标签源码解析。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值