深入分析JavaWeb 48 -- Struts2中OGNL表达式与ValueStack

一、OGNL表达式

1、OGNL表达式介绍

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

OGNL相对其它表达式语言具有下面几大优势:

  • 1、支持对象方法调用,如xxx.doSomeSpecial();
  • 2、支持类静态的方法调用和值访问,表达式的格式:

    @[类全名(包括包路径)]@[方法名 | 值名],例如:@[email protected](‘foo %s‘, ‘bar‘)

    @[email protected]_NAME

    设置 struts.ognl.allowStaticMethodAccess=true

  • 3、支持赋值操作和表达式串联,如price=100, discount=0.8,

    calculatePrice(),这个表达式会返回80;

  • 4、访问OGNL上下文(OGNL context)和ActionContext(重点);
  • 5、操作集合对象。

    Ognl 有一个上下文(Context)概念,说白了上下文就是一个MAP结构,它实现了java.utils.Map的接口.

简单使用示例:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
  <head>
    <title>演示OGNL的主要功能</title>
  </head>

  <body>
    <br/>在Struts2中使用ONGL表达式,必须把表达式放到Struts2中的标签中,才能使用(JSP页面)<br/>
    <br/>调用普通对象的普通方法<br/>
    <!-- JSTL:c:out 功能和 s:property暂时任务输出内容到页面上 .property的value属性的取值就是一个OGNL表达式-->
    <s:property value="‘lxc‘.length()"/><br/>
    <s:property value="‘lxc‘.charAt(1)"/>
    <br/>调用静态字段<br/>
    <s:property value="@[email protected]_VALUE"/>
  </body>
</html>

二、ValueStack&ActionContext(非常重点:值栈)

ValueStack实际是一个接口,在Struts2中利用OGNL时,实际上使用的是实现了该接口的OgnlValueStack类,这个类是Struts2利用OGNL的基础 。

ValueStack(值栈): 贯穿整个 Action 的生命周期(每个 Action 类的对象实例都拥有一ValueStack 对象). 相当于一个数据的中转站. 在其中保存当前 Action 对象和其他相关对象.

1、针对用户的每次动作访问,都会创建属于自己的ValueStack对象

在 ValueStack 对象的内部有两个逻辑部分:

(1)ObjectStack: Struts 把动作和相关对象压入 ObjectStack 中–List

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

这些关系映射包括:

  • parameters: 该 Map 中包含当前请求的请求参数
  • request: 该 Map 中包含当前 request 对象中的所有属性
  • session: 该 Map 中包含当前 session 对象中的所有属性
  • application:该 Map 中包含当前 application 对象中的所有属性
  • attr: 该 Map 按如下顺序来检索某个属性: request, session, application

为了更好的理解可以断点执行如下代码:

Object ctx=ServletActionContext.getRequest().getAttribute("struts.valueStack");

三、理解OGNL Context

ValueStack 类包含两个重要的属性 一个root和一个context。其中root本质上是一个ArrayList. 而context 是一个Map(更确切的说是一个OgnlContext对象)

在这个OgnlContext对象(context)中,有一个默认的顶层对象 _root,OGNL访问context中这个默认顶层对象中的元素时,是不需要#号的,直接通过元素的名称来进行访问,

而访问其他对象时,如 request、session、attr等,则需要#号引用。

注:Struts2将ValueStack的root对象赋值给了OgnlContext 中的_root对象,在ValueStack的root对象中,保存着调用Action的实例,因此,在页面上通过Struts2标签访问Action 的属性时,就不需要通过#号来引用

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

其中ActionContext提供了对ognl上下文对象中数据操作的方法.

 ServletActionContext.getRequest().setAttribute("username", "username_request");
 ServletActionContext.getServletContext().setAttribute("username", "username_application");
 ServletActionContext.getContext().getSession().put("username", "username_session");

 ValueStack valueStack=ServletActionContext.getContext().getValueStack();
 System.out.println("valueStack       "+valueStack);
 valueStack.set("username", "username_valueStack");

总结 :ognl Context包含 ObjectStack属性和ContextMap属性

四、一些常用方法

1、ValueStack中的的常用方法

功能一定要知道干什么的。实现类OgnlValueStack

- getContext():返回的是一个Map<String,Object>

- *getRoot():返回的是一个CompoundRoot。

- CompoundRoot就是一个ArrayList的子类(就是一个List)。实现了一个栈结构

  • CompoundRoot():默认的构造方法。实际就是创建一个List集合
  • CompoundRoot(List):用一个集合来初始化一个CompoundRoot对象
  • cutStack(int):按照集合中的索引,剪出一个的List集合
  • peek():取集合中的第一个元素。get(0)
  • pop():从集合中删除第一个元素
  • push(Object):向集合中的第一个元素位置插入数据
  • peek():CompoundRoot中的第一个元素。get(0)
  • pop():从CompoundRoot集合中删除第一个元素
  • push(Object):向CompoundRoot集合中的第一个元素位置插入数据
  • size():获取 CompoundRoot这个集合的大小
  • setValue(String,Object):第一个参数,是OGNL表达式。向自己(getContext())得到那个Map中尝试放东西。
  • setParameter(String,Object):第一个参数,是OGNL表达式。向自己(getContext())得到那个Map中尝试放东西。
  • findString(String expr):返回的是一个String类型的,否则报错.第一个参数,是OGNL表达式。
  • findValue():返回的是一个Object。第一个参数,是OGNL表达式。

2、ActionContext

static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>();

每个线程都会有唯一的ActionContext,且一个线程中只有一个ActionContext。

  • getApplication():返回的是一个Map
  • getContext():放回的当前线程上的ActionContext对象
  • getContextMap():返回ActionCOntext中的那一个Map
  • getParameters():返回的是一个Map
  • getSession():返回的是一个Map
  • getValueStack():返回的是当前线程中的ValueStack实例
  • get(String):从ActionContext的Map中获取数据

最终总结:

3、ServletActionContext决定contextMap中会存哪些数据

总结:ValueStack有一个根的list叫CompundRoot和一个contextMapMap<String,String>getContext(), 而ActionContext中存储的Map就是ValueStack中的那个Map。都是从ActionContext的那个大Map中获取的。

4、在执行动作方法前,contextMap中放了以下东西

contextMap中

k

5、利用OGNL获取ValueStack中:根栈和contextMap中的数据

原则:OGNL表达式如果以#开头,访问的contextMap中的数据

如果不以#开头,是访问的根栈中的对象的属性

1、在动作类中Demo1Action中重写execute方法

package com.itheima.actions;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;

public class Demo1Action extends ActionSupport{
    private String username = "刘小晨";
//  private String p="actionP";

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

//  public String getP() {
//      return p;
//  }
//
//  public void setP(String p) {
//      this.p = p;
//  }

    public String execute() throws Exception {
//      ServletActionContext.getRequest().setAttribute("p", "rp");//ServletRequest中的request
        ServletActionContext.getRequest().getSession().setAttribute("p", "sp");//HttpSession中的session
        ServletActionContext.getServletContext().setAttribute("p", "ap");//ServletContext中的application

        ValueStack vs = ActionContext.getContext().getValueStack();
        System.out.println(vs);
        return super.execute();
    }

}

配置struts.xml

<struts>
    <constant name="struts.devMode" value="true" />
    <package name="p1" extends="struts-default">
        <action name="showContextMap" class="com.itheima.actions.Demo1Action">
            <result>/contextMap.jsp</result>
        </action>
    </package>
</struts>

编写contextMap.jsp页面

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>OGNL取contextMap中的数据</title>
  </head>

  <body>
    <br/>取contextMap中的根中(List类型的,实际类型CompoundRoot)的数据.<br/>
    <s:property value="locale"/>
    <s:property value="p" /><br/><!--从栈顶开始搜索Map的key或者是对象的属性-->
    <s:property value="[0]" /><br/><!--不是栈顶对象,是从索引为0处砍出来的一个新集合-->
    <s:property value="[0].top" /><br/><!--栈顶对象-->
    <!-- value="username"的涵义:从根中的栈顶开始,对每个对象搜索他的getUsername()方法,找到为止 -->
    <br/>取contextMap中的其他的数据(非根中的).<br/>

    <s:property value="#request"/><br/>
    动作中的:<s:property value="p"/><br/>
    请求范围:<s:property value="#request.p"/><br/>
    会话范围:<s:property value="#session.p"/><br/>
    应用范围:<s:property value="#application.p"/><br/>
    <!-- 依次从动作\页面\请求\会话范围\应用范围查找名称为p的对象 -->
    attr:<s:property value="#attr.p"/><br/>

    <!-- 显示出来的东西并不是contextMap中所有的东西,只是大部分 -->
    <s:debug></s:debug>
  </body>
</html>

数据内存结构

6、ValueStack的常用方法

  • void set(String key,Object value):先获取根栈栈顶的Map,如果不存在,压入一个新的Map

  • void setValue(String ,Object):String是一个OGNL表达式。如果表达式以#开头,操作contextMap。如果不是,设置根栈中对象的某个属性,从顶到尾依次搜寻。

  • Object findValue(String expr):参数是一个OGNL表达式。如果以#开头,从contextMap中找key值所对应的对象。如果不是以#开头,搜索根栈中对象的属性(getter方法)

    特别注意:如果编写的表达式不是以#开头,先搜索根栈对象的所有属性,如果没有找到,会把它当做key值到contextMap中找。

  • String findString(String expr):把OGNL表达式获取的对象转换成String

7、contextMap中放的常用数据

request:请求范围的数据。即ServletRequest中的那个Map

parameters:请求参数的数据。即request.getParameterMap得到

application:应用范围的数据。即ServletContext中的那个Map

session:会话范围的数据。即HttpSession中的那个Map

attr:也是一个Map。会从以下Map中依次搜索:request、session、application

五、OGNL的一些其他操作

1、投影

1、 集合的投影(只输出部分属性)

 <s:iterator value="allList.{name}" var="person">
           <s:property/>  <br>
 </s:iterator>

2、 过滤条件:this 表示集合中的元素;

a.“?#”:过滤所有符合条件的集合,如:users.{?#this.age > 19};

b.“^#”:过滤第一个符合条件的元素,如:users.{^#this.age > 19};

c.“$#</code>”:过滤最后一个符合条件的元素,如:<code>users.{$#this.age > 19} 。

<s:iterator value="allList.{?#this.age>25}" var="person">
        <s:property value="name"/>  xxxxxx  <s:property value="age"/> <br>
 </s:iterator>

3、 集合的投影和过滤

投影(过滤)操作返回的是一个集合,可以使用索引取得集合中指定的

元素,如:users.{?#this.age > 19}[0]

<s:iterator value="allList.{?#this.age>25}.{name}" var="person">
        <s:property/><br>
    </s:iterator> 

 <s:iterator value="allList.{?#this.age>25}[0]" var="person">
        <s:property/><br>
  </s:iterator>

编写TagDemo1Action

package com.itheima.actions;

import java.util.ArrayList;
import java.util.List;

import com.itheima.domain.Person;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;

public class TagDemo1Action extends ActionSupport {
    private String username="刘小晨";
    private List<Person> ps = new ArrayList<Person>();
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public List<Person> getPs() {
        return ps;
    }

    public void setPs(List<Person> ps) {
        this.ps = ps;
    }

    public String execute() throws Exception {
        //初始化一些人
        ps.add(new Person("安康", "女性", 7000));
        ps.add(new Person("唐诗诗", "男性", 10000));
        ps.add(new Person("王卫星", "有待鉴定", 10000));

        ActionContext.getContext().getValueStack().setValue("#gender", "美女");
        return SUCCESS;
    }

}

编写jsp页面

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>OGNL投影等操作</title>
  </head>

  <body>
    <br/>--------OGNL的投影查询----------<br/>
    <table border="1">
        <tr>
            <th>姓名</th>
        </tr>
        <!-- ps.{nickname} ---List<String>  var=“p”引用的是一个String类型的,就代表着匿名  -->
         <s:iterator value="ps.{nickname}" var="p">
            <tr>
                <td><s:property value="#p"/></td>
            </tr>
         </s:iterator>
     </table>

     <hr/>
     <br/>--------OGNL的过滤----------<br/>
     <table border="1">
        <tr>
            <th>姓名</th>
            <th>性别</th>
            <th>薪水</th>
        </tr>
         <s:iterator value="ps.{?#this.salary>8000}" var="p">
            <tr>
                <td>${p.nickname}</td>
                <td><s:property value="#p.gender"/></td>
                <td>${p.salary}</td>
            </tr>
         </s:iterator>
     </table>
     <table border="1">
        <tr>
            <th>姓名</th>
            <th>性别</th>
            <th>薪水</th>
        </tr>
         <s:iterator value="ps.{^#this.salary>8000}" var="p">
            <tr>
                <td>${p.nickname}</td>
                <td><s:property value="#p.gender"/></td>
                <td>${p.salary}</td>
            </tr>
         </s:iterator>
     </table>
     <table border="1">
        <tr>
            <th>姓名</th>
            <th>性别</th>
            <th>薪水</th>
        </tr>
         <s:iterator value="ps.{$#this.salary>8000}" var="p">
            <tr>
                <td>${p.nickname}</td>
                <td><s:property value="#p.gender"/></td>
                <td>${p.salary}</td>
            </tr>
         </s:iterator>
     </table>
    <s:debug></s:debug>
  </body>
</html>

2、创建集合对象

构造Map,如#{‘foo1’:‘bar1’, ‘foo2’:‘bar2’}。这种方式常用在给radio或select、checkbox等标签赋值上

jsp页面:

 <s:radio list=“#{‘male’:‘男’,‘female’:‘女’}”  name=“sex”  label=“性别” /> 

运行结果是

<input type="radio" name="sex" id="sexmale" value="male"/>男
<input type="radio" name="sex" id="sexfemale" value="female"/>女

Action中的代码:

Map map=new HashMap();
map.put("male", "男");
map.put("female", "女");
ServletActionContext.getRequest().setAttribute("map", map);

jsp页面:

  <s:property value="#request.map.male"/><br>
  <s:property value="#request.map[‘female‘]"/><br>

运行结果是

男 女

 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>利用OGNL创建List和Map(很重要)</title>
    <s:head/>
  </head>

  <body>
        <!--
        规律:
            通用标签:
                value属性:大部分都是OGNL表达式(90%)
            UI标签:表单有关
                value属性:大部分都不是OGNL表达式(90%)

            如果要当做OGNL表达式对待:使用%{}
            如果要把OGNL当做字符串对待:使用‘‘
         -->
        <br/>-----创建List-------<br/>
        <s:iterator value="{‘a‘,‘b‘,‘c‘}" var="s">
            <s:property/><br/>
        </s:iterator>
        <br/>-----创建Map----有点像Json---<br/>
        <s:iterator value="#{‘a‘:‘aaa‘,‘b‘:‘bbb‘,‘c‘:‘ccc‘}" var="me">
            <s:property value="#me.key"/>=<s:property value="#me.value"/><br/>
        </s:iterator>
        <br/>-----把字符串当做ONGL表达式-------<br/>
        <s:set value="‘昵称‘" var="usernameLable"></s:set>
        <s:textfield name="username" label="%{usernameLable}" value="abc" requiredLabel="true"></s:textfield>
  </body>
</html>

3、OGNL中的%{}操作符的用法

作用:把普通字符串当做OGNL表达式来用

知识:把OGNL表达式当做普通字符串对待,请使用单引号或双引号

4、OGNL中的$用法

  • 在struts.xml配置文件和国际化消息资源文件中,使用OGNL,放在${}中

    以下是在配置文件中使用OGNL表达式

<param name="contentDisposition">attachment;filename=${@java.net.URLEncoder@encode(fileName,"UTF-8")}</param>
  • 在JSP中使用${}

    有两个主要的用途

    • 用于在国际化资源文件中,引用OGNL表达式
    • 在Struts 2配置文件中,引用OGNL表达式

      EL表达式还是原有功能,如果在域范围中找不到,{}内部的东西就变成了OGNL表达式。

5、EL获取数据

    <%--
    <br/>--------用OGNL来取-----------<br/>
    OGNL_REQUEST:<s:property value="#request.p"/><br/>
    OGNL_SESSION:<s:property value="#session.p"/><br/>
    OGNL_APPLICATION:<s:property value="#application.p"/><br/>
    <br/>--------用EL来取-----------<br/>
    OGNL_REQUEST:${requestScope.p}<br/>
    OGNL_SESSION:${sessionScope.p}<br/>
    OGNL_APPLICATION:${applicationScope.p}<br/>
    <br/>------------EL取数据特殊问题:Struts2对EL的功能进行了改写<br/>
    ${p}从request范围中取数据<br/>
     --%>

     ${p}<br/>
    <s:debug></s:debug>
时间: 2024-10-03 13:45:09

深入分析JavaWeb 48 -- Struts2中OGNL表达式与ValueStack的相关文章

Struts2中OGNL表达式的用法

OGNL的全称是Object Graph Navigation Language(对象图导航语言),它是一种强大的表达式语言,能通过简单一致的表达式语法来读取和设置Java对象的属性值,调用对象的方法,遍历整个对象的结构图,实现字段类型转换等功能. 一.为什么要使用OGNL 视图层的表达式语言通常是用来简化数据的访问操作,取代Java脚本代码,提供更清晰的视图层实现.比如,要获取user对象的age属性,利用OGNL表达式可以写成: <s:property value="user.age&

深入分析JavaWeb Item49 -- Struts2中常用标签与主题

一.非UI标签 1.property标签 property标签用于输出指定值: * default:可选属性, 如果需要输出的属性值为null,则显示该属性指定的值 * escape:可选属性,指定是否格式化HTML代码. * value: 可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值. 2.set标签 set标签用于将某个值放入指定范围. var:变量的名字,name,id和var表达的含义是一样的,name,id被var替代 scope:指定变量

深入分析JavaWeb 49 -- Struts2中常用标签与主题

一.非UI标签 1.property标签 property标签用于输出指定值: * default:可选属性, 如果需要输出的属性值为null,则显示该属性指定的值 * escape:可选属性,指定是否格式化HTML代码. * value: 可选属性,指定需要输出的属性值,如果没有指定该属性,则默认输出ValueStack栈顶的值. 2.set标签 set标签用于将某个值放入指定范围. var:变量的名字,name,id和var表达的含义是一样的,name,id被var替代 scope:指定变量

struts2 与 OGNL 表达式,jsp中 利用ognl 在valuestack中取值

在Struts2中,一个请求在最终到达Action的方法之前,Action对象本身会被压入ValueStack(实际上就是放到ValueStack的CompoundRoot中),所以Action对象是CompoundRoot中的一个元素.看下面的代码: public class UserAction { private String username; private Integer age; private boolean valid; //查看用户的详细信息 public String de

struts2中ognl标签详解

<body> <s:set name="age" value="61" /> <!-- if elseif else 参数test:决定标志里的内容是否显示的表达式,类型boolean 注:else标志没有这个参数 --> <h3>if语句</h3> <s:if test="{age>60}"> 老年人 </s:if> <s:elseif test=&

Struts2之OGNL表达式

OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目.Struts2框架使用OGNL作为默认 的表达式语言. 相对于EL表达式,它提供了平时我们需要的一些功能,如: 支持对象方法调用,如xxx.sayHello(); 支持类静态方法调用和常量值访问,表达式的格式为@[类全名(包括包路径)]@[方法名|值名],例如:@[email protected]('foo%s','bar')或@[email protected]_NAME;

struts2的OGNL表达式

基本概念: OGNL(Object Graph Navigation Language),是一种表达式语言.使用这种表达式语言,你可以通过某种表达式语法,存取Java对象树中的任意属性.调用Java对象树的方法.同时能够自动实现必要的类型转化.如果我们把表达式看做是一个带有语义的字符串,那么OGNL无疑成为了这个语义字符串与Java对象之间沟通的桥梁. OGNL的API看起来就是两个简单的静态方法: public static Object getValue( Object tree, Map

Struts2:OGNL表达式详解

OGNL用于操作ValueStack中的数据.它负责两件事.第一是在接收HTTP请求时将请求中的字符串转换成Java中的类型,比如int.String等,并赋值给ValueStack上的JavaBean,第二是在页面渲染的时候,从ValueStack中获取属性,并将Java类型转换成字符串进行输出.整个过程如下图: (侵权可删) 在HTTP的请求参数中,可以包含OGNL表达式(这就是当初Struts2爆重大漏洞的原因了). user.name=xxx表示给user属性中的name属性进行赋值.

深入分析JavaWeb 45 -- Struts2封装请求参数与类型转换

作为MVC框架,必须要负责解析HTTP请求参数,并将其封装到Model对象中,Struts2提供了非常强大的类型转换机制用于请求数据 到 model对象的封装. 1.Struts2 提供三种数据封装的方式 Action 本身作为model对象,通过成员setter封装 创建独立model对象,页面通过ognl表达式封装 使用ModelDriven接口,对请求数据进行封装 1. 方式一:在动作类中成员变量给予初始值. 在配置文件中struts.xml中 <package name="p1&q