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

  • 相关阅读:
    fastjson1.2.22-1.2.24 反序列化命令执行实践测试
    Spring boot JdbcTemplate sql注入测试
    java反序列化命令执行测试实践
    SpringBoot 整合mybatis SQL注入漏洞实践
    SpringBoot整合mybatis入门
    python函数默认参数为可变对象的理解
    python笔记
    python
    python面向对象
    ICMP
  • 原文地址:https://www.cnblogs.com/lulu638/p/4322003.html
Copyright © 2011-2022 走看看