(十六)Struts2的标签库

一、简介

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Struts2的标签库使用OGNL为基础,大大简化了数据的输出,也提供了大量标签来生成页面效果,功能非常强大。
 
 在早期的web应用开发中,jsp页面主要使用jsp脚本来控制输出。jsp页面嵌套大量的java脚本。
 导致页面的可读性较差,可维护性也很低,页面美工人员不懂java,java开发人员也不懂美工设计。
 JSP规范1.1之后,增加了自定义标签库的规范。
 通过使用自定义标签库,在简单的标签中封装复杂的功能,从而避免了jsp页面中出现大量java代码。
 JSP规范制订了一个标准的标签库,JSTL。
 
 Struts2的标签库的标签不依赖于任何表现层技术,也就是说,Struts2提供的标签,可以在各种表现层技术中使用,包括jsp页面。
 
     Struts2的标签主要分为三类
     -UI标签:主要用于生成HTML元素的标签
     -非UI标签:主要用于数据访问,逻辑控制等
     -Ajax标签:用于Ajax支持的标签

 

二、OGNL表达式

(1)概述

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
        Struts2利用内建的OGNL表达式语言支持,大大增加了Struts2的数据访问功能,Xwork在原有的OGNL的基础上,增加了对ValueStack的支持。
 
        我们每次发出请求时,都会产生请求数据,这些数据存放在哪里呢?
        在每次动作执行前,核心控制器都会创建一个ActionContext对象,每次动作访问都会创建。
 
        context map是OGNL上下文对象,是一个Map集合,Struts2中将ActionContext设置为OGNL上下文,但内部还是使用的OGNL context,包含了Stack Context和ValueStack对象, 里面包含着以下内容
        Stack Context里包含 application,session,request,parameters,attr
 
                               |--application  是一个Map,封装着application域的属性
                               |
                               |--session       是一个Map,封装着session域的属性
(ActionContext)context map---|
                               |--request       是一个Map,封装着request域的属性
                               |
                               |--parameters    是一个Map,封装着请求参数
                               |
                               |--attr (searches page, request, session, then application scopes) 是一个Map,封装着四个域的所有属性,依次按照PageContext,request,session,application的顺序进行搜索属性。
                               |       
                               |--value stack(root)  是一个List
                               |
                               |--action (the current action):当前action的引用
 
                         当系统创建Action实例后,Action实例被保存到ValueStack中。
 
 
我们可以在页面上用   <s:debug></s:debug> 标签来查看 ValueStack和Stack Context里的数据。

 

 (2)OGNL中的集合操作

1
2
创建List集合的语法{e1,e2,e3...}
创建Map集合的语法#{key1:value1,key2:value2,....}

 (3)OGNL访问静态成员

1
2
3
4
5
6
7
8
9
10
11
12
13
(一)访问静态属性
     @全类名@静态属性
     例如我们Integer类中有一个MAX_VALUE
     <s:property value="@java.lang.Integer@MAX_VALUE"/> 
     会在页面输出2147483647
 
(二)访问静态方法
     @全类名@静态方法
    Struts2默认禁用访问静态方法,我们可以在struts.xml中设置使用。
    <constant name="struts.ognl.allowStaticMethodAccess" value="true"/> 
 
    然后在jsp页面就可以访问静态方法
    <s:property value="@java.lang.Math@random()"/> 

  (4)OGNL的表单标签

1
2
3
-checkboxlist:复选框。
 -select:下拉列表
 等等很多,可以自己查阅使用。

  (5)OGNL的常用标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
控制标签可以完成流程控制,如分支,循环等,也可完成对集合的合并、排序等。
 
(一)if/elseif/else标签
       用于分支控制,根据boolean表达式,决定是否计算,输出内容等。
       三个标签可以组合使用,但只有<s:if ../>标签可以单独使用
       语法:
           <s:if test="表达式">
               标签体
           </s:if>
           <s:elseif test="表达式">
               标签体
           </s:elseif>
           ....
           可以有多个elseif
           ....
           <s:else>
               标签体
           </s:else>
 
 
   (二)iterator标签
       用于对集合进行迭代,这里的集合包括List,set和数组,也可对Map进行迭代输出。
       属性
       -value:可选属性,指定被迭代的集合。如果没有指定该属性,则迭代ValueStack栈顶的集合。
       -var:可选属性,指当前迭代的集合元素,如果写了该属性,把var的值作为key,当前遍历的元素作为value,存到Stack Context中。如果不写该属性,就把当前遍历的元素压入栈顶。
       -status:可选属性:是一个对象的名称,该对象包含一些迭代时的计数信息方法,该对象放在Stack Context中。
               *   int getCount();返回当前迭代了几个元素
               *   int getIndex();当前迭代元素的索引
               *   boolean  isEven()/isOdd();当前元素的索引是否是偶数/奇数。
               *   boolean  isFirst()/isLast();当前元素是否是第一个/最后一个元素  
       -begin/end:开始和结束的索引
       -step:步长   
 
   (三)set标签
       将var属性作为Key,为字符串,将Value属性作为Value,为OGNL表达式,存到StackContext中。
       <s:set value="'test'" var="str"/>
 
   (四)append标签
       用于将多个集合对象拼接起来,组成一个集合,通过这种拼接,就可以使用一个<s:iterator />标签完成对多个集合的迭代
       var属性:指定新拼接生成的新集合名称 并且放入stack Context中
       param属性:指定一个集合
 
       <s:append var="newList">
               <s:param value="{e1,e2.e3...}"/>
               ....
       </s:append>
 
   (五)generator标签
       用于将字符串按照指定分隔符分割成多个子串(一个list集合来存放)。临时生成的多个字串可以进行迭代。
       count:可选属性。指定生成集合中元素的总数。
       value:必填属性。指定被解析的字符串
       separator:必填属性。指定字符串的分隔符
       var:可选属性。如果指定该属性,则生成的集合以该名称放入Stack Context中。
 
       <s:generator separator="," val="'e1,e2,e3'">
           <s:iterator status="st">
                   <s:property/>
           </s:iterator>
       </s:generator> 
 
   (六)action标签
       允许在jsp页面调用action。如果指定了executeResult参数的属性值为true,该标签还会把action的处理结果包含在本页面中。
 
   (七)url标签
       用于生成一个url地址。
       属性
       *action:可选属性。指定url地址为哪个action,不写的话,使用Value为url地址
       *value:可选属性。指定url地址为哪个action,不写的话,使用action为url地址
       *method:可选属性。指定action的方法
   <s:url value="save">
       <s:param name="name" value="'zhangsan'"></s:param>
   </s:url>

  (6)OGNL的数据存取

1
我们发送请求时,核心控制器会创建ActionContext,里面包含Stack Context和ValueStack。

 

  

三、StackContext中存取数据

(1)向StackContext中存放数据,ActionContext是一个Map

 

1
2
3
4
5
6
7
8
9
10
第一步:创建一个Action类
public class SaveDemo extends ActionSupport {
    public String execute(){
        //获取ActionContext
        ActionContext context=ActionContext.getContext();
        //存放数据
        context.put("hello", "hello context");
        return SUCCESS;
    }
}

 

配置Action

1
2
3
<action name="save" class="com.cad.struts2.action.SaveDemo">
    <result>/Demo.jsp</result>
</action>

查看ActionContext里的内容,使用<s:debug />标签 会发现有一行 hello hello context 说明已经存入。

 

(2)向StackContext中的Session,application,attr等里面存放数据

 我们前面说过ActionContext是一个Map,里面包含的request,session等也都是Map。

第一步:创建Action类

1
2
3
4
5
6
7
8
9
10
11
public class SaveDemo extends ActionSupport {
    public String execute(){
        //第一种获取ActionContext中的session,是一个Map集合
        Map<String,Object> session=ActionContext.getContext().getSession();
        session.put("hello", "hello session"); 
        //第二种,获取ServletAPI,存放
        HttpSession s=ServletActionContext.getRequest().getSession();
        s.setAttribute("session", "session888");
        return SUCCESS;
    }
}

  

第二步:Stack Context中取数据

1
2
3
4
5
6
7
<body>
    <!--取ActionContext中的数据,通过#key读取-->
    <s:property value="#hello"/>
    <br>
    <!--取Session中,通过#key.session map中的key-->
    <s:property value="#session.hello"/>
</body>

 

四、ValueStack中存取数据

  struts2在请求到来时,会创建ValueStack,将当前的Action对象放入栈顶。 Struts2把ValueStack存放在request中,所以我们可以在request中获取ValueStack对象。 ValueStack是值栈,先进后出,后进先出。

 

 (1)存数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SaveDemo extends ActionSupport {
    public String execute(){
        /*//获取ValueStack对象
        HttpServletRequest request=ServletActionContext.getRequest();
        ValueStack vs1=(ValueStack) request.getAttribute("struts.valueStack");
        System.out.println(vs1);
 
        //第二种获取ValueStack的方法
        ActionContext context=ActionContext.getContext();
        Map<String,Object> map=(Map<String, Object>) context.get("request");
        ValueStack vs2=(ValueStack) map.get("struts.valueStack");
        System.out.println(vs2);*/
 
        //第三种方式获取ValueStack方法
        ActionContext context=ActionContext.getContext();
        ValueStack vs3=context.getValueStack();
        //将对象压栈
        vs3.push(new User("张三",18));
        return SUCCESS;
    }
}

  (2)取数据

1
2
3
4
//只能取对象中的属性,ValueStack是list集合,里面是一个一个元素,我们只能取元素的属性,比如user对象的username,age等,不能取这个对象
//由于ValueStack是根对象,取ValueStack中的对象属性时,不使用#。
//从栈顶开始逐个对象查找指定的属性名称,只要找到就不再继续查找
<s:property value="username"/>

  (3)ValueStack案例

 

我们在我们的Action类中也设置一个username属性

 

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SaveDemo extends ActionSupport {
    private String username="李四";
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String execute(){
 
        ActionContext context=ActionContext.getContext();
        ValueStack vs3=context.getValueStack();
        vs3.push(new User("张三",18));
 
 
        return SUCCESS;
    }
}

  

1
2
3
4
5
6
7
8
9
10
由于我们发送请求时,会将请求的Action实例先压入栈,然后再将我们的对象压栈
                如图,栈里有两个username,我们怎么取动作类里的username呢?
 
                可以使用索引来查找,从0开始,代表属性的索引 所以。
                    <s:property value="[0].username"/><br>
                    <s :property value="[1].username"/>
 
                    输出 :
                            张三
                            李四

  (4)ValueStack的其他方法

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    -setValue(String expr,Object value):expr是OGNL表达式,value是数据。这个方法是存放数据,存到哪里看OGNL表达式
    如果OGNL表达式使用#,则存放到ActionContext中
 
    没有使用,就存放到ValueStack中
 
    vs3.setValue("#username", "王五"); //将数据存到ActionContext中,username是key,王五是值
 
    vs3.setValue("username", "赵六");  //将ValueStack中的第一个username替换成赵六。如果类中没有username的set方法。就不会替换和设置。
 
 
 
-void set(String key,Object o); key是Map的key,o是Map的value。
    这个方法如果栈顶是一个Map元素,就把key作为map的key,把Object作为map的value。设置进去。
 
    如果栈顶不是Map元素,则创建一个Map对象,把Key作为map的key,Object作为map的value,压入栈顶。
 
    vs3.set("user1",new User("刘德华",54) );
 
    在页面中怎么取呢?这是一个Map对象,是key和value,并没有属性
        <s:property value="user1.username"/>
 
 
    当我们使用<s:property  />元素不指定Value时,默认输出当前栈顶的元素。
 
-findValue(String expr):根据OGNL表达式查找
 其实我们的<s:property  />标签的原理就是这个
     ValueStack vs=ActionContext.getContext().getValueStack();
     Object obj=vs.findValue(OGNL表达式)

 

 

五、Struts2对EL表达式的改变

我们来看一个例子,我们先创建一个Action,action有一个属性username

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class SaveDemo extends ActionSupport {
                private String username="Action类中的值";
 
 
                public String getUsername() {
                    return username;
                }
 
 
                public void setUsername(String username) {
                    this.username = username;
                }
 
 
 
                public String execute(){
                    return SUCCESS;
                }
            }

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
        我们在动作类中有一个username属性,当我们请求action时,该Action对象被压入栈顶。该属性也存在Action对象中。          
 
        我们在页面输出这个username,用EL表达式和OGNL表达式
                <body>
                    <s:debug></s:debug>
                    EL表达式:${username }<br>
                    OGNL表达式:<s:property value="username"/>
                </body>
 
EL表达式是按照指定的域的顺序查找,我们没有向域中存入数据,所以应该查找不出来,但结果却出乎意料
 
结果:
        EL表达式:Action类中的值
        OGNL表达式:Action类中的值  
 
这是为什么呢?

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
我们再来向request域中存值,来看看EL表达式是否还和以前一样按照顺序取值    
                public class SaveDemo extends ActionSupport {
                    private String username="Action类中的值";
 
 
                    public String getUsername() {
                        return username;
                    }
 
 
                    public void setUsername(String username) {
                        this.username = username;
                    }
 
 
 
                    public String execute(){
                        HttpServletRequest request=ServletActionContext.getRequest();
                        request.setAttribute("username", "request域中的值");
 
                        return SUCCESS;
                    }
                }
 
                这时候输出结果
                        request域中的值
                        Action类中的值

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
我们接着向Session中放入值
 
            public class SaveDemo extends ActionSupport {
                private String username="Action类中的值";
 
 
                public String getUsername() {
                    return username;
                }
 
 
                public void setUsername(String username) {
                    this.username = username;
                }
 
 
 
                public String execute(){
                    HttpServletRequest request=ServletActionContext.getRequest();
 
                    HttpSession session=request.getSession();
                    session.setAttribute("username", "session中的值");
                    return SUCCESS;
                }
            }
            页面中的EL表达式应该显示Session中的值,结果却又出人意料
            结果:
                EL表达式:Action类中的值
                OGNL表达式:Action类中的值 
 
这就说明EL的取值顺序改变了。这是怎么回事呢?
1
2
3
4
5
这是因为struts2对request进行了包装
通过StrutsRequestWrapper类对request进行包装
 
struts2通过EL表达式获取数据时,先从request中找,找到就返回该属性。
如果没找到,就从ValueStack中找,如果没找到,就去StackContext map中找,没找到再去session,application中找,这就是struts2对EL表达式的改变。

  

  

 

 

posted @   跃小云  阅读(187)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示