4. ValueStack 和 OGNL

1. 属性哪来的

  • 当我们通过Action处理完用户请求以后,可以直接在页面中获取到 action 的属性值。
  • 如果我们在页面中尝试遍历四个域中的属性,会发现域中并没有username之类的Action中的属性。
  • 但是我们自己又没有在域中进行设置,经过研究发现request域中出现了一个奇怪属性
    • 属性的名字:struts.valueStack
    • 属性的类型:OgnlValueStack
  • ValueStack翻译过叫做值栈,顾名思义就是存储值的栈
  • Struts在每次处理请求都会创建一个新的ValueStack用来存储这个请求过程中所要用到的属性及对象。
    像我们熟悉ActionContext就是值栈的一部分。

2. ValueStack(值栈)

  • 所谓值栈就是一个 OgnlValueStack 类型的对象,他以struts.valueStack为键保存在request域中。Struts2 会将 Action 的实例保存值栈中。
  • 值栈实际是由两部分组成 Object Stack (CompoundRoot )Map Context (Map<String, Object>
    • context是一个Map,里边保存着三个域、请求参数等对应的Map对象。
    • root 实际就是一个List,Action对象就保存在这个List中。
  • ObjectStack: Struts 把 Action 和相关对象压入 ObjectStack 中
  • ContextMap: Struts 把各种各样的映射关系(一些 Map 类型的对象) 压入 ContextMap 中. 实际上就是对 ActionContext 的一个引用
    • Struts 会把下面这些映射压入 ContextMap 中
      • parameters: 该 Map 中包含当前请求的请求参数
      • request: 该 Map 中包含当前 request 对象中的所有属性
      • session: 该 Map 中包含当前 session 对象中的所有属性
      • application:该 Map 中包含当前 application 对象中的所有属性
      • attr: 该 Map 按如下顺序来检索某个属性: request, session, application
  • ContextMap实际上之前我们就使用过,我们使用的 ActionContext 就包含一个ContextMap
  • 我们可以查看一下 CompoundRoot 对象,实际上是一个List
  • root (CompoundRoot)就是Struts2存储Action实例的地方。当我们在JSP中获取Action中的属性时,实际上就是去root中进行查找。
  • 但是我们查看方法会发现,CompoundRoot 每次都是从栈顶取得对象
/**
 * A Stack that is implemented using a List.
 * 
 * @author plightbo
 * @version $Revision$
 */
public class CompoundRoot extends CopyOnWriteArrayList<Object> {
    private static final long serialVersionUID = 8563229069192473995L;
    public CompoundRoot() {
    }
    public CompoundRoot(List<?> list) {
        super(list);
    }
    public CompoundRoot cutStack(int index) {
        return new CompoundRoot(subList(index, size()));
    }
    public Object peek() {
        return get(0);
    }
    public Object pop() {
        return remove(0);
    }
    public void push(Object o) {
        add(0, o);
    }
}
  • 值栈的结构

  • 在Servlet我们属性HttpServletRequest实际已经被Struts2用 org.apache.struts2.dispatcher.StrutsRequestWrapper 给包装了(装饰者模式)

  • 我们的request的getAttributes方法已经被Struts重写。

  • 新的getAttribute方法

    • 首先,从request域中查找属性,如果有直接返回。
    • 然后,如果域中没有找到指定属性,则去值栈中查找(这里的值栈主要指CompoundRoot),
    • 因为值栈中可能有多个对象,所以会先从索引值为0的对象开始找,直到找到了需要的属性,如果找到直接返回。
    • 最后,如果值栈中依然没有找到指定属性接下来去context中查找,如果找到直接返回,如果也没找到则直接返回null。

3. OGNL (Object-Graph Navigation Language)

  • Object-Graph Navigation Language
  • 这是一种从Java对象中获取或设置属性的表达式语言。
  • Struts2内部使用OGNL表达式从而大大增强了Struts2的数据访问功能。
  • Struts2 利用 s:property 标签和 OGNL 表达式来读取值栈中的属性值

  1. 值栈中的属性值:
    • 对于对象栈: 对象栈中某一个对象的属性值
    • Map 栈: request, session, application 的一个属性值 或 一个请求参数的值.
  2. 读取对象栈中对象的属性:
    • 若想访问 Object Stack 里的某个对象的属性. 可以使用以下几种形式之一:
      • object.propertyName ;
      • object['propertyName'] ;
      • object["propertyName"]
    • ObjectStack 里的对象可以通过一个从零开始的下标来引用. ObjectStack 里的栈顶对象可以用 [0] 来引用,
    • 它下面的那个对象可以用 [1] 引用.
      • [0].message
      • [n] 的含义是从第 n 个开始搜索, 而不是只搜索第 n 个对象
    • 若从栈顶对象开始搜索, 则可以省略下标部分: message
    • 结合 s:property 标签: <s:property value="[0].message" /> <s:property value="message" />
    • 默认情况下, Action 对象会被 Struts2 自动的放到值栈的栈顶.
  3. 我们可以试着改变值栈的栈顶对象
 //改变对象栈,栈顶的对象
ActionContext context = ActionContext.getContext();
ValueStack valueStack = context.getValueStack();
valueStack.push(User.builder().username("猪八戒").address("高老庄").build());
  1. 获取对象栈中的数据:
<!-- 可以直接获取栈顶对象的属性,如果栈顶对象没有就会继续往下找,再没有就从Map栈中找 -->
用户名 : <s:property value="user.username"></s:property> <br /> <br />
年龄 : <s:property value="age"></s:property> <br /> <br />
地址 : <s:property value="address"></s:property> <br /> <br />
<hr>
<!-- 对于被我们改变的栈顶对象,我们可以用下标的方式 -->
用户名 : <s:property value="[1].username"></s:property> <br /> <br />
地址 : <s:property value="[1].address"></s:property> <br /> <br />
<hr>
<!-- 另外两种方式 object['filedName'] 或者 object["filedName"] -->
用户名 : <s:property value="[1]['username']"></s:property> <br /> <br />
地址 : <s:property value="[1]['address']"></s:property> <br /> <br />
  1. 读取 Context Map 里的对象的属性
    • 如果希望从map栈中查找属性,只需要在表达式的开头加个#
    • 例如:我们要从session域中查找一个属性 #session.hello
<%
    session.setAttribute("hello", "你好");
    application.setAttribute("key", "appKey");
    session.setAttribute("key", "sessKey");
    request.setAttribute("key", "reqKey");
%>
<hr><br><br>
<!-- Map栈中Session数据 -->
获取到Session域中属性: <s:property value="#session.hello"></s:property><br><br>
获取请求参数: <s:property value="#parameters.username"></s:property><br><br>
<!-- attr 会从小到大的范围去查找 -->
通过attr获取属性值:<s:property value="#attr.key"/>
  • OGNL 调用字段和方法
    • 可以利用 OGNL 调用
      • 任何一个 Java 类里的静态字段或方法.
      • 被压入到 ValueStack 栈的对象上的公共字段和方法.
    • 首先对于静态方法的调用,我们需要在 struts.xml 文件中开启
<!-- 开启静态方法访问 -->
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
  • 调用静态字段或方法需要使用如下所示的语法:
    • @fullyQualifiedClassName@fieldName: @java.util.Calendar@DECEMBER
    • @fullyQualifiedClassName@methodName(argumentList): @app4.Util@now()
<!-- 调用静态方法和字段 -->
调用静态字段: <s:property value="@java.util.Calendar@WEEK_OF_YEAR"></s:property><br>
<!-- 对于调用静态方法,我们需要去 struts.xml 文件中配置-->
调用静态方法: <s:property value="@org.pan.struts2.entity.User@sayHello('张三丰')"></s:property>
  • 调用 ValueStack 栈的对象的 非静态方法和字段公共字段和方法.)
<!-- 调用栈顶对象的 公共方法和属性(属性就是我们之前访问的字段值) -->
调用公共方法: <s:property value="sayYou('巨无霸')"></s:property>
  • OGNL 获取 数组、List、Map 信息
  1. OGNL 访问数组类型的属性
    • 有些属性将返回一个对象数组而不是单个对象, 可以像读取任何其他对象属性那样读取它们. 这种数组型属性的各个元素以逗号分隔, 并且不带方括号
    • 可以使用下标访问数组中指定的元素: colors[0]
    • 可以通过调用其 length 字段查出给定数组中有多少个元素: colors.length
<!-- OGNL 访问数组类型的属性 -->
<%
    String[] names = new String[]{"aa","bb","cc","dd"};
    request.setAttribute("names", names);
%>
length:<s:property value="#attr.names.length"/>
<br/>
names[1]:<s:property value="#attr.names[1]"/>
  1. OGNL 访问 List 类型属性
    • 有些属性将返回的类型是 java.util.List, 可以像读取任何其他属性那样读取它们. 这种 List 的各个元素是字符串, 以逗号分隔, 并且带方括号
    • 可以使用下标访问 List 中指定的元素: colors[0]
    • 可以通过调用其 size 方法或专用关键字 size 的方法查出给定List 的长度: colors.size 或 colors.size()
    • 可以通过使用 isEmpty() 方法或专用关键字 isEmpty 来得知给定的 List 是不是空. colors.isEmpty 或 colors.isEmpty()
    • 还可以使用 OGNL 表达式来创建 List, 创建一个 List 与声明一个 Java 数组是相同的: {“Red”, “Black”, “Green”}
<%
    List<String> colors = new ArrayList<String>();
    colors.add("Red");
    colors.add("Black");
    colors.add("Green");
    request.setAttribute("colors", colors);
%>
isEmpty: <s:property value="#request.colors.isEmpty()"></s:property><br><br>
size: <s:property value="#request.colors.size"></s:property><br><br>
colors: <s:property value="#request.colors[1]"></s:property>
  1. OGNL 访问Map 类型属性
    • 读取一个 Map 类型的属性将以如下所示的格式返回它所有的键值对:
    • 若希望检索出某个 Map 的值, 需要使用如下格式: map['key']
    • 可以使用 size 或 size() 得出某个给定的 Map 的键值对的个数
    • 可以使用 isEmpty 或 isEmpty() 检查某给定 Map 是不是空.
    • 可以使用 #{key1:value1, key2:value2, key3:value3}的方式创建一个Map
<!-- OGNL 访问Map类型属性 -->
<%
    Map<String,String> letters = new HashMap<String,String>();
    letters.put("AA", "aa");
    letters.put("BB", "bb");
    letters.put("CC", "cc");
    request.setAttribute("letters", letters);
%>
isEmpty: <s:property value="#request.letters.isEmpty"></s:property><br><br>
size:<s:property value="#attr.letters.size()"/><br><br/>
AA:<s:property value="#attr.letters['AA']"/><br><br>
创建Map: <s:set var="testMap" value="#{'AA':'aa', 'BB':'bb', 'CC':'cc'}"></s:set><br><br>
读取创建的Map: <s:property value="#attr.testMap.AA"></s:property>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容

  • 概述 什么是Struts2的框架Struts2是Struts1的下一代产品,是在 struts1和WebWork的...
    inke阅读 2,249评论 0 50
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,603评论 18 399
  • 1、struts2工作流程 Struts 2框架本身大致可以分为3个部分: 核心控制器FilterDispatch...
    重山杨阅读 1,515评论 0 38
  • 标签 如果要配置的标签,那么必须要先配置标签,代表的包的概念。 包含的属性 name包的名称,要求是唯一的,管理a...
    偷偷得路过阅读 1,333评论 0 0
  • 二、 我被接近皇宫的第二天,王子便告诉我我们会结婚的喜讯。我几乎落泪,我的爱人啊!你是国家的王子,更是我的王子啊!...
    Sikooooo阅读 363评论 0 0