zoukankan      html  css  js  c++  java
  • struts2深入

    Struts2

    一.OGNL表达式

    1 什么是OGNL语言

    你还记得 EL表达式语言么?没错OGNL也是表达式语言,不过它比EL可强大太多了。OGNL的全称为Object Graphic Navigation Language(对象图导航语言)。它是Struts2的默认表达式语言!

    使用OGNL需要导入OGNL的Jar包:ognl-3.0.5.jar

    OGNL的功能介绍:

    l  EL一样的JavaBean导航;

    l  调用对象方法;

    l  调用类的静态方法;

    l  索引数组元素;

    l  操作集合;

    1. EL可以读取JavaBean属性,OGNL使用起来与EL表达式有点相似
    2. EL操作的数据是域对象,OGNL操作的数据OgnlContext,可以在OgnlContext指定一个root对象,在不指定操作的对象时默认为root对象。
    2 OGNL入门

    为了可以执行OGNL表达式,所以我们必须要先学会怎么来使用Ognl类。为了更好的演示Ognl我们需要先创建一些JavaBean用来做测试。

    public class Address {

        private String country;

        private String city;

        private String street;

    ……

    }

    public class Employee {

        private String name;

        private double salary;

        private Address address;

    ……

    }

    我们可以回忆一下EL的使用:${name},它会在所有域中去查找name这个域属性。也就是说,EL表达式也需要一个查询的范围。相同的道理,OGNL也需要一个查找的范围,它查询的范围是OgnlContext。OgnlContext其实就是一个Map,你可以向其内添加N个键值对。它还可以指定多个键值中的一个为根对象(root)!

           OgnlContext cxt = new OgnlContext();

           Address add1 = new Address("中国", "北京", "大北窑");

           Employee e1 = new Employee("张三", 10000, add1);

           Address add2 = new Address("中国", "北京", "西三旗");

           Employee e2 = new Employee("李四", 12000, add2);    

           cxt.put("emp1", e1);

           cxt.put("emp2", e2);

           cxt.setRoot(e1);

    演示OGNL表达式需要使用Ognl类,我们先从它的getValue()方法开始。这个方法需要三个参数,分别是:

    l  OGNL表达式;

    l  上下文对象;

    l  根对象。

           String name = (String)Ognl.getValue("#emp2.name", cxt, cxt.getRoot());

           System.out.println(name);

    其中“#emp2.name”是OGNL表达式,查找上下文中的元素必须以“#”开关,其中emp2对应上下文中的键,“.name”表示调用该元素的getName()方法,即JavaBean导航。如果它这个OGNL表达式翻译成Java代码,即:((Employee)cxt.get(“emp2”)).getName()。

           String name = (String)Ognl.getValue("name", cxt, cxt.getRoot());

           System.out.println(name);

    其中“name”为OGNL表达式,当没有使用“#”开始时,表达在使用根对象。所以“name”表达式可以理解为:cxt.getRoot().getName()。即输出“张三”。

           String name = (String)Ognl.getValue("address.street", cxt, cxt.getRoot());

           System.out.println(name);

     因为没有使用“#”开头,那么就是在使用根对象,即cxt.getRoot().getAddress().getStreet(),即输出“大北窑”。

    1. OgnlContext就是一个Map,指定操作的对象时需要使用“#”为前缀,后缀为Map的key为后缀;
    2. 当没有使用“#”时,表示操作的对象为root对象。
    3 OGNL常量

    OGNL也可以使用常量:

    l  字符串常量:使用双引号,或单引号。如果字符串只有唯一字符时,必须使用双引号;

    l  字符常量:使用单引号;

    l  数值常量:与Java相同;

    l  boolean常量:与Java相同。

    l  OGNL也支持null常量。

    1. “hello”是字符串常量,’hello’也是字符串常量,’h’是字符常量;
    2. true、false为boolean类型常量;
    3. 1.0、100为数值类型常量;
    4. null也是常量。
    4 运算符

    OGNL运算符基本与Java相同,但它还有一些特殊的运算符:

    l  逗号(,):用逗号分隔多个OGNL表达式,这些表达式会依次被执行,而最后一个表达式的值为整个表达式的值;

    l  大括号({}):用来创建列表,例如:{‘zhangSan’,’liSi’,’wangWu’}表示一个List;

    l  in和not in:in表示判断某个值是否在集合中,not in表达某个值是否不在集合中。

           double s = (Double)Ognl.getValue("name,#emp2.name,salary", cxt, cxt.getRoot());

           System.out.println(s);

      上面使用了逗号表达式,一共是3个OGNL表达式,它们会被依次运行,但整个整句表达式的值为最后一个OGNL表达式的值,即salary的值。

           boolean bool = (Boolean)Ognl.getValue("name in {'张三','李四'}", cxt, cxt.getRoot());

           System.out.println(bool);

     表达式“name in {‘张三’,’李四’}”首先需要得到name的结果,即根对象的name属性值,然后判断这个值是否在列表中存在,所以返回为true。

    1. 逗号运算符很少使用,它的作用是把多个OGNL表达式连接成一个OGNL表达式,每个子表达式会从左到右依次运行,完整表达式的值来最右边表达式的值;
    2. in和not in,用于判断某个值在集合中是否存在;
    3. {}可以用来创建List集合。
    4 设置JavaBean属性值(重点)

    OGNL表达式不只是可以获取JavaBean属性值,不可以设置JavaBean属性值。但这需要使用Ognl类的setValue()方法。

           Ognl.setValue("#emp2.address.street", cxt, cxt.getRoot(), "育新");

           System.out.println(Ognl.getValue("#emp2.address.street",cxt, cxt.getRoot()));

    表达式指定的是cxt中键为emp2的对象,然后导航到它的address属性,再导航到street属性,最终给该属性设置值为“育新”。

    1. “name”表示root对象的name属性;
    2. “address.city”表示root对象的address属性的city属性;
    3. “#emp.salary”表示上下文中key为emp的对象的salary属性。
    5 调用对象方法

    OGNL不只可以获取JavaBean属性,还可以调用对象方法。

           String s = (String)Ognl.getValue("'OGNL'.toLowerCase()", cxt, cxt.getRoot());

           System.out.println(s);

    表达式“’OGNL’.toLowerCase()”表示调用字符串OGNL的toLowerCase()方法,千万要小心,在OGNL上添加单引或双引,指定其为字符串常量,不然会被误会来根对象的属性的。

    当然,不只是可以调用字符串的方法,什么对象的方法都可以调用。

    6 静态方法和静态属性

    OGNL还可以调用类的静态方法,以及读取静态属性。操作静态方法或属性时,需要使用完整的类名称,并且需要使用两个“@”把类名称包含起来!

           String s = (String) Ognl.getValue(

                  "@java.lang.String@format('%tF   %<tT', new java.util.Date())",

                  cxt, cxt.getRoot());

           System.out.println(s);

    上面的表达式可以翻译成:java.lang.String.format(“%tF %<tT”, new java.util.Date())

    OGNL对java.lang.Math对有特殊的待遇,如果是操作Math类的静态属性或静态方法,可以不用给出类名称,但“@”不可以省略。

           int max = (Integer)Ognl.getValue("@@max(1,2)", cxt, cxt.getRoot());

           System.out.println(max);

           double pi = (Double)Ognl.getValue("@@PI", cxt, cxt.getRoot());

           System.out.println(pi);

    7 操作数组

      OGNL可以用来操作数组,使用下标法即可:

           String[] names = {"zhangSan", "liSi", "wangWu"};

           cxt.put("ns", names);

           String name = (String)Ognl.getValue("#ns[0]", cxt, cxt.getRoot());

           System.out.println(name);

    8 操作集合

    OGNL可以用来创建List和Map,先来创建一个List它。

           List<String> names =   (List<String>)Ognl.getValue("{'zhangSan', 'liSi',   'wangWu'}",   cxt, cxt.getRoot());

           System.out.println(names);

    这段代码创建一个List,类型为ArrayList。

    创建Map必须以“#”开头

           Map<String,String> map   = (Map<String, String>)Ognl.getValue("#{'a':'A','b':'B'}", cxt, cxt.getRoot());

           System.out.println(map);

    创建的Map默认类型为HashMap类型。还可以创建Map时指定Map的类型:

           Map<String, String>   map = (Map<String, String>) Ognl.getValue(

                  "#@java.util.LinkedHashMap@{'aa':'AA','bb':'BB'}", cxt,

                  cxt.getRoot());

           System.out.println(map);

    获取Map的值。

           String s = (String) Ognl.getValue(

                  "#@java.util.LinkedHashMap@{'aa':'AA','bb':'BB'}['bb']", cxt,

                  cxt.getRoot());

           System.out.println(s);

    9 投影和选择

    我们先来聊聊投影,如果有一个Employee的集合,可以通过投影获取所有的Employee的name属性值。这与数据库操作的查询指定列相似。

    List<Employee> list = new ArrayList<Employee>();

    list.add(new Employee("zhangSan", 10000));

    list.add(new Employee("liSi", 11000));

    list.add(new Employee("wangWu", 12000));

    cxt.put("empList", list);

    List<String> nameList = (List<String>) Ognl.getValue("#empList.{name}", cxt, cxt.getRoot());

    System.out.println(nameList);

    获取所有Employee的name属性。这相当于循环遍历list集合,然后调用每个Employee对象的getName()方法。

    选择是给出一个条件,获取集合中满足条件的元素。这相当与select * from xxx where con例如我们要获取salary > 10000的Employee对象。

           List<Employee> list = new ArrayList<Employee>();

           list.add(new Employee("zhangSan", 10000));

           list.add(new Employee("liSi", 11000));

           list.add(new Employee("wangWu", 12000));

           cxt.put("empList", list);

           List<Employee> empList   = (List<Employee>) Ognl.getValue(

                  "#empList.{?salary >   10000}", cxt,   cxt.getRoot());

           System.out.println(empList);

      其中“{?salary > 10000}”中的“?”表示所有满足条件的元素都查询出来。还可以使用“^”或“$”,其中“^”表示查询第一个满足条件的元素,而“$”表示查询最后一个满足条件的元素。

    二.值栈 ValueStack

    通过几个问题来了解值栈----

    问题一 什么是值栈 ValueStack ?

    *Struts2框架是我们ActionContext中设置的OGNL上下文,ValueStack是OGNL的根对象。(值栈是一组多个对象,但OGNL原来是一个单独的对象)。

    *ValueStack 是 struts2 提供一个接口,实现类 OgnlValueStack ----值栈对象(OGNL是从值栈中获取数据的 )

    *每个Action实例都有一个ValueStack对象,一个请求对应一个ValueStack对象,保存当前Action对象和其他相关对象(值栈中是有Action引用的 )

    *Struts 框架把 ValueStack对象保存在名为“struts.valueStack”的请求属性中,request中 (值栈对象是 request一个属性)

             valueStack vs = request.getAttribute("struts.valueStack");

    问题二 值栈的内部结构 ?                                                           

      值栈由两部分组成

             root  (CompoundRoot): Struts  把动作和相关对象压入 ObjectStack 中--List

             Context  (OgnlContext): Struts 把各种各样的映射关系(一些 Map 类型的对象) 压入 ContextMap 中

             Struts 会把下面这些映射压入 ContextMap 中

                       parameters: 该 Map 中包含当前请求的请求参数

                       request: 该 Map 中包含当前 request 对象中的所有属性

                       session: 该 Map 中包含当前 session 对象中的所有属性

                       application:该 Map 中包含当前 application  对象中的所有属性

                       attr: 该 Map 按如下顺序来检索某个属性: request, session, application

    问题三 值栈对象的创建 ,ValueStack 和 ActionContext 是什么关系

             值栈对象是请求时创建的

             doFilter中 prepare.createActionContext(request, response);

                       * 创建ActionContext 对象过程中,创建 值栈对象ValueStack

                       * ActionContext对象 对 ValueStack对象 有引用的 (在程序中 通过 ActionContext 获得 值栈对象 )

             Dispatcher类 serviceAction 方法中 将值栈对象保存到 request范围

                       request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());(struts2源码)

    获得值栈对象 有两种方法

    ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

    ValueStack valueStack2 = ActionContext.getContext().getValueStack();

    关系图:

    其实OgnlContext并不是ActonContext,而是ActionContext中包含了OgnlContext对象。ValueStatck也不是root对象,而是内部有root对象。其实在ActionContext中包含的OgnlContext对象,在ValueStack中也有OgnlContext的引用。

        

    问题五:值栈应用(主要针对root

    1.存取数据,在JSP中获取值栈的数据

        访问root中数据 不需要#

        访问 其它对象数据 加 #

    2.valueStack对象 保存在request范围, 值栈生命周期 就是 request的生命周期 ,值栈的内部结构(root、ognlContext)、获取值栈对象、向值栈保存数据、在JSP显示值栈的内容

    3、 值栈在开发中应用

    主流应用 : 值栈 解决 Action 向 JSP 传递 数据问题

    哪些数据默认会放入到值栈 ?

             1)每次请求,访问Action对象 会被压入值栈 ------- DefaultActionInvocation 的 init方法 stack.push(action);

             * Action如果想传递数据给 JSP,只有将数据保存到成员变量,并且提供get方法就可以了

             2)ModelDriven 接口 有一个单独拦截器

             <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>

             在拦截器中 ,将model对象 压入了 值栈 stack.push(model);

             * 如果Action 实现ModelDriven接口,值栈默认栈顶对象 就是model对象

    4、 值栈的数据 通过EL访问

    StrutsPreparedAndExecuteFilter的doFilter代码中 request = prepare.wrapRequest(request);

             * 对Request对象进行了包装 ,StrutsRequestWrapper

             * 重写request的 getAttribute

    <源码分析>

                       Object attribute = super.getAttribute(s);

                       if (attribute == null) {

                          attribute = stack.findValue(s);

                       }

          访问request范围的数据时,如果数据找不到,去值栈中找

             * request对象 具备访问值栈数据的能力 (查找root的数据)

    2 Struts2标签使用OGNL表达式

    l  使用<s:property>标签可以执行OGNL表达式,例如:<s:property value=”name”/>;

    l  当OGNL访问的是根对象时(例如OGNL表达式name),Struts2会把ValueStack中每个元素当成根对象,从栈顶依次向下查找,直到找到某个元素有name属性。

    Struts2中有很多标签都支持OGNL表达式,我们也知道OGNL表达式访问的数据是在OgnlContext中。在Struts2中上下文对象就是ActionContext,其中根对象就是ValueStack。

    例如<s:property value=”name”/>这个标签的value属性值name就必须是一个OGNL表达式。我们知道,没有使用“#”为前缀的OGNL表达式表示是在获取根对象的name属性值,但在Struts2中根对象是ValueStack,它不是一个对象,而是一组对象,一个栈!Struts2的根对象特殊之处是,它会在ValueStack中从栈顶开始,直接到栈尾遍历每个栈中的对象,查看是否有name属性,如果找到栈中某个对象存在name属性,那么就不会再向下去找!也就是说,如果栈顶元素就有name属性,那么就算第二个元素也有name属性,那么输出的也是栈顶元素的name属性值。

    前面我们也说过了,当访问一个Action后,Struts2会把当前Action对象压入到值栈中,即栈顶位置上(当然我们也可以调用ValueStack的push()方法来向其压入对象)。然后在Action跳转的JSP页面中使用<s:property>标签来获取值栈中元素的属性值。

    我们知道,还可以通过OGNL表达式访问上下文中其他的对象,但这需要以“#”为前缀。例如可以访问parameters、request、session、application、attr这些Map对象。

    <s:property value="#parameters.password"/><br/>

    <s:property value="#request.hello"/><br/>

    在ActionContext中存放着很多个Map:

    key

    value

    request

    Map类型,对应request中的属性,但它不是request对象本身

    session

    Map类型,对应session中的属性

    application

    Map类型,对应application中的属性

    parameters

    Map类型,对应request中请求参数

    attr

    Map类型,对应所有域对象,依次查找。

    #request表示一个Map,而#request.hello表示获取#request这个Map中key为hello的值。当然,如果你只有request.setAttribute(“hello”, “xxx”)过,才能获取到值。

    3 [N]语法

    [N]语法用来获取子栈,例如[3]表示截取从下标3位置开始到栈底的一个子栈。例如[3].name表示从[3]子栈中查找每个元素是否存在name属性,如果下标3元素就存在name属性,那么直接返回即可。

    例如我们向ValueStack中压入两个对象:

    public class OgnlDemo1Action extends ActionSupport {

        private String name;

        public String getName() {

           return name;

        }

        public void setName(String name) {

           this.name = name;

        }

        @Override

        public String execute() throws Exception   {

           Emp emp = new Emp();

           emp.setName("emp_zhangSan");

           Student stu = new Student();

           stu.setName("stu_liSi");

          

           ValueStack vs = ActionContext.getContext().getValueStack();

           vs.push(emp);

           vs.push(stu);

           return SUCCESS;

        }

    }

    <s:property value="[0].name"/><br/>

    <s:property value="[1].name"/><br/>

    <s:property value="[2].name"/><br/>

      千万注意,[1]不是表示下标1的元素,而是一个子栈。

    4 top关键字

    top语法用来获取栈顶元素本身。例如还是上面的例子,值栈中从上到下依次是Student、Emp、Action三个对象,那么top表示的就是Student对象本身。而[1].top表示获取[1]子栈的栈顶元素,即Emp对象。

    <s:property value="[0].top"/><br/>

    <s:property value="[1].top"/><br/>

    <s:property value="[2].top"/><br/>

    5 访问静态成员

    我们知道OGNL可以访问类的静态方法和属性,但在Struts2中默认是不允许OGNL访问静态成员的,如果想访问类的静态成员,需要设置一个常量:struts.ognl.allowStaticMethodAccess,该常量默认值为false,我们需要把它设置为true,这样就可以访问静态成员了。

    <constant name="struts.ognl.allowStaticMethodAccess"   value="true"/>

    <s:property value="@@min(10,20)"/><br/>

    除了使用标准的OGNL表达式访问静态属性和静态方法外,Struts2还允许你不指定完整的类名,而是通过“vs”前缀来调用保存在值栈中对象的静态属性和静态方法。

    例如:@vs@hello(),表示调用栈顶元素的静态方法hello(),如果栈顶元素是MyAction类型的对象,那么@vs@hello()就表示MyAction.hello()!

    还可以指定值栈中元素序号来调用静态成员,例如@vs1@hello()表示调用的是值栈中第一个元素的静态方法,其实它与@vs@hello()是相同的。因为不存在@vs0@,序号是从1开始的。@vs2@hello()表示调用值栈中第2个元素的静态方法hello()。

    6 模型驱动与ValueStack

    我们知道,Struts2会把当前Action对象压入到值栈中,所以我们在JSP页面中总是可以直接使用OGNL来访问Action元素的属性:<s:property value=”username”/>,如果我们没有自己向值栈中压入其他元素,那么Action就是栈顶元素了,所以上面的标签输出的就是当前Action的username属性值。

    当Action使用模型驱动时,ModelDrivenInterceptor拦截器会把当前model对象压入值栈,这时值栈中model对象就是栈顶元素,所以<s:property value=”username”/>访问的是model对象的username属性。

    7 EL表达式访问值栈

    让人感到奇怪的是,可以通过EL表达式访问到值栈。例如在JSP页面中使用${name},但在域对象中没有name这个域属性,奇怪的是它会去到值栈中找到元素的name属性值。

    这是因为Struts2对request对象进行了装饰,这个装饰类是StrutsRequestWrapper,它对getAttribute()方法进行了增强,它会先到域对象中查找名为name的属性值,但如果没有找到它会去到ValueStack中去找,所以EL才会有这个本事。

    8 Struts2 OGNL中的#、%、$

    在Struts2的标签中,有些标签可以直接使用OGNL表达式,但有些是不能直接使用的。例如我们使用过的<s:property>标签的value属性就是默认为OGNL表达式的。但<s:url>标签的action属性默认是字符串,而不是OGNL表达式。例如<s:url action=”demo1”/>会在页面中输出/day03/damo1.action。如果你想在<s:url>标签的action属性中使用OGNL,那么需要使用%{},这样Struts2就知道你给出的值是一个OGNL表达式了,例如:<s:url action=”%{#request.url}”/>,其中#request.url会被Struts2当做OGNL表达式来解析!

    当然,你也可以在<s:property>标签的value属性中使用%{},但对于默认就是OGNL表达式的属性而言,添加%{}是没有意义的:<s:property value=”%{name}”/>

    对于OGNL表达式中的“#”,表示的是访问上下文中的非root对象时需要使用以“#”开头。这个应该没什么问题吧。

    ${}是在国际化资源文件中用来使用OGNL表达式用的,${}还可以在struts.xml文件用来使用OGNL表达式。

    例如在res.properties文件中:msg:我的名称叫${#request.name},然后在Action或JSP中就可以使用它了。

                 <s:i18n name="res">

                                <s:text name="msg"></s:text>

                       </s:i18n>

    例如在struts.xml文件中使用:

    <result type=”stream”>

      <param name=”contentType”>${type}</param>

    </result>

    其中${type}表示OGNL表达式,即调用当前Action的getType()方法(因为Action在值栈的栈顶位置)。

    9 从Action中向页面传递数据

    1.  一般性数据传递

    每个Action都有自己保存信息的地方,这是从ActionSupport类继承而来的!

    l  ActionSupport#addFieldError(“字段名”, “字段错误信息内容”):它会把信息添加到Map<String,List>中;

    l  ActionSupport#addActionError(“Action错误信息内容”):它会把信息添加到Collection<String>中;

    l  ActionSupport#addActionMessage(“Action通知信息内容”):它会把信息添加到Collection<String>中。

    因为Action会被压入到值栈中,所以在页面中Struts2的标签就可以来获取这些信息了。

    l  <s:fielderror fieldame=”字段名”/>:打印指定字段的所有错误信息;

    l  <s:actionerror/>:打印Action错误信息;

    l  <s:actioinmessage/>:打印Action通知信息。

      2. 复杂数据传递

    也可以把复杂类型的数据通过压入值栈来向页面传递:

    valueStack.push(new User(“zhangSan”, “123”));

    在页面中可以使用Struts2标签来获取值栈的内容:

    <s:property value=”username”/>

    <s:property value=”password”/>

    10 页面中查看ValueStack内部数据

      Struts2为开发人员提供了一个标签:<s:debug/>,它会在页面中生成一个超链接,点击链接会在页面中显示ValueStack的内部数据。

    Struts2标签

    打开struts-2.3.7docsWW ag-reference.html可以看到Struts2提供的所有标签。其中分为“通用标签”和“UI标签”两大部分。我们不可能把所有标签都介绍到,我们只会介绍其中比较有用的几个标签介绍一下。

    1 Struts2通用标签之数据标签

    l  <s:property>(重要)

    <s:property>标签用来执行OGNL表达式,最为常用的方式是使用它在页面中输出ValueStack或ActionContext中的数据。

    <s:property value=”#request.hello”/>,等于ActionContext.getContext().getRequest().get(“hello”)。

    名称

    必需

    默认值

    类型

    说明

    default

    String

    表示默认值。当结果为null时,输出默认值。

    escape

    true

    boolean

    表示是否进行转义。该值为true时,当结果中包含<、>、”、’、&时,对其进行转义。

    value

    栈顶对象

    Object

    Ognl表达式

    l  <s:set>

    <s:set>标签用来创建一个变量,保存到指定的范围内。

    <s:set var=”myVar” value=”#parameters.score[0]” scope=”page”/>,创建一个变量,保存到page范围,key为myVar,值为“#parameters.score[0]”的运算结果。

    名称

    必需

    默认值

    类型

    说明

    var

    String

    变量的域名字,默认不是Ognl表达式

    value

    栈顶对象

    Object

    Ognl表达式。

    scope

    Action

    String

    变量的范围。可选值为:application、session、request、page、action

    scope的可选值中的action是我们陌生的范围,它是scope的默认值。它表示保存到request和OgnlContext两个范围中。即比request范围多出了一个OgnlContext范围。

    <s:set var=”myVar” value=”#parameters.score[0]” />

    <s:property value=”#myVar”/>等同于ActionContext.getContext().get(“myVar”);

    <s:property value=”#request.myVar”/>等同于ActionContext.getContext.getReuqest().get(“myVar”);

    l  <s:push>

    <s:push>标签是把指定值暂时压入到值栈中,当执行完<s:push>标签后,压入到值栈中的值会被弹出。

    <s:push value=”’hello’”/>等于把hello字符串压入到值栈中后,马上又弹出了,相当于什么都没做。

    <s:push value=”#session.user”>把user压入到值栈中

      <s:property value=”username”/>打印值栈元素的username属性

      <s:property value=”password”/>打印值栈元素的password属性

    </s:push>把user从值栈中弹出

    l  <s:url>

    <s:url>标签通常用来生成action路径,它与<c:url>标签很相似。

    <s:url action=”TestAction”/>在页面中打印/contextpath/TestAction.action。也就是说它与<c:url>一样会生成全路径。而且无论给出后缀“.action”!action属性的值只需要与struts.xml文件中<action>元素的name属性值相同即可。

    <s:url action=”TestAction” namspace=”/” />还可以指定名称空间

    <s:url action=”TestAction”>

      <s:param name=”name” value=”’张三’”/>

    </s:url>

    页面中打印为:/ognl/TestAction.action?name=%E5%BC%A0%E4%B8%89

    还可以为URL指定参数,其中参数包含中文时自动使用URL编码。

    其中<s:param>是用来作为子标签的,它的作用是指定参数。它的value属性值为Ognl表达式,所以我们需要把“张三”用单引号引起来,表示Ognl表达式的字符串常量。

    名称

    必需

    默认值

    类型

    说明

    action

    String

    指定用于生成URL的action。

    value

    String

    指定用于生成URL的地址值。通常只使用action或value其中一个属性。

    var

    String

    如果指定了该属性,那么生成的URL不会输出到页面中,而是被保存到OgnlContext中。

    method

    String

    指定调用action中的方法。如果使用的是value属性,那么该属性无效。

    namespace

    String

    指定action所属的名称空间。

    forceAddSchemeHostAndPort

    false

    Boolean

    当该属性为true时,生成的URL为绝对路径,而且会包含主机名及端口号。

    l  <s:a>

    它用来生成超链接,与<s:url>相似!

    <s:a action=”TestAction” namespace=”/”>添加用户

      <s:param name=”name” value=”’张三’”/>

    </s:a>

    l  <s:debug>

    Debug标签用于调试,它在页面中生成一个“[Debug]”超链接,单击这个超链接,可以查看ValueStack和ActionContext中保存的所有对象。

    2 Struts2通用标签之控制标签

    控制标签很好理解,就是流程控制了。例如if、elseif等,以及iterator等。

    l  <s:if>、<s:elseif>、<s:else>

    这种标签大家应该一看就会用了。我们直接给出个小例子看看。

    <!—

    在浏览器中输入:http://localhost:8080/tagtest/index.jsp?score=85

    -->

     

    <s:set name="score"   value="#parameters.score[0]"/>

    <s:property value="#score"/>:

    <s:if test="#score >   100 || #score < 0">

        <s:property value="'输入错误'"/>

    </s:if>

    <s:elseif test="#score >=   90">

        <s:property value="'A'" />

    </s:elseif>

    <s:elseif test="#score >=   80">

        <s:property value="'B'" />

    </s:elseif>

    <s:elseif test="#score >=   70">

        <s:property value="'C'" />

    </s:elseif>

    <s:elseif test="#score >=   60">

        <s:property value="'D'" />

    </s:elseif>

    <s:else>

        <s:property value="'E'"/>

    </s:else>

    l  <s:iterator>

    <s:iterator>标签可以用来迭代一个集合,可以迭代的集合有:Collection、Map、Enumeration、Iterator或者是数组。iterator标签在迭代过程中,会把当前对象暂时压入值栈,这样在子标签中就可以直接访问当前对象的属性(因为当前对象在栈顶),在标签体执行完毕后,位于栈顶的对象就会被删除,在循环的第二圈时,把新的当前对象再压入值栈中。

    <s:iterator value="{'zhangSan','liSi','wangWu' }">

        name: <s:property/><br/>

    </s:iterator>

    如果为<s:iterator>标签指定了var属性,那么当前对象不只是压入到了值栈中,而且还会被添加到OgnlContext中。

    <s:iterator value="{'zhangSan','liSi','wangWu' }" var="name">

        name: <s:property value="#name"/><br/>

    </s:iterator>

    名称

    必需

    默认值

    类型

    说明

    var

    String

    如果指定了该属性,那么迭代的集合中的元素将被保存到OgnlContext中,可以通过该属性的值来引用集合中的元素。该属性几乎不被使用。

    value

    Coolection、Map、Enumeration、Iterator 或数组

    指定迭代的集合。如果没有指定该属性,那么iterator标签将把位于值栈栈顶的对象放入一个新创建的List中进行迭代。

    status

    String

    如果指定了该属性,一个IteratorStatus实例将被放入到OgnlContext中,通过该实例可以获取迭代过程中的一些状态信息。

    IteratorStatus类在org.apahce.struts2.views.jsp包下。下面是对该类常用方法的介绍:

    l  public int getCount():得到当前已经迭代的元素的总数。

    l  public int getIndex():得到当前迭代的元素的索引。

    l  public boolean isEven():判断当前迭代的元素的个数是否是偶数。

    l  public boolean isOdd():判断当前迭代的元素的个数是否是奇数。

    l  public boolean isFirest():判断当前迭代的元素是否是第一个元素。

    l  public boolean isLast():判断当前迭代的元素是否是最后一个元素。

    在OGNL表达式中使用IteratorStatus类的方法时,可以直接使用:count、index、even、odd、first、last属性。

    <s:iterator value='{"one", "two", "three"}'   status="status">

        <s:property value="#status.count"/>,

        <s:property value="#status.index"/>,

        <s:property value="#status.even"/>,

        <s:property value="#status.odd"/>,

        <s:property value="#status.first"/>,

        <s:property value="#status.last"/><br/>

    </s:iterator>

    <hr/>

    <s:iterator value="#{'1':'one','2':'two','3':'three','4':'four'}"   status="st">

    <s:property value="key"/>:<s:property value="value"/><br/>

    </s:iterator>

    3 Struts2标签UI标签之表单标签

      Struts2的表单标签还是比较好用的,但它也存在一些败笔,例如主题这一部分就不是很灵活。所以导致开发中没有公司会使用它提供的主题。

    Struts2标签的优势:

    l  简化代码;

    l  自动数据回显;

    l  指定主题样式(说是优点,但很多人也会认为这是缺点);

    1. 表单标签入门

    我们首先使用一个登录表单来对比一下html表单和Struts2表单的区别。

    <form action="<c:url value='/user/LoginAction.action'/>"   method="post">

      用户名 <input type="text"   name="username"/><br/>

      密 码 <input type="password"   name="password"/><br/>

      <input type="submit"   value="登录"/>

    </form>

    <hr/>

    <s:form action="LoginAction"   namespace="/user">

        <s:textfield name="username" label="用户名" />

        <s:password name="password" label="密 码" />

        <s:submit value="登录" />

    </s:form>

    <form action="/ognl/user/LoginAction.action" method="post">

      用户名 <input type="text" name="username"/><br/>

      密 码 <input type="password" name="password"/><br/>

      <input type="submit" value="登录"/>

    </form>

     

    <hr/>

     

    <form id="LoginAction" name="LoginAction"   action="/ognl/user/LoginAction.action" method="post">

    <table class="wwFormTable">

    <tr>

        <td class="tdLabel"><label for="LoginAction_username" class="label">用户名:</label></td>

        <td

    ><input type="text" name="username"   value="" id="LoginAction_username"/></td>

    </tr>

     

    <tr>

        <td class="tdLabel"><label for="LoginAction_password" class="label">密 码:</label></td>

        <td

    ><input type="password" name="password"   id="LoginAction_password"/></td>

    </tr>

    <tr>

        <td colspan="2"><div align="right"><input type="submit" id="LoginAction_0"   value="登录"/>

    </div></td>

    </tr>

    </table></form>

    通过上面生成的代码可以看来: 

    l  <s:form>

    • 通过action和namespace两部分来指定请求路径,action直接给<action>元素的name值即可,无需给出后缀“.action”;
    • method默认为post;
    • 会自动添加id属性,值与action属性值相同;
    • 整个表单会生成在<table>中。

    l  <s:textfield>

    • 对应<input type=”text”>标签;
    • 通过lable来生成<lable>标签;

    l  <s:password>

    • 对应<input type=”password”>标签;
    • 通过label来生成<lable>标签

    l  <s:submit>

    • 对应<input type=”submit”>标签;
    1. 表单主题

    我们发现,整个表单都会在<table>中生成,这也就说明无需为每个表单项添加<br/>。因为在表格中就无需再换行了。

    生成在表格中是因为<s:form>标签的theme属性的默认值为xhtml,它表示一个主题样式。这个主题样式由Freemarker模板来完成。如果你不希望使用这个xhtml主题,那么有下列3种方法来修改主题:

    l  在<s:textfield>的theme属性指定为simple,那么这个表单项就使用简单主题;

    l  在<s:form>的theme属性指定为simple,那么整个表单都使用简单主题;

    l  设置struts.ui.theme常量为simple,那么所有表单标签的默认主题都是simple。

    当表单设置为theme=”simple”之后:

    <s:form action="LoginAction"   namespace="/user" theme="simple">

        <s:textfield name="username" label="用户名" />

        <s:password name="password" label="密 码" />

        <s:submit value="登录" />

    </s:form>

    <form id="LoginAction" name="LoginAction" action="/ognl/user/LoginAction.action" method="post">
     <input type="text" name="username" value="" id="LoginAction_username"/>
     <input type="password" name="password" id="LoginAction_password"/>
     <input type="submit" id="LoginAction_0" value="登录"/>
    </form>

      这时没有表格来格式化表单,而且也不能生成<lable>,这说明样式都要自来来设定。好处是你重新获得了自由,坏处是一切都要靠自己了。

    1. 自动回显

    我们知道,当表单提交后,再返回到表单页面后,html标签不可能帮我们回显数据。而Struts2的表单标签可以做到这一点。原因是当前Action就在值栈顶,而表单标签会从值栈中获取数据来回显。

    4 表单标签之选择性标签
    1. <s:redio>标签

    表单标签中简化比较大的标签可以不在需要写循环,还有就是自动回显。我们都知道在下拉列表、单选、复选中,手动回显是比较麻烦的事。但使用Struts2的表单标签就方便多了。

    <s:radio list="#{'male':'男','female':'女'}" name="gender"/>

    list指定的是一个Map,默认key为实际值,而value为显示值。

    也可以为list属性指定为list:

    <s:radio list="{'',''}" name="gender"/>

    这时实际值和显示值是相同的。

    1. <s:checkboxlist>标签

      这个东西也很好用,一下子可以显示很多复选按钮。而且自动回显!

    <s:checkboxlist list="#{'read':'看书','netplay':'上网','music':'音乐' }" name="hobby"/>

    1. <s:select>标签

    下拉列表与上面两个标签也一样,都是选择性的!使用它也是无需循环,无需处理循环。

    <s:select name="city"   list="#{'bj':'北京','sh':'上海','gz':'广州'}"/>

    防止重复提交

    1 什么是重复提交

    当用户提供一个表单后,然后使用浏览器后退后,再次提交表单!

    表单重复提交 危害: 刷票、 重复注册、带来服务器访问压力(拒绝服务)如何重复提交:

    l  转发到Action,这时地址栏中的地址是指向Action的,所以可以刷新页面来重复提交;

    l  通过浏览器后退回表单页面,再次提交;

    l  因为服务器很慢,在点击了提交后,页面还没有变化时,再次点击提交。

    2 防止重复提交的原理

    防止表单重复提交的原理就是使用令牌机制!所谓令牌机制就是在请求到达表单页面时,页面会生成令牌值(UUID),然后保存到session和表单的<input type=”hidden”>中。

    然后用户提交表单后,令牌值会发送给Action。这时Action获取session中的令牌值与请求参数中的令牌值进行比较,如果相同,说明这是正常提交,Action会执行操作,然后把session中的令牌值删除。

    当用户后退后,再次提交时,因为没有再次访问表单页面,那么就不会生成新的令牌值,所以参数中的令牌值还是上一次的,而session中已经没有令牌值了,所以Action会判断为重复提交。

    3 Struts2防止重复提交

    防止重复提交需要在页面中生成令牌值,然后在表单和session中分别保存,这个任务可以使用Struts2的标签:<s:token/>来未完成。这个标签会生成令牌值,并保存到session和表单的隐藏字段中。

    在Action中获取session和请求参数中的令牌值进行比较,然后在令牌值无误时,在session中删除令牌值;在令牌值不同时转发到invalid.token结果。这些任务由token拦截器来完成。

    <s:token>标签和token拦截器来完成防止表单重复提交。

    当token拦截器确定当前是重复提交后,会跳转到invalid.token结果,这说明我们需要在指定名为invlalid.token的结果。而且token拦截器还会在actionError中添加错误信息。

        <s:form action="tokenTest"   namespace="/">

           <s:token />

           <s:submit value="呵呵" />

        </s:form>

    public class TokenAction extends ActionSupport {

        @Override

        public String execute() throws Exception   {

           System.out.println("execute()...");

           return NONE;

        }

    }

           <action name="tokenTest" class="cn.itcast.action.TokenAction">

               <result name="invalid.token">/error.jsp</result>

               <interceptor-ref name="defaultStack" />

               <interceptor-ref name="token" />

           </action>

      <body>

        <s:actionerror/>

      </body>

                                                                         

    我们可以通过查看TokenInterceptor,知道这条错误令牌的key值是struts.messages.invalid.token,所以我们可以在国际化资源文件中覆盖这个key值。

  • 相关阅读:
    606. Construct String from Binary Tree
    696. Count Binary Substrings
    POJ 3255 Roadblocks (次短路)
    POJ 2823 Sliding Window (单调队列)
    POJ 1704 Georgia and Bob (博弈)
    UVa 1663 Purifying Machine (二分匹配)
    UVa 10801 Lift Hopping (Dijkstra)
    POJ 3281 Dining (网络流之最大流)
    UVa 11100 The Trip, 2007 (题意+贪心)
    UVaLive 4254 Processor (二分+优先队列)
  • 原文地址:https://www.cnblogs.com/lulu638/p/4322003.html
Copyright © 2011-2022 走看看