JavaWeb 之 OGNL 表达式、Struts2 值栈与OGNL 特殊符号

OGNL 表达式 & Struts2 值栈 & OGNL 特殊符号
OGNL表达式
OGNL表达式概述(了解)
OGNL 是
Object Graphic Navigation Language(对象图导航语言)的缩写所谓对象图,即以任意一个对象为根,通过 OGNL 可以访问与这个对象关联的其它对象
通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。
Struts2 框架使用
OGNL作为默认的表达式语言OGNL是一种比EL强大很多倍的语言xwork提供OGNL表达式ognl-3.0.5.jarOGNL 提供五大类功能
支持对象方法调用
支持类静态的方法调用和值访问
访问 OGNL 上下文(OGNLcontext)和 ActionContext
支持赋值操作和表达式串联
操作集合对象测试的代码
1  | // 访问对象的方法  | 
自己测试的代码:
1  | package com.renkaigis;  | 
在 Struts2 框架中使用 OGNL 表达式
1). Struts2 引入了 OGNL 表达式,主要是在 JSP 页面中获取值栈中的值
2). 具体在 Struts2 中怎么使用呢?如下步骤
需要先在 JSP 页面中引入
Struts2的标签库
1  | <%@ taglib prefix="s" uri="/struts-tags" %>  | 
使用
Struts2提供的标签中的标签
1  | <s:property value="OGNL表达式"/>  | 
3). 在 JSP 页面使用 OGNL 表达式
访问对象方法
1  | <s:property value="'hello'.length()"/>  | 
Struts2 框架的值栈
值栈的概述
值栈就相当于
Struts2框架的数据的中转站,向值栈存入一些数据。从值栈中获取到数据。ValueStack是 struts2 提供一个接口,实现类OgnlValueStack—- 值栈对象 (OGNL是从值栈中获取数据的 )Action是多例的,有一个请求,创建Action实例,创建一个ActionContext对象,代表的是Action的上下文对象,还会创建一个ValueStack对象。每个
Action实例都有一个ValueStack对象 (一个请求对应一个ValueStack对象 )在其中保存当前
Action对象和其他相关对象Struts 框架把
ValueStack对象保存在名为“struts.valueStack”的请求属性中,request中 (值栈对象是request一个属性)
1  | // 不常用  | 
值栈的内部结构
值栈由两部分组成
值栈由以下两部分组成:
root ———— Struts 把动作和相关对象压入
ObjectStack中–List
context ———— Struts 把各种各样的映射关系(一些Map类型的对象) 压入ContextMap中Struts 会默认把下面这些映射压入
ContextMap(context)中注意:
request代表的是Map集合的key值,value的值其实也是一个Map集合。parameters:该 Map 中包含当前请求的请求参数 ?name=xxx&password=123
request:该 Map 中包含当前request对象中的所有属性
session:该 Map 中包含当前session对象中的所有属性
application:该 Map 中包含当前application对象中的所有属性
attr:该 Map 按如下顺序来检索某个属性:request,session,applicationValueStack 中存在
root属性 (CompoundRoot) 、context属性 (OgnlContext )CompoundRoot就是ArrayListOgnlContext就是Mapcontext 对应 Map 引入 root 对象
context 中还存在 request、 session、application、 attr、 parameters 对象引用
操作值栈默认指操作 root 元素
OGNL 获取值
如果从 root 栈中获取值,OGNL 表达式默认情况下不能写 # 号:
1  | <s:property value="表达式"/>  | 
如果从 context 栈中获取值,OGNL 表达式默认需要加 # 号(访问 request、 session、application、 attr、 parameters 对象数据必须写):
1  | <s:property value="#表达式"/>  | 
值栈的创建和 ActionContext 对象的关系
值栈对象是
请求时创建的ActionContext是绑定到当前的线程上,那么在每个拦截器或者Action中获取到的ActionContext是同一个。ActionContext中存在一个Map集合,该Map集合和ValueStack的context是同一个地址。ActionContext中可以获取到ValueStack的引用,以后再开发,使用ActionContext来获取到值栈对象
获取值栈对象
- 获得值栈对象,有三种方法:
 
1  | ValueStack vs1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");  | 
1  | ValueStack vs2 = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);  | 
★重要:
1  | // 获取值栈对象,首先要获取 ActionContext 对象  | 
向值栈中保存数据
向值栈保存数据 (主要针对 root 栈)
push 方法
1  | // 向栈顶压入对象  | 
push方法的底层调用root对象的push方法(把元素添加到 0 位置)
set 方法
1  | // 向栈顶压入 map 集合,把 key 和 obj 存入到 map 集合中  | 
- 源码获取 
map集合(map有可能是已经存在的,有可能是新创建的),把map集合压入到栈顶,再把数据存入到map集合中。 
在 jsp 中,通过
<s:debug/>查看值栈的内容
从值栈中获取值
一些小细节
访问
root中的数据不需要#
访问context中的对象数据,要加#
如果向root中存入对象的话,优先使用push方法。
如果向root中存入集合的话,优先要使用set方法。
在 OgnlContext 中获取数据
1  | request:<s:property value="#request.username"/>  | 
代码演示
前提 struts.xml:
1  | <struts>  | 
注意:jsp 中首先要引入标签库 <%@taglib prefix="s" uri="/struts-tags" %>,在 jsp 中使用 <s:debug></s:debug> 可以查看值栈的内容。
以下代码演示 压栈和取值:
push 字符串
Action 代码:
1  | // 获取值栈  | 
jsp 代码:
1  | <%--获取栈顶的值--%>  | 
set 字符串
Action 代码:
1  | vs.set("msg", "小瓜");  | 
jsp 代码:
1  | <%--栈顶是 map 集合,通过 key 来获取值--%>  | 
获取对象(push)
首先需要创建一个 JavaBean:
1  | package com.renkaigis.demo2;  | 
Action 代码:
1  | // 创建 User 对象  | 
jsp 代码:
1  | <%--栈顶放 user 对象--%>  | 
获取对象(set)
Action 代码:
1  | vs.set("user", user);  | 
jsp 代码:
1  | <s:property value="[0].top.user.username"/>  | 
List 集合(push)
Action 代码:
1  | ArrayList<User> ulist = new ArrayList<>();  | 
jsp 代码:
1  | <s:property value="[0].top[0].username"/>  | 
List 集合(set)
Action 代码:
1  | vs.set("ulist", ulist);  | 
jsp 代码:
1  | <s:property value="ulist[0].username"/>  | 
从 context 栈中获取值
底层已经封装了 request、session 等对象,操作的就是 map 集合
request
Action 代码:
1  | HttpServletRequest request = ServletActionContext.getRequest();  | 
jsp 代码:
1  | <s:property value="#request.msg"/>  | 
session
Action 代码:
1  | request.getSession().setAttribute("msg", "小卡");  | 
jsp 代码:
1  | <s:property value="#session.msg"/>  | 
parameters
路径传值:
1  | http://localhost:9090/save.action?id=10  | 
jsp 代码:
1  | <s:property value="#parameters.id"/>  | 
attr
attr 从最小域开始找
jsp 代码:
1  | <s:property value="#attr.msg"/>  | 
EL 表达式也能获取到值栈中的数据
EL 获取值栈的值
获取上面的 ulist:
首先导包:
jstl.jar、standard.jar引标签库:
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
1  | <%--在 jsp 页面上使用 EL 和 JSTL 标签库来取值--%>  | 
为什么 EL 也能访问值栈中的数据?
因为 Struts2 底层使用了装饰者模式,对 getAttribute() 方法进行了增强。
StrutsPreparedAndExecuteFilter 的 doFilter 代码中 request = prepare.wrapRequest(request);
对
Request对象进行了包装StrutsRequestWrapper增强了request的getAttribute()方法
1  | Object attribute = super.getAttribute(s);  | 
访问
request范围的数据时,如果数据找不到,会去值栈中找request对象具备访问值栈数据的能力(查找root的数据)
OGNL 表达式的特殊符号
# 符号的用法 ★
获得 contextMap 中的数据
1  | <s:property value="#request.name"/>  | 
# 可以构建一个 map 集合
以构建表单为例:
1  | <h3>编写表单</h3>  | 
使用 # 构建 map 集合:
1  | <h3>编写表单</h3>  | 
% 符号的用法
强制字符串解析成 OGNL 表达式
例如:在
request域中存入值,然后在文本框(<s:textfield>)中取值,现在到value上。
1  | <s:textfield value="%{#request.msg}"/>  | 
{ } 中的值用 ‘’ 引起来,此时不再是 ognl 表达式,而是普通的字符串
1  | <s:property value="%{'#request.msg'}"/>  | 
$ 符号的用法
- 在配置文件中可以使用 OGNL 表达式,例如:文件下载的配置文件。
 
1  | <action name="download1" class="com.renkaigis.demo2.DownloadAction">  |