struts2.1.6教程四、OGNL与ValueStack(VS)

1.值栈入门

下面我们建立struts2ognl项目来练习ognl的使用。
步骤一、搭建strust2的开发环境
步骤二、建立LoginAction,主要代码如下:

package com.asm;
public class LoginAction extends ActionSupport{
    private User user;
    public String execute() throws Exception {    
            return SUCCESS;
    }
    ...省略user的get/set方法    
}

步骤三、配置此Action,struts.xml的主要内容如下:

<struts>
    <constant name="struts.devMode" value="true"></constant> 
    <package name="ognl" extends="struts-default">
        <action name="login" class="com.asm.LoginAction">
            <result>/loginSuc.jsp</result>
        </action>
    </package>
</struts>

步骤四、编写login.jsp页面,主要代码如下:

<body>
    <form action="<%=request.getContextPath()%>/login.action"  method="get">
        用户名:<input type="text" name="user.username"><br>
        密  码:<input type="password" name="user.password"><br>
        <input type="submit" value="login">
    </form>
</body>

步骤五、编写loginSuc.jsp页面,主要代码如下:

<body> 
        调试:<s:debug></s:debug>
        获取值栈中的username属性:<s:property value="user.username"/> <br>
</body>

步骤六、发布测试及说明

 

当我们输入用户名并成功跳到logSuc.jsp页面后,会得到登录时输入的用户名信息。下面简要说明这一过程:

(1).login.jsp登录提交登录信息给login.action

 

(2).struts2监听到客户端的login.action请求,按配置文件要求,把此请求交给LoginAction处理。这表示会去new LoginAction(), 当struts2  new出此Action对象后会把这个对象放在值栈中,只是这个Action非常特殊,所以放在值栈中,而放在值栈中的对象是可以直接引用的,放在其它context map中的对象引用时会要求加#。

 

(3).当new LoginAction时,表示它也会初始化此类中的对象,比如这里会去初始化User对象,但是要注意的是如果我们在用户名和密码什么都不输,再来用debug来看值栈中的user是,发现它仍会new此对象,因为尽管我们没用输入值,但是后台的set方法还是要被调用,所以会new出此对象,但是如果我们直接输入.../struts2ognl/login.action时我们会发现跳转到loginSuc.jsp页面时,用debug来看值栈中此User user,发现它的值为null。

第二点要注意的是我们必须为User类提供默认的构造方法,否则将会出现如下错误: java.lang.InstantiationException: com.asm.vo.User

 

总结:

1.Action会在请求时被创建,且会把创建的对象放到值栈中。
2.Action中的对象字段只有在需要时才会以new 的形式初始化,而且这些对象字段必须提供默认的构造方法。
3.ValueStack对象贯穿整个Action的生命周期(每个Action类的对象实例会拥有一个ValueStack对象)。当Struts 2接收到一个.action的请求后,会先建立Action类的对象实例,但并不会调用Action方法,而是先将Action类的相应属性放到ValueStack对象的顶层节点(vs对象相当于一个栈)。

补充:值栈(根)对象也可以直接使用EL表达式访问,比如这里可以直接通过${user.username}来获取username的值,我们知道el表达式只能访问四种scope范围内的对象,那为什么这里能访问到值栈对象呢?原因是struts2对HttpServletRequet进行了一次封装,封装的代码主要是重写了getAttribute方法,简述重写此方法的核心代码:首先在原始的HttpServletRequest对象中查找el表达式中的属性,如果找不到再通过ActionContext获取值栈对象,进而再从值栈对象中查找el表达式中要访问的属性。

 

2.OGNL入门

下面我们在com.asm.vo.User类中增加一个字段private Address addres;,并提供此字段的get/set方法,随后再在login.jsp中增加如下代码:

城  市:

<input type="text" name="user.addres.city"><br>

然后再在loginSuc.jsp中增加如下代码:

获取城市属性:

<s:property value="user.addres.city"/><br>

 

然后测试,会得到登录时输入的城市信息(中文会有乱码)。下面借助此例谈ognl的定义:在这个例子中,我们的LoginAction中有一个User对象,而在User对象中又有一个Address对象,这些对象之间依靠这种类的字段进行关联,或者说是依靠字段属性进行导航,这也就是OGNL的定义:Object Graph Navigation Language:对象图导航图语言,它是建立在值栈技术之上的一种全新语言。

 

补充:用%{}可以取出存在值堆栈中的Action对象,直接调用它的方法.我们在loginSuc.jsp中增加如下内容调用LoginAction中的get方法:


调用值栈对象中的方法:

<s:property value="%{get()}"/> 

LoginACtion中增加的get方法如下:
public String get(){

       return "这是User中的get方法";

}

 

3.普通方法访问

首先在User中增加一个成员方法,代码如下:

public String get(){
        return "这是User中的get方法";
}

在LoginAction中也有类似的get方法,随后再在loginSuc.jsp中增加如下代码:

 

调用值栈对象中的普通方法(1):

 

<s:property value="user.username.length()"/><br>

 

 

调用值栈对象中的普通方法(2):

 

<s:property value="user.get()"/><br>

 

 

调用LoginAction中的普通方法:

<s:property value="get()"/><br>


最后测试,发现这些方法都可以访问到。

 

4.静态方法访问

在LoginAction中增加如下方法:

public static String getSta() {
        return "这是LoginAction中的静态方法";
}

然后在loginSuc.jsp中增加如下代码:

调用Action中的静态方法:

 

<s:property value="@com.asm.LoginAction@getSta()"/><br>

 

 

调用LoginAction中的静态方_方式(2):

 

<s:property value="@vs@getSta()"/><br>

 

 

 

说明:我们在方式二中用到@vs,只有那些值栈中的对象才可以这样写。
然后访问,发现访问不到,因为在struts2.1.6的版本中,struts.ognl.allowStaticMethodAccess的默认值为false,我们只需在struts.xml中增加如下内容:

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


再来访问时便可以访问到。

 

5.默认类Math的访问

在loginSuc.jsp中增加如下代码:
调用Math类中的静态方法:

 

<s:property value="@java.lang.Math@min(1,2)"/><br>

 

 

调用Math类中的静态方法_方式(2):

 

<s:property value="@@min(1,2)"/><br>

 

 

调用Math类中的字段:

 

<s:property value="@@PI"/><br>

 

 

说明:因为是默认的类,所以可以省略类名

 

6.调用普通类的构造方法

建立一个新的类:Student,在此省略代码。
然后在loginSuc.jsp中增加如下代码:
调用普通类中的构造方法  :

<s:property value="new com.asm.vo.Student('jack','20','85.5')"/><br>

调用普通类中的构造方法并访问其字段 :

<s:property value="new com.asm.vo.Student('jack','20','85.5').name"/>

说明:第一种是只new出对象,显示的时候其实是调用对象的toString方法。

 

7.集合对象初步

首先在LoginAction中增加如下字段并提供相应的get/set方法:

private List myList = new ArrayList();
private Set mySet = new HashSet();
private Map myMap = new HashMap();

然后再在execute方法中初始化这些集合对象,代码如下:

myList.add("list1");
myList.add("list2");
myList.add("list3");
myList.add("list4");
mySet.add("set1");
mySet.add("set3");
mySet.add("set1");
mySet.add("set2");
myMap.put("m1", "map1");
myMap.put("m3", "map3");
myMap.put("m2", "map2");

最后在loginSuc.jsp中增加如下代码:

获取List:

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

获取List中的第一个元素:

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

获取Set:

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

获取Set中的第一个元素(set无序,不能取到)

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

获取Map:

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

获取Map中的key=m1的元素的值:<br>

方式一:

<s:property value="myMap.m1"/>

方式二:

<s:property value="myMap['m1']"/><br><hr>

获取List的大小:

<s:property value="myList.size"/>|<s:property value="myList.size()"/><br>

获取Map中所有键:

<s:property value="myMap.keys"/><br>

获取Map中所有值:

<s:property value="myMap.values"/><br>

 

最后测试,这些东西不多作解释。

8.集合对象进阶

首先在LoginAction中增加如下字段并提供相应的get/set方法:

private List studentList = new ArrayList();

然后再在execute中为其初始化赋值,代码如下:

studentList.add(new Student("jack", 20, 86.0f));
studentList.add(new Student("lily", 22, 96.5f));
studentList.add(new Student("tom", 23, 56.5f));

最后在loginSuc.jsp中增加如下代码:

获取List中的Student对象:<s:property value="studentList"/><br>
利用投影获取List中的name属性:<s:property value="studentList.{name}"/><br>
利用投影获取List中的age属性:<s:property value="studentList.{age}"/><br>
利用投影获取List中的第一个对象的name属性:<s:property value="userList[0].username"/>     或者<s:property value="studentList.{name}[0]"/><br>
利用选择获取List中grade>60的student信息:
<s:property value="studentList.{?#this.grade>60}"/><br>
利用选择获取List中grade>60的student名字信息:
<s:property value="studentList.{?#this.grade>60}.{name}"/><br>
利用选择获取List中grade>60的第一个student名字信息:
<s:property value="studentList.{?#this.grade>60}.{name}[0]"/><br>
利用选择获取List中grade>60的第一个student名字信息(链表):
<s:property value="studentList.{^#this.grade>60}.{name}"/><br>
利用选择获取List中grade>60的最后一个student名字信息(链表):
<s:property value="studentList.{$#this.grade>60}.{name}"/><br>

说明:这里重点是说明?#的使用,结合此例来看,studentList中有许多Stutdent对象,我们可以用条件来限制取哪些对象,这些条件必须以?#开始,并且条件要用{}括起。而this是指在判断studentList中的对象是否符合条件的当前对象。?#是指取出符合条件的所有Student对象,而^#是指取出符合条件的第一个对象,$#是指取出符合条件的最后一个对象。

 

9.N语法top语法

 

我们在loginSuc.jsp中增加如下下代码:

N语法[0]:<s:property value="[0]"/><br>
N语法[1]:<s:property value="[1]"/><br>
N语法[0].top:<s:property value="[0].top"/><br>
N语法[1].top:<s:property value="[1].top"/><br>
N语法top:<s:property value="top"/><br>
N语法取值:<s:property value="[0].user.username"/><br>
N语法取值:<s:property value="top.user.username"/><br>

说明:规定栈顶的对象为[0],而我们只使用[0]的意思是从值栈中第一个对象取,一直取至栈底。N的意思是从值栈中的第N个对象开始,取到栈底为止。如果要想访问某个对象,需要使用[N].top,它的意思是取出符合N语法的栈顶对象,比如在这里,[0]会取出两个对象,而[0].top是取出这两个对象的栈顶对象。纯top可以简洁地取出值栈中的栈顶对象。
为什么要提出N语法,当我们通过chain链访问时,值栈中可能有两个以上的Action对象,如果这些对象中存在相同的属性,N便能正确区分他们。通常,这些Action对象的入栈顺序是:先访问先入栈。

从上面的N语法取值实例中,我们知道[N]top语法的一个重要作用就是能通过它们引用值栈对象中的属性。结合前面的五种[N]top语法实例,不难理解这里的取值实例。
补充:在此实例中,我们用<s:debug>调试会发现,值栈中还有一个DefaultTextProvider对象(因为此Action继承自ActionSupport),它的作用是获取资源文件中的内容(其实本质是ActionSupport重写了getText()方法),这也就是在国际化问题中我们能直接调用它的getText()方法的原因。

 

10.获取Stack Context中的信息

我们知道,除了可以从值栈中获取信息,还可以从Stack Context中获取信息,只是要加上#,下面我们通过scope对象来演示。首先是在LoginAction中增加如下字段:

Map myRequest;
Map mySession;
随后再用前面提到的“在Action中获取Scope对象”的方式二来完成这些对象的初始化。即实现RequestAware和SessionAware接口。然后再在execute方法中增加如下内容:
myRequest.put("req", "Req属性");
mySession.put("ses", "Ses属性");
最后在loginSuc.jsp中增加如下代码:
获取Request属性:<s:property value="#request.req"/><br>
获取Session属性:<s:property value="#session.ses"/><br>
获取parameters属性:<s:property value="#parameters.mes"/>

说明:我们获取这些对象都用了#,因为这些对象都是存在一般的Context Map中,而不是存在值栈中。别最后一个信息的获取是因为我们在login.jsp中增加了如下代码:

<input type="hidden" name="mes" value="the message is transfer by hidden">

 

注意:值栈和Stack Context是两种不同的方式存取servletApi的值,一种struts封装好list来使用,一种是map来使用加#号,任选一种或两种都选都可以。

ValueStack 由 OGNL框架实现,可以把它简单的看作一个栈(List) 
Stack Context(保存方式是map类型):stack的上下文,它包含一系列对象,包括 
request,session,attr,application,map 等ValueStack中保存的值可以直接取,而stack中的需要在前面加#(request,session,application)

 

关于这些scope的更多信息可以参看下表:

名称

作用

例子

parameters

包含当前HTTP请求参数的Map

#parameters.id[0]作用相当于request.getParameter("id")

request

包含当前HttpServletRequest的属性(attribute)的Map

#request.userName相当于request.getAttribute("userName")

session

包含当前HttpSession的属性(attribute)的Map

#session.userName相当于session.getAttribute("userName")

application

包含当前应用的ServletContext的属性(attribute)的Map

#application.userName相当于application.getAttribute("userName")

Attr

用于按request > session > application顺序访问其属性

#application.userName相当于application.getAttribute("userName")

 

 

11.总结$ # %的区别

$用于i18n和struts配置文件

#取得ActionContext的值

%将原来的文本串解析为ognl,对于本来就是ognl的文本不起作用。形式:%{要解析的文本串}

 

12.总结OGNL[重点]

 

OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。Struts2使用OGNL作为默认的表达式语言。

相对于EL表达式,它提供了平时我们需要的一些功能,如:支持对象方法调用,支持各类静态方法调用和值访问,支持操作集合对象。OGNL有一个上下文的概念,这个上下文件实质就是一个Map结构,它实现了java.utils.Map接口,在struts2中上下文的实现为ActionContext,下面是上下文的结构示意图:

 

当struts2接受一个请求时,会迅速创建ActionContext,ValueStack,action。然后把action存放进ValueStack,所以action的实例变量可以接受OGNL访问。

访问上下文中的对象需要使用#号标注命名空间,如#application、#session。另外OGNL会设定一个根对象,在struts2中根对象就是ValueStack值栈对象,如果要访问根对象中对象的属性,则可以省略#命名空间,直接访问该对象的属性即可。

 

在struts2中,根对象的实现类为OgnlValueStack,该对象不是我们想象的只存放单个值,而是存放一组对象,在OgnlValueStack类里有一个List类型的变量,就是使用这个List变量来存放一组对象。在root变量(List类型)中处于第一位的对象叫栈顶对象,通常我们在Ognl表达式里直接写上属性的名称即可访问root变量里对象的属性,搜索顺序是从栈顶对象开始寻找,如果栈顶对象不存在该属性,就会从第二个对象寻找,如果没有找到就从第三个对象寻找,依次往下寻找。

注意:struts2中 ,OGNL表达式需要配合struts的标签才可以使用。

 


项目例子

1.LoginAction

package com.asm;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.interceptor.ServletResponseAware;

import com.opensymphony.xwork2.ActionSupport;
public class LoginAction  extends ActionSupport implements ServletRequestAware,ServletResponseAware{


    public HttpServletRequest request;
    public HttpServletResponse response;
    
    @Override
    public void setServletResponse(HttpServletResponse response) {
        this.response = response;
    }

    @Override
    public void setServletRequest(HttpServletRequest request) {
        this.request = request;
    }
    
    private static final long serialVersionUID = 1L;

    private User user;
    private String username2;

    private List myList = new ArrayList();
    private Set mySet = new HashSet();
    private Map myMap = new HashMap();
        
    private List<User> userList = new ArrayList<User>(); 
    
    public String execute() throws Exception {  
        myList.add("list1");
        myList.add("list2");
        myList.add("list3");
        myList.add("list4");
        mySet.add("set1");
        mySet.add("set3");
        mySet.add("set1");
        mySet.add("set2");
        myMap.put("m1", "map1");
        myMap.put("m3", "map3");
        myMap.put("m2", "map2");
        
        userList.add(new User("100","70"));
        userList.add(new User("100","50"));        
        userList.add(new User("100","80"));        
        
        request.setAttribute("req", "reqreqreq");
        request.getSession().setAttribute("ses", "sessesses");
        
            return SUCCESS;
    }
    
    public String ptMethon() throws Exception {    
        return "LoginAction的普通方法";
}    
    
    
    public static String jtMethon() throws Exception {    
        return "LoginAction的静态方法";
}    
    
    
    
    public List getMyList() {
        return myList;
    }

    public void setMyList(List myList) {
        this.myList = myList;
    }

    public Set getMySet() {
        return mySet;
    }

    public void setMySet(Set mySet) {
        this.mySet = mySet;
    }

    public Map getMyMap() {
        return myMap;
    }

    public void setMyMap(Map myMap) {
        this.myMap = myMap;
    }
    public List getUserList() {
        return userList;
    }

    public void setUserList(List userList) {
        this.userList = userList;
    }

    public String getUsername2() {
        return username2;
    }

    public void setUsername2(String username2) {
        this.username2 = username2;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
    
}

 

 

2.User

package com.asm;

public class User {

    private String username;
    private String password;
    
    /*
     * 必须提供默认的构造方法
     */
    public User(){

    }
    public User(String username,String password){
        this.username=username;
        this.password=password;
    }


    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }

    
    public String userMethon() throws Exception {    
        return "user的普通方法";
}    
    
}

 

3.struts.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
    
<struts>
<!-- 包含其他文件 -->

<!-- 2.1.6必须这样开启静态方法调用 -->
<constant name="struts.ognl.allowStaticMethodAccess" value="true"/>
<constant name="struts.i18n.encoding" value="gbk"></constant>
<constant name="struts.configuration.xml.reload" value="true" />
    <constant name="struts.devMode" value="true"></constant> 
    <package name="ognl" extends="struts-default">
        <action name="login" class="com.asm.LoginAction">
            <result>/success.jsp</result>
        </action>
    </package>
</struts>

 

4.login.jsp

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
  </head>
  
<body>
    <form action="<%=request.getContextPath()%>/login.action"  method="get">
        用户名:<input type="text" name="user.username"><br>
        用户名2:<input type="text" name="username2"><br>        
        密  码:<input type="password" name="user.password"><br>
        <input type="submit" value="login">
    </form>
</body>

</html>

 

5.success.jsp

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ page import="com.asm.User"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
  </head>
  


<body>
        调试:<s:debug></s:debug>
        获取值栈中的username属性(1)s:property:<s:property value="user.username"/> <br>
        获取值栈中的username属性(2)EL:${user.username}<br>
         获取值栈中的username属性(3) getAttribute:<%=request.getAttribute("user")%><br>  
         获取值栈中的username属性(3) getAttribute:<%=request.getAttribute("username")%>(要先取出对象再取属性)<br>  
  <c:set var="user2" scope="request" value="<%=request.getAttribute(\"user\")%>"/>
        获取值栈中的username属性(3)EL:${user2.username}<br>  
         获取值栈中的username属性(4)getParameter:<%=request.getParameter("user.username")%><br>  
         <hr/>    
         获取值栈中的username2属性(5)s:property:<s:property value="username2"/> <br>              
        获取值栈中的username2属性(5)getParameter:<%=request.getParameter("username2")%><br>   
        获取值栈中的username2属性(5)EL:${username2}<br> 
         获取值栈中的username属性 (5) getAttribute:<%=request.getAttribute("username2")%><br>   
          <hr/>           
   login的普通方法:<s:property value="ptMethon()"/><br>  
   <!-- 在struts2.1.6的版本中,struts.ognl.allowStaticMethodAccess的默认值为false,改成true就能访问静态方法 -->
   login的静态方法:<s:property value="@com.asm.LoginAction@jtMethon()"/><br>    
   login的静态方法:<s:property value="@vs@jtMethon()"/><br>       
   user实体方法:<s:property value="user.userMethon()"/><br>
          <hr/>  
          -- 集合 --<br>
-- List --<br>
<s:property value="myList"/><br>
<s:property value="myList[0]"/><br>
-- iterator迭代list --<br>
<s:iterator var="list" value="myList" status="s">
序号:<s:property value="#s.index "/><font color="red"><s:property value="list"/></font><br>
</s:iterator>

 -- Set --<br> 
<s:property value="mySet"/><br>
<s:property value="mySet[0]"/>获取Set中的第一个元素(set无序,不能取到)<br>
-- iterator迭代list --<br>
<s:iterator var="set" value="mySet" status="s">
序号:<s:property value="#s.index "/><font color="red"><s:property value="set"/></font><br>
</s:iterator>

-- Map
 --<br>
<s:property value="myMap"/><br>  
<s:property value="myMap.m1"/><br>
<s:property value="myMap['m1']"/><br>
<s:property value="myMap.keys"/><br>
<s:property value="myMap.values"/><br>
-- iterator迭代Map --<br>
<s:iterator var="key" value="myMap.keys" status="s">
序号:<s:property value="#s.index "/><font color="red">key:<s:property value="key"/>
    <c:set var="key2" value="key"/>
    --value:${myMap.get(key)}<br></font>
</s:iterator>
<br>

-- 集合进阶 --<br>
获取List中的User对象:<s:property value="userList"/><br>
利用投影获取List中的username属性:<s:property value="userList.{username}"/><br>
利用投影获取List中的password属性:<s:property value="userList.{password}"/><br>
利用投影获取List中的第一个对象的username属性:<s:property value="userList[0].username"/>   
或者<s:property value="userList.{username}[0]"/><br>
<br>
利用选择获取List中username>60的user信息:
<s:property value="userList.{?#this.username>60}"/><br>
利用选择获取List中username>60的student名字信息:
<s:property value="userList.{?#this.username>60}.{username}"/><br>
利用选择获取List中username>60的第一个student名字信息:
<s:property value="userList.{?#this.username>60}.{username}[0]"/><br>
利用选择获取List中username>60的第一个student名字信息(链表):
<s:property value="userList.{^#this.username>60}.{username}"/><br>
利用选择获取List中username>60的最后一个student名字信息(链表):
<s:property value="userList.{$#this.username>60}.{username}"/><br>
  
-- 获取Stack Context中的信息 --<br>         
获取Request属性:<s:property value="#request.req"/><br>
获取Session属性:<s:property value="#request.session.ses"/><br>
获取Session2属性:<s:property value="#session.ses"/><br>                                    
</body>


</html>

 

 

结果页面

 

 

 

网盘http://pan.baidu.com/s/1mgFtLLA

posted @ 2015-04-15 11:35  crazyYong  阅读(429)  评论(0编辑  收藏  举报