struts2:Struts 2杂谈(1):ValueStack对象的传送带机制

  本文举例源代码或素材下载

  众所周知Strut 2Action类通过属性可以获得所有相关如请求参数、Action配置参数、向其他Action传递属性值(通过chain结果)等等要获得这些参数值我们要做件事就是在Action类中声明和参数同名属性在Struts 2Action类Action思路方法(默认是execute思路方法)的前就会为相应Action属性赋值

  要完成这个功能有很大程度上Struts 2要依赖于ValueStack对象这个对象贯穿整个Action生命周期(每个Action类对象例子会拥有个ValueStack对象)当Struts 2接收到个.action请求后会先建立Action类对象例子但并不会Action思路方法而是先将Action类相应属性放到ValueStack对象顶层节点(ValueStack对象相当于个栈)只是所有属性值都是默认如String类型属性值为null类型属性值为0等

  在处理完上述工作后Struts 2就会拦截器链中拦截器完所有拦截器后最后会Action类Action思路方法Action思路方法的前会将ValueStack对象顶层节点中属性值赋给Action类中相应属性大家要注意在这里就给我们带来了很大灵活性也就是说在Struts 2拦截器过程中可以改变ValueStack对象中属性当改变某个属性值后Action类相应属性值就会变成在拦截器中最后改变该属性这个值

  从上面描述很容易知道在Struts 2Action类可以获得和属性同名参数值就是通过区别拦截器来处理如获得请求参数拦截器是params获得Action配置参数拦截器是Params等在这些拦截器内部读取相应并更新ValueStack对象顶层节点相应属性而ValueStack对象就象个传送带将属性值从个拦截器传到了另个拦截器(当然在这其间属性值可能改变)最后会传到Action对象并将ValueStack对象中属性值终值赋给Action类相应属性

  也许有读者会看出来个问题如果有多个拦截器都改变同个属性值那么在后面引用拦截器将覆盖的前引用拦截器改变属性值由于在defaultStack拦截器栈中Params是在params的前引用因此如果某个请求参数和Action类配置参数同名请求参数值将覆盖配置参数值

  下面我们使用个例子来演示这个过程在这个例子中实现了个拦截器该拦截器功能是将个属性文件中key-value对映射成相应属性如下面是个属性文件内容:

  name = 超人
  price = 10000


  我们可以在Action类中定义name和price属性在Action中引用这个拦截器后就会自动为属性赋值

  在使用该拦截器有如下规则:

  1. 拦截器读取属性文件路径由path参数指定

  2. 属性文件编码格式由encoding参数指定默认值是UTF-8

  3. 如果某个key中包含有“.”(该符号不能出现在标识符中)则有如下处理思路方法:

  (1)将Action类属性名定义为去掉“.”key例如key为person.name而属性名可定义为personname

  (2)将Action类属性名定义为将“.”替换成其他表示符号例如key为person.name而属性名可定义为person_name其中“_”由separator参数指定

  4. 如果key太长也可以直接使用Action参数进行映射例如key为country.person.name可做如下映射:

  <param name="countrypersonname">name</param>

  要注意name属性值不能包含“.”因此应将key值中“.”去掉现在就可以直接在Action类中定义名为name属性name属性值会和key值相同

  5. 上面所有规则可以同时使用

  拦截器源代码:packageerceptors;
importjava.util.Enumeration;
importjava.util.Map;
importjava.util.Properties;
importjava.io.InputStream;
importjava.io.FileInputStream;
importcom.opensymphony.xwork2.ActionContext;
importcom.opensymphony.xwork2.ActionInvocation;
importcom.opensymphony.xwork2.config.entities.ActionConfig;
importcom.opensymphony.xwork2.erceptor.AbstractInterceptor;
importcom.opensymphony.xwork2.util.ValueStack;
publicPropertyInterceptorextendsAbstractInterceptor
{
  privatefinalStringDEFAULT_PATH_KEY="path";
  privatefinalStringDEFAULT_ENCODING_KEY="encoding";
  privatefinalStringDEFAULT_SEPARATOR_KEY="separator";
  protectedStringpathKey=DEFAULT_PATH_KEY;
  protectedStringencodingKey=DEFAULT_ENCODING_KEY;
  protectedStringseparatorKey=DEFAULT_SEPARATOR_KEY;
  publicvoidPathKey(StringpathKey)
  {
    this.pathKey=pathKey;
  }
  publicvoidEncodingKey(StringencodingKey)
  {
    this.encodingKey=encodingKey;
  }
  publicvoidSeparatorKey(StringseparatorKey)
  {
    this.separatorKey=separatorKey;
  }
  @Override
  publicStringercept(ActionInvocationinvocation)throwsException
  {
    ActionConfigconfig=invocation.getProxy.getConfig;
    Map<String,String>parameters=config.getParams;
    (parameters.containsKey(pathKey))
    {
      Stringpath=parameters.get(pathKey);
      Stringencoding=parameters.get(encodingKey);
      Stringseparator=parameters.get(separatorKey);
      (encodingnull)
        encoding="UTF-8";
      (separatornull)
        separator="";
      path=invocation.getAction.getClass.getResource(path)
          .getPath;
      Propertiesproperties=Properties;
      InputStreamis=FileInputStream(path);
      java.io.Readerreader=java.io.InputStreamReader(is,encoding);
      
      properties.load(reader);
      ActionContextac=invocation.getInvocationContext;
      ValueStackstack=ac.getValueStack;
      .out.prln(stack.hashCode);
      Enumerationnames=properties.propertyNames;
      while(names.hasMoreElements)
      {
        // 下面会使用Value思路方法修改ValueStack对象中相应属性值
        Stringname=names.nextElement.toString;
        (!name.contains("."))
          stack.Value(name,properties.get(name));
        StringName=null;
        Name=parameters.get(name.replaceAll(".",""));
        (Name!=null)
          stack.Value(Name,properties.get(name));
        (!separator.equals(""))
        {
          Name=name.replaceAll(".","");
          stack.Value(Name,properties.get(name));
        }        
        Name=name.replaceAll(".",separator);
        stack.Value(Name,properties.get(name));
      }
    }
    invocation.invoke;
  }
}


  用于测试Action类源代码:

packageactions;
publicMyAction
{
  privateStringname;
  privateIntegerprice;
  privateStringlog4jappenderstdout;
  privateStringlog4j_rootLogger;
  privateStringconversionPattern;
  publicStringgetName
  {
    name;
  }
  publicvoidName(Stringname)
  {
    this.name=name;
  }
  publicIntegergetPrice
  {
    price;
  }
  publicvoidPrice(Integerprice)
  {
    this.price=price;
  }
  publicStringgetLog4jappenderstdout
  {
    log4jappenderstdout;
  }
  publicvoidLog4jappenderstdout(Stringlog4jappenderstdout)
  {
    this.log4jappenderstdout=log4jappenderstdout;
  }
  publicStringgetLog4j_rootLogger
  {
    log4j_rootLogger;
  }
  publicvoidLog4j_rootLogger(Stringlog4j_rootLogger)
  {
    this.log4j_rootLogger=log4j_rootLogger;
  }
  publicStringgetConversionPattern
  {
    conversionPattern;
  }
  publicvoidConversionPattern(StringconversionPattern)
  {
    this.conversionPattern=conversionPattern;
  }
  publicStringexecute
  {
    .out.prln("name:"+name);
    .out.prln("price:"+price);
    .out.prln("log4jappenderstdout:"+log4jappenderstdout);
    .out.prln("log4j_rootLogger:"+log4j_rootLogger);
    .out.prln("conversionPattern:"+conversionPattern);
    null;
  }
}


  Action类配置代码如:

<?xmlversion="1.0"encoding="UTF-8"?>
<!DOCTYPEstrutsPUBLIC
  "-//ApacheSoftwareFoundation//DTDStrutsConfiguration2.1//EN"
  "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
  <packagename="struts"extends="struts-default">
    <erceptors>
      <erceptorname="property"
        ="erceptors.PropertyInterceptor"/>
      <erceptor-stackname="myStack">
        <erceptor-refname="defaultStack"/>
        <erceptor-refname="property"/>
      </erceptor-stack>
    </erceptors>
    <actionname="test"="actions.MyAction">
      <erceptor-refname="myStack"/>
      <paramname="path">/log4j.properties</param>
      <paramname="encoding">UTF-8</param>
      <paramname="separator">_</param>
      <paramname="log4jappenderstdoutlayoutConversionPattern">
        conversionPattern
      </param>
    </action>
  </package>
</struts>




  请将log4j.properties文件复制到WEB-INFes目录并在该文件中加入name和price属性

  测试结果:

name:中国
price:34
log4jappenderstdout:org.apache.log4j.ConsoleAppender
log4j_rootLogger:error,stdout
conversionPattern:%d{ABSOLUTE}%5p%c{1}:%L-%m%n


  由于property拦截器在defaultStack后引用因此在该拦截器中设置属性值是最终结果如果将property拦截器放在defaultStack前面(将两个<erceptor-ref>元素掉换下)就可以通过同名胜Action配置参数或请求参数来干预最终究输出结果了



Tags:  struts2spring struts标签 struts2.0 struts2

延伸阅读

最新评论

发表评论