打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
struts源代码阅读(struts 的执行)
第二篇  struts的执行(struts-1.1版)

本篇详细介绍struts在初始化之后是如何处理一个请求,并返回数据的。这里最核心的类是
requestprocessor以及requestutils。requestprocessor类通过requestdispatcher实现页面的跳转,
而requestprocessor负责处理request中传来的请求信息,存放到formbeanconfig中,以及对要跳转的
url进行处理。

struts 在初始化完成之后,会根据请求调用doget(...)或者dopost(...)方法,这两个方法直接
调用process(request, response)方法。process(...)方法首先判断当前的request属于
哪一个moduleconfig,然后生成与这个moduleconifg相对应的requestprocessor,最后调用这个
requestprocessor的process(...)方法,执行request的请求。

一、requestutils.selectmodule(string prefix, httpservletrequest,servletcontext)方法:
    这个方法,根据prefix,从servletcontext中选择相应的moduleconfig,然后把这个moduleconfig
    保存到request中。servletcontext对应的key值为globals.module_key + prefix,保存到request
    中使用的key值为globals.module_key。如果在servletcontext中不存在这样的一个moduleconfig,
    那么调用request.removeattribute(globals.module_key)方法。然后以同样的方法查找、保存
    messageresources对象。当prefix为空时,会调用下面的方法选择moduleconfig。

二、requestutils.selectmodule(httpservletrequest, servletcontext)
    这个方发首先使用getmodulename(httpservletrequest, servletcontext)获取相应的path,然后
    通过调用getmodulename(string matchpath, servletcontext)获取相应的prefix。以这prefix为
    参数调用(一)中的selectmodule(...)选择moduleconfig。
    获取path的过程为:首先从request中查找名称为
    include_servlet_path(javax.servlet.include.servlet_path)的属性,如果为空,就调用
    request.getservletpath()方法获取servletpath。

    获取prefix的过程为:它首先调用getmoduleprefixes(servletcontext),获取所有已存在的
    module前缀。 然后通过分析上面获取的path来判断当前的url属于哪一个module,方法是截取
    path中最后一个"/"字符前面的字符串,然后与有上面方法中获取的prefixes[]逐个对比,如果
    prefixes[]中存在这样的一个值,则返回这个截取的字符串,否则继续截取path最后面的"/"前面
    的字符串,然后对比。如果始终找不到,则返回""。

    getmoduleprefixes(servletcontext)的执行过程是:首先通过
    context.getattribute(prefixes_key)查找是否存在这样的一个保存所有的prefix的string array,
    如果存在,就说明已经解析过一次了,就直接返回这个string array,否则遍历servletcontext中
    所有的attribute name,查找以module_key(org.apache.struts.action.module)开头的
    atrribute name,然后截取这个atrribute name中module_key后面的字符串,作为一个prefix. 最
    后把通过这个这个方式获取的所有的prefix作为一个arraylist存储到servletcontext中,属性key
    值为prefixes_key(org.apache.struts.util.prefixes),这样下次再次查找的时候就可以直接从这
    个attribute中获取了。

三、getmoduleconfig(httpservletrequest)
    这个方法就是获取上面的selectmodule(...)方法所得到的moduleconfig。如果找不到这样的
    moduleconfig,那么就把servletcontext中缺省的moduleconfig返回(调用
    getservletcontext().getattribute(globals.module_key))

四、getrequestprocessor(moduleconfig config)
    这个方法从根据moduleconfig的prefix作为key,从servletcontext中获取requestprocessor。这
    个key值为globals.request_processor_key + config.getprefix()。

    如果servletcontext中不存在这样的一个requestprocessor,那么就生成一个新的
    requestprocessor的实例,完成初始化(保存actionservlet以及moduleconfig到这个新的实例中)后
    将其保存到servletcontext中。

五、requestprocessor.process(httpservletrequest, httpservletresponse)
    这是真正执行httpservletrequst请求的方法。

    这个方法首先判断但前的httpservletrequest是否是multipart类型的request,也就是说当前
    的request是否有字段是"file"类型。这样做的原因是这种类型的request中getparameter()等
    相类似的方法都是无法执行的,所以要区分对待。如果是multipart类型的,那么把这个request包
    装成multipartrequestwrapper类。

    multipartrequestwrapper 与 httpservletrequest不同的就是重新实现了
    setparameter(string name, string value),getparameter(string name),getparameternames()
    以及getparametervalues(string name)方法。
    这些方法的思想是所有的变量名、值对都保存到一个hashmap里:key为变量名,value为变量值,但
    是注意value都是string[]格式的。当有一个新的值加入的时候,通过setparameter(...)
    方法,把值加到数组的最后。在setparameter(...)中有一个比较少见的保存值的方法,记录如下:
    public void setparameter(string name, string value) {
        string[] mvalue = (string[]) parameters.get(name);
        if (mvalue == null) {
            mvalue = new string[0];
        }
        string[] newvalue = new string[mvalue.length + 1];
        system.arraycopy(mvalue, 0, newvalue, 0, mvalue.length);
        newvalue[mvalue.length] = value;
        
        parameters.put(name, newvalue);
    }

    然后是调用processpath(httpservletrequest, httpservletresponse)获取地址,以这个地址作为
    从moduleconfig获取actionmapping的key。这里首先查询
    request.getattribute(include_path_info)中是否有这样的一个值,如果为空就调用
    request.getpathinfo()方法获取path。如果这样还是获取不了,就从
    request.getattribute(include_servlet_path)方法中获取path,找不到就使用
    request.getservletpath()得到的path进行分析。分析过程如下:

    然后如果这个path不是属于当前的moduleconfig的话,直接返回null。截取prefix后面的字符串,
    如果这个字符串以.xx结尾,那么就截取"."前面的字符,比如
    http://localhost:8080/servlet/where/go.do。where为module的名称(prefix),那么我们获取的
    path值为/go。

    然后是通过processlocale(httpservletrequest ,httpservletresponse)为当前用户设定一个local
    对象,它查找的顺序是:moduleconfig.getcontrollerconfig().getlocale(),
    session.getattribute(globals.locale_key),request.getlocale()。你可以在config xml文件
    中通过<controller><set-property..../></controller>执行相关的定义。

    调用processcontent(httpservletrequest, httpservletresponse)为response设定contenttype。
    这个contenttype是从moduleconfig.getcontrollerconfig().getcontenttype()获取的。你可以
    在config xml文件中通过<controller><set-property..../></controller>执行相关的定义。

    通过processnocache(httpservletrequest, httpservletresponse)方法设置response的缓存。
    你可以在config xml文件中通过<controller><set-property..../></controller>执行相关的定义。

    processpreprocess(httpservletrequest, httpservletresponse)预处理request,这个方法是预
    留的,用户可以根据自己的需要加一些预处理的程序。

    通过processmapping(httpservletrequest, httpservletresponse, string path)以
    processpath(...)的返回值为key从moduleconfig中查找actionmapping 对象。如果找到了,那么保
    存这个mapping到request中,key值为globals.mapping_key。如果不存在,那么遍历moduleconfig
    中所有的actionmapping,查找哪一个是缺省的actionmapping。然后把它保存到request中,key值
    为globals.mapping_key。

    通过processroles(httpservletrequest,httpservletresponse,actionmapping)检查当前用户是
    否有权限执行这个请求。如果request.isuserinrole(roles[i])返回true,则代表有。

    通过processactionform(httpservletrequest, httpservletresponse, actionmapping)生成一个
    actionform,然后根据actionmapping所属的scope,保存到request或者session中。key值为这个
    actionmapping中的attribute属性。actionform是通过requestutils.createactionform(...)方法
    获取的,在下一篇将会对这个方法进行详细的说明。这里只说明actionform与formbeanconfig以及
    formpropertyconfig之间的区别。每个formpropertyconfig代表form表单的一个字段,表单中的所
    有formpropertyconfig以一个hashmap保存到formbeanconfig中。而formbeanconfig是在
    actionservet初始化的时候生成的:
    <form-beans>
      <form-bean name="一个名称,作为action选择的key" type="实际对应的actionform类"/>
    <form-beans>
    名称用来在moudleconfig中的formbeans hashmap中查找相应的formbeanconfig,而formbeanconfig
    中有一个type,它保存了上面xml文件中定义的值,用来生成actionform。
    
    通过processpopulate(httpservletrequest, httpservletresponse, actionform,
    actionmapping)方法初始化actionform 以及 formbean,这个方法首先会设定这个actionform所属
    于的actionservlet,然后对这个actionform进行初始化。判断当前的reqest是否multipart类型,
    如果是就把相应的multipartclass类全名保存到request中,key值为globals.multipart_key。调
    用requestutils.populate(...)对request中的参数进行处理,并保存到相应的formbean中。在下
    一篇将会对这个方法进行详细的说明。最后根据request的parameter判断当前的请求是否是被取
    消,然后把相关信息保存到request中:
    if ((request.getparameter(constants.cancel_property) != null)
         (request.getparameter(constants.cancel_property_x) != null)) {
        request.setattribute(globals.cancel_key, boolean.true);
    }

六、下面说明这个request是如何真正被处理的,下面的方法都是在requestprocessor中定义。

    通过processvalidate(httpservletrequest, httpservletresponse, actionform,
    actionmapping)判断request中的parameter是否合法。如果合法就返回true,否则取消这次请求,
    并且返回到指定的输入页面。它判断合法的过程是:如果这次的请求被取消,那么直接返回true;
    如果没有要求对request中的parameter进行合法性检验,也直接返回true;最后通过调用
    form.validate(mapping, request)执行检验,如果返回的actionerrors为null,那么代表通过
    检验,返回true,否则取消这次request。取消的过程是:如果这个请求是multipart类型的,那么
    要对这个multipartrequesthandler进行回滚;而后,获取当前的actionmapping的input值,即在
    config xml 中<action.../>定义的input属性。如果actionmapping没有这个input值,那么调用
    response.senderror(...)方法,然后返回false;如果存在,就保存验证出错信息到request中,
    key为globals.error_key,跳转到input所指定的地址中。

    processforward(httpservletrequest, httpservletresponse, actionmapping)以及
    processinclude(httpservletrequest, httpservletresponse, actionmapping)分别通过执行
    requestdispatcher.forward(request, response) 以及 requestdispatcher.include(request,
    response)实现对页面的跳转。
    
    但是如果当前的请求还有其它操作要执行,那么actionmapping中的include或者forward属性值就
    会为空。这时需要通过调用processactioncreate(httpservletrequest, httpservletresponse,
    actionmapping)方法根据config xml文件的配置信息生成一个action类,然后通过
    processactionperform(...)调用生成的action中的execute(...)方法。最后根据execute(...)
    方法返回的actionforword类,执行跳转。在整个过程中有一个url的计算方法,这个将在下一篇
    中说明。


七、对actionmaping结构的说明:
    在actionconfig为保存moudle action属性的一个javabean,它有以下属性:
    * boolean configured 这个对象的所有属性是否已经被配置完。如果已经完成,那么在改变其中
      任何属性都会抛出illegalstateexception("configuration is frozen")
    * moduleconfig moduleconfig 本actionconfig类所属于的moduleconfig。
    * string attribute request范围 或者 session范围 的属性名称,我们将通过这个属性名称来
      获取相应的form bean,注意,这个属性与form bean中定义的名称是不一样的。注意以下的方
      法:
      public string getattribute() {
          if (this.attribute == null) {
              return (this.name);
          } else {
              return (this.attribute);
          }
      }
    * string forward 调用requestdispatcher.forward()方法时所需要的上下文相关的地址。
    * string include 调用requestdispatcher.include()方法时所需要的上下文相关的地址。
    * string type action类的类全名,如果上面的两个属性都没有定义的话,就会用它来处理
      request。
    * string input 如果输入数据没有通过验证,将会以这个值为地址,跳转到相应的页面。
    * string multipartclass multipartrequesthandler实现类的全名
    * string name 与本类相关的 form bean 的名称。
    * string parameter 用于struts的扩展,存储其它的配置信息。struts自己不会用到这个属性。
    * string path 上下文相关的地址,这个地址为下一个将会进入的地址,这个地址必须要以"/"
      开头。如果使用了extension mapping的话,这个地址将不会带有扩展名。
    * string roles 一个以","分隔的角色名称。这些角色将会有权限访问这个request。
    * string scope 缺省为session。
    * string prefix,string suffix后缀和前缀。
    * boolean unknown 标志当前的actionconfig类是否是为了未知的request path而准备的,
      actionmappings将会根据这个标志返回这个actionconfig。
    * boolean validate 是否调用validate()方法进行数据校验。    

    actionmapping 继承自 actionconfig,它增加了从moduleconfig查找actionforword的方法。同
    时它还提供了从moduleconfig查找exceptionconfig的方法,如果找不到,会通过
    type.getsuperclass()方法,根据父类的类名称继续查找,直到最后。

八、关于request.getservletpath()的解释:
    request.getservletpath() 的返回值就是在下面url中定义的值,比如如果按照下面的定义
    <url-pattern>
      *.do
    </url-pattern>
    那么:
    http://localhost:8080/servlet/go.do ---------->/go.do
    http://localhost:8080/servlet/where/go.do ---------->/where/go.do
    这里有一个小常识,如果
    <url-pattern>
      /where/*.do
    </url-pattern>
    那么地址中只有http://localhost:8080/servlet/where/*.do有效(tomcat 4.0)


    在actionconfig为保存moudle action属性的一个javabean,它有以下属性:
    * boolean configured 这个对象的所有属性是否已经被配置完。如果已经完成,那么在改变其中
      任何属性都会抛出illegalstateexception("configuration is frozen")
    * moduleconfig moduleconfig 本actionconfig类所属于的moduleconfig。
    * string attribute request范围 或者 session范围 的属性名称,我们将通过这个属性名称来
      获取相应的form bean,注意,这个属性与form bean中定义的名称是不一样的。注意以下的方
      法:
      public string getattribute() {
          if (this.attribute == null) {
              return (this.name);
          } else {
              return (this.attribute);
          }
      }
    * string forward 调用requestdispatcher.forward()方法时所需要的上下文相关的地址。
    * string include 调用requestdispatcher.include()方法时所需要的上下文相关的地址。
    * string type action类的类全名,如果上面的两个属性都没有定义的话,就会用它来处理
      request。
    * string input
    * string multipartclass multipartrequesthandler实现类的全名
    * string name 与本类相关的 form bean 的名称。
    * string parameter 用于struts的扩展,存储其它的配置信息。struts自己不会用到这个属性。
    * string path 上下文相关的地址,这个地址为下一个将会进入的地址,这个地址必须要以"/"
      开头。如果使用了extension mapping的话,这个地址将不会带有扩展名。
    * string roles 一个以","分隔的角色名称。这些角色将会有权限访问这个request。
    * string scope 缺省为session。
    * string prefix,string suffix后缀和前缀。
    * boolean unknown 标志当前的actionconfig类是否是为了未知的request path而准备的,
      actionmappings将会根据这个标志返回这个actionconfig。
    * boolean validate 是否调用validate()方法进行数据校验。    

    actionmapping 继承自 actionconfig,它增加了从moduleconfig查找actionforword的方法。同
    时它还提供了从moduleconfig查找exceptionconfig的方法,如果找不到,会通过
    type.getsuperclass()方法,根据父类的类名称继续查找,直到最后。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
JR 精品文章 - 使用Timmer使Struts修改struts-config.xml文...
Go 语言内置的 Handler
struts2 处理请求流程分析(结合源码)2
struts源代码阅读(struts 初始化)
扩展struts
Struts第一天
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服