OGNL简介

OGNL,即Object-Graph Navigation Language,对象视图导航语言,是一种数据访问语言,比EL表达式更加强大:

  • EL只能从11个内置对象中取值,且只能获取属性,不能调用对象的方法。
  • OGNL可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图。

OGNL是可以单独使用的。OGNL并不属于Struts2,只不过Struts2觉得OGNL不错,把OGNL给整合进来了。

Struts2的8个核心jar包中已经包含了OGNL的jar包,不需要我们再导包。


ValueStack(值栈) 简介

ValueStack是Struts2的一个接口,用于存储Struts中的数据。ValueStack接口有一个实现类OgnlValueStack。

创建一个Action实例时,会自动为这个Action实例创建一个ActionContext实例,创建ActionContext实例时,又会自动创建一个ValueStack(OgnlValueStack)的实例,将OgnlValueStack的引用放到ActionContext中,将ActionContext的引用放到Action中。

这三者的生命周期是一致的。

ActionContext存储数据,实际是用ActionContext中的ValueStack来存储数据。


ValueStack的内部结构

ValueStack由2部分组成:root、context。

root部分是一个CompoundRoot对象,extends  ArrayList,内部维护了一个列表。

context部分是一个OgnlContext对象,implements Map,内部维护了一个Map,用来存放常用对象的引用,其中也包括root部分的引用,所以OgnlContext其实是可以访问到所有的数据的。

Ognl表达式使用的数据就是ValueStack中的数据。

ActionContext能获取到域对象(Map)、获取到原生的Servlet对象(request、response、session),就是因为ActionContext内部有ValueStack的引用,可以访问ValueStack中的数据(对象)。

可以在jsp中用<s:debug>标签显示ValueStack的调试信息。

平时说的操作值栈,是指操作root部分,因为context中这些常用对象的引用由struts自动管理,无需我们操作。

ValueStack,顾名思义,是一个栈,ValueStack的栈指的是root部分,栈是通过ArrayList实现的。


获取ValueStack对象的2种方式

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

Object attribute = ServletActionContext.getRequest().getAttribute("struts.valueStack");
Object attribute1 = ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);

获取ValueStack对象有2种方式:通过ActionContext对象、通过request对象。

AtionContext中有ValueStack的引用,能获取到;

Struts2对request进行了包装、增强(拦截器|过滤器),request中也有ValueStack的引用,也能获取到。

但request是以属性获取,返回值是Object,需要强转,且字符串常量不好记,通过ActionContext获取更方便。

字符串常量  ServletActionContext.STRUTS_VALUESTACK_KEY  即  "struts.valueStack"。

一个Action实例中,只有一个对应的ActionContext对象,只有一个对应的ValueStack对象。


ValueStack的数据存取

1、将数据设置为Action的成员变量,并提供getter方法

public class LoginAction extends ActionSupport{
private String name;
private User user; public String getName() {
return name;
} public User getUser() {
return user;
} @Override
public String execute() throws Exception {
name = "chy";
user = new User("张三", 19);
return "index";
} }

root部分是栈,Struts默认会把当前Action的实例压入栈中,所以能从ValueStack中取到此Action实例的属性。

取Action的属性,底层是调用此Action的getter方法,所以需要提供对应的getter方法。

在jsp中取值:

  <s:property value="name" />
<s:property value="user.name" />

<s:property>是struts2的标签,value中写ognl表达式。

user.name底层是调用user对象的getter方法,所以User类需要提供对应的getter方法。

Struts2对request对象进行了增强,request中也有ValueStack的引用,所以request中也能获取到ValueStack中的数据。

  ${requestScope.name}
${requestScope.user.name} <%=request.getAttribute("name") %>
<%=(User)request.getAttribute("user") %> <%
String name = (String) request.getAttribute("name");
out.print(name); User user = (User)request.getAttribute("user");
out.print(user.getName());
%>

EL表达式可以不指定requestScope:

  ${name}
${user.name}

默认先在requestScope中找,找不到就到ValueStack中找。

此种方式是将数据存到ValueStack的root部分。

如果存储的数据个数较多,Action中会有大量的成员变量、getter方法,很冗杂,一般不用这种。

2、直接操作ValueStack

public class LoginAction extends ActionSupport{

    @Override
public String execute() throws Exception {
ValueStack valueStack = ActionContext.getContext().getValueStack();
String name = "chy";
User user = new User("张三", 18);
valueStack.set("user",user);
valueStack.set("name",name);
return "index";
} }

JSP中取值:

<s:property value="name" />
<s:property value="user.name" />

此种方式是将数据都放到一个Map中,将此Map压入栈顶,在JSP直接通过Map的key来获取对应的value。

或者使用push():

public class LoginAction extends ActionSupport{

    @Override
public String execute() throws Exception {
ValueStack valueStack = ActionContext.getContext().getValueStack();
User user = new User("张三", 18);
valueStack.push(user);
return "index";
} }
<s:property value="name" />

直接将push的对象压入栈顶,JSP中通过属性名来获取值。

底层是调用对象的getter方法来获取属性值,所以对象需要提供getter方法。

一次push就向栈顶压入一个对象,取对象属性的时候不能带对象名,是从栈顶开始向栈底寻找这个属性,找到就返回。

        valueStack.push(user1);
valueStack.push(user2);

比如push2次, <s:property value="name" /> ,取到的是靠近栈顶的user2的name属性。

int、String、Array、List、Map等对象push到栈中,能获取到length、size这些属性值,但获取不到这些对象本身,也获取不到数组、集合中的元素,因为这些数组、集合中的元素不是属性,没有属性名。

set()是将数据都放到一个Map中,将此Map压入栈顶,数据都在栈顶。

set()根据Map中的key来取元素,能获取到对象本身,如果对象是数组、集合,也能获取到数组、集合中的元素。

public class LoginAction extends ActionSupport{

    @Override
public String execute() throws Exception {
ValueStack valueStack = ActionContext.getContext().getValueStack();
ArrayList<String> list = new ArrayList<>();
list.add("刘");
list.add("关");
list.add("张");
valueStack.set("list",list);
return "index";
} }
<s:property value="list" />
<s:property value="list[0]" />

可通过key获取对象本身,也可以通过下标获取Array、List的某个元素,可通过key获取Map对象中某个键值对的value。

set()比push()更优,推荐使用。

既然是栈,操作的自然是root部分,存取的是root部分的数据。

3、存取context中的数据

public class LoginAction extends ActionSupport{

    @Override
public String execute() throws Exception {
HttpServletRequest request = ServletActionContext.getRequest();
request.setAttribute("user",new User("chy",18));
request.setAttribute("group","vip");
return "index";
} }
  <s:property value="#request.group" />
<s:property value="#request.user" />
<s:property value="#request.user.name" />

需要加#,#表示是从context(几个域对象)中取数据。

获取原生的Servlet对象,setAttribute()存入数据,在JSP从对应的对象中取出来即可。

请求参数封装在request对象的parameters对象中,不是直接封装在request对象中,request中直接封装的是setAttribute()存入的数据。

把request作为一个大Map,里面还有一个小Map,小Map用于封装请求参数。 Map<String, String[]> parameterMap = request对象.getParameterMap(); 。

比如表单字段: 用户名:<input type="text" name="user" />

Object obj=request对象.getAttribute("user");  获取的是setAttribute()存入request中的数据。

String user = request对象.getParameter("user"); 获取的才是请求参数(从小Map中查找数据)。

在JSP中获取请求参数:

 <s:property value="#parameters.user" />
<s:property value="#request.parameters.user" />
<%=request.getParameter("user")%>

<s:property value="#request.user" /> 获取的不是请求参数,而是setAttribute()存入request中的数据。


OGNL表达式

OGNL是一种数据访问语言,可直接使用(写在Java代码中,即可获取值,也能设置值),也可配合Struts2标签使用,写在Struts2标签中叫做OGNL表达式,用于获取值。

访问ValueSatck中的数据:

  <s:property value="name" />
<s:property value="user.name" />
<s:property value="#parameters.user" />

不带#的是从root中取值,带#的是从context中取值。

特殊字符#:从context中取值,构建Map。

遍历List:

<s:iterator var="item" value="{'刘备','关羽','张飞'}">
<s:property value="item" />
</s:iterator>

var指定临时变量,表示一项,value指定Array、List。<s:iterator>的元素体即循环体。

不能 <s:property value="hello+item" /> 或者 <s:property value="hello"+"item" /> 这样来指定格式,这样解析不了临时变量。

可以配合其他标签来指定格式,比如:

hello <s:property value="item" />!
<div class="">
<p>
     hello
  <s:property value="item" />
   </p>
<img src="" />
</div>

构建Map:

<s:iterator var="entry" value="#{'name':'刘备','age':40,'group':'蜀'}">
<s:property value="entry" />
   <s:property value="entry.key" />
   <s:property value="entry.value" />
</s:iterator> <s:iterator value="#{'name':'刘备','age':40,'group':'蜀'}">
<s:property value="key" />:<s:property value="value" />
</s:iterator>

#表示这玩意儿是Map。Map默认var为key、value,所以可缺省 var="key,value" 。

如果指定了var="entry",可通过entry.key,entry.value来引用属性。

IDEA下可能会报红,这是IDEA的问题,代码本身没有错误。

单选按钮:

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

使用List时,<input type="radio" />的value、选项文字都是list中的元素。

使用Map时,Map的key作为<input type="radio" />的value,Map的value作为选项文字。

构建Map时,key必须引,value是字符、字符串才引。

特殊符号%:强制解析、不解析OGNL表达式

有的Struts标签默认会解析OGNL表达式,有的Struts标签默认不会解析OGNL表达式。

比如<s:textfield />默认不会解析OGNL表达式,会将#session.user作为字符串原样显示。

<%
session.setAttribute("user","chy");
%> <s:textfield name="user" value="#session.user" label="用户名" />

将OGNL表达式放在%{   }中,会作为OGNL表达式处理,强制解析。

<s:textfield name="user" value="%{#session.user}" label="用户名" />

有的Struts标签默认会解析OGNL表达式,如果不想解析OGNL表达式:

%{'#session.user'}

放在%{'     '}中即可,会作为字符串处理。

特殊符号$:在配置文件中使用OGNL表达式取值

将OGNL表达式放在${   }中即可。

.properties配置文件:

user=${#session.user.name}

xml配置文件:

<param>${#session.user.name}</param>

使用OGNL表达式访问成员属性、调用成员方法

<%
User user=new User("曹操",40);
session.setAttribute("user",user);
%> <s:property value="#session.user.name" />
<s:property value="#session.user.getName()" />
<s:property value="#session.user.setName('chy')" />
<s:property value="'hello'.length()" />

访问成员变量,底层其实是调用对应的getter方法。

如果方法有返回值,会用返回值替换原来的OGNL表达式。

使用OGNL表达式访问静态成员(static)

<s:property value="@java.lang.Math@PI" />
<s:property value="@java.lang.Math@abs(-10)" />

以  @全限定类名@静态变量|静态方法 的方式调用,必须是全限定类名,可以是java自带的类,也可以是自定义的类。

访问静态变量不用进行常量配置,但调用静态方法必须在struts.xml中进行常量配置:

<struts>
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant> <package name="action" namespace="/" extends="struts-default"> </package> </struts>

允许静态方法调用,输入ognl就出来了,将value设置true(默认为false)。

和EL表达式一样,OGNL表达式没有空指针异常、没有数组越界,如果没有指定的变量、方法,不会出现异常。但OGNL比EL更强大。

OGNL可以单独使用,但OGNL表达式常常需要配合Struts标签使用,在html标签中单独使用OGNL表达式无效,比如<p>#request.name</p>会原样显示OGNL表达式,不会解析OGNL表达式,加上%也不会强制解析:<p>%{#request.name}</p>。

Struts2 OGNL表达式、ValueStack的更多相关文章

  1. struts2 OGNL表达式

    一.OGNL OGNL是Object-Graph Navigation Language的缩写,全称为对象图导航语言,是一种功能强大的表达式语言,它通过简单一致的语法,可以任意存取对象的属性或者调用对 ...

  2. struts2 OGNL 表达式

    一.Struts 2支持以下几种表达式语言: OGNL(Object-Graph Navigation Language),可以方便地操作对象属性的开源表达式语言:JSTL(JSP Standard ...

  3. struts2 ognl表达式访问值栈

    1:简单的说,值栈是对应每一个请求对象的轻量级的数据存储中心,在这里统一管理着数据,供Action.Result.Interceptor等Struts2的其他部分使用,这样数据被集中管理起来而不凌乱. ...

  4. JSTL标签,EL表达式,OGNL表达式,struts2标签 汇总

    一下纯属个人总结摘抄,总结一起方便查看,解决疑问,有遗漏或错误,还请指出.       1,JSTL标签总结: a).JSTL标签有什么用?          JSTL是由JCP(Java Commu ...

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

    在Struts2中,一个请求在终于到达Action的方法之前,Action对象本身会被压入ValueStack(实际上就是放到ValueStack的CompoundRoot中),所以Action对象是 ...

  6. struts2(六)之ognl表达式与ActionContext、ValueStack

    前言 前面已经把struts2讲内容说了一半了,我写的很详细,希望对博友们有帮助. 一.OGNL表达式语言概述 1.1.OGNL表达式简介 百度上是这样说: OGNL是Object-Graph Nav ...

  7. 【Struts2五】ValueStack以及ognl表达式二(经常使用标签)

    Ognl经常使用标签:   1.s:debug       假设把该标签放入到s:iterator中能够看到当前正在迭代的元素的状态    2.s:property       1.输出       ...

  8. Struts2的OGNL表达式语言

    一.OGNL的概念 OGNL是Object-Graph Navigation Language的缩写,全称为对象图导航语言,是一种功能强大的表达式语言,它通过简单一致的语法,可以任意存取对象的属性或者 ...

  9. Struts2的标签库(二)——OGNL表达式

    Struts2的标签库(二) --OGNL表达式 1.Struts2中的OGNL表达式增加了ValueStack的支持. 注:ValueStack--实际上是一个容器对象,该对象在启动Struts2框 ...

随机推荐

  1. React Hooks究竟是什么呢?

    摘要: React Hooks原理解析. 原文:快速了解 React Hooks 原理 译者:前端小智 我们大部分 React 类组件可以保存状态,而函数组件不能? 并且类组件具有生命周期,而函数组件 ...

  2. pycharm 字符编码错误处理

    如果在MySQL创建表空间时指定了他的字符集为utf8 但是插入数据后 在pycharm查看却出现字符乱码 怎么解决呢 ? 首先进入cmd 页面 输入 chcp 65001  (意思是指定cmd所有字 ...

  3. ubuntu pdfium

    dept_tool export PATH=`pwd`/depot_tools:"$PATH" gn工具在内

  4. Centos 7 编译安装mariadb 5.5

    一.环境 OS :Linux 3.10.0-693.el7.x86_64 mariadb下载地址: ]# wget https://downloads.mariadb.org/interstitial ...

  5. lua 12 table 的使用

    转自:http://www.runoob.com/lua/lua-tables.html table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组.字典等. Lua table ...

  6. Bootstrap分页查询

    前台方法: function show() { $('#reportTable').bootstrapTable({ method: 'get', url: "@Url.Action(&qu ...

  7. SQL Server关系的创建

    如果两个表的相关列都是主键或具有唯一约束,创建的就是一对一关系. 如果只有一列具有主键或唯一约束,则创建的时一对多关系 关联字段的字符类型必须相同. 1. 一对一关系 USE [Howie] crea ...

  8. xml、bean、json互转工具,可直接CV就用

    1.jar包 <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactI ...

  9. poi实现excel数据的导入和导出

    内容来源于网络,侵删. 1.需要的jar包 <dependency> <groupId>org.apache.poi</groupId> <artifactI ...

  10. 如何在Console下面生成一个WIN32窗口

    一个小挑战? VS2017里面,新建一个控制台工程,输入名字(你不需要也成,有默认的),得到一个控制台工程. 好了,生成的代码,如下: // Win32InConsole.cpp : This fil ...