Struts2 (三) — OGNL与值栈
一、OGNL表达式
1.概述
1.1什么是OGNL
 OGNL是Object-Graph Navigation Language的缩写,俗称对象图导航语言. 它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。
 Eg: hibernate 查询方式 : 对象导航查询。
 其实就是查询出来了一个对象之后,通过对象里面的getXXX() 来获取关联的对象。
 它是一个开源项目,并不是struts发明出来的,只是struts里面默认的表达式是使用OGNL表达式,也就是说OGNL是struts2的默认表达式语言。
1.2 OGNL作用
获取对象的成员(支持对象方法的调用,支持对象对象属性访问,支持静态方法的调用,支持集合对象操作, )
在配置文件里面使用
进行运算
2.OGNL要素
 OGNL 有三个核心元素 ,我们只有理解这三个核心元素,才能去更好的学习OGNL表达式
2.1表达式 (Expression)
 表达式用于表示我们到底想执行什么操作 。 比如:想获取成员 ,直接用对象.成员名字就可以得到该成员的值
2.2 根元素 (Root)
 OGNL 表述的是对象导航语言、那么必须的指明根元素是什么。 好比我们要在树上找果子、必须指定到底是那一棵树是一样的道理。
2.3上下文 (Context)
 上下文其实就是我们的那个root寄存的位置,它在内存当中其实就是一个Map 。OGNL 有一个类专门用来表示上下文环境 叫做OGNLContext , 它其实就是一个Map.
EG:Map里面存放了很多的User对象, 那么我们必须指明是在哪一个根上面找这些user对象。
我们通过一个例子来描述这三者之间的关系
EG: 一个果农在果园摘苹果
上下文 (Context): 果园, 果园里面有苹果树, 梨树, 桃树....
根元素 (Root): 苹果树
表达式 (Expression): 要摘什么样的苹果(大的, 小的, 熟的, 红的....)
3.OGNL入门
大家在使用OGNL的时候,要在脑海中有一个想像,就是内存中已经有了一个对象,我们需要去操作这个对象,获取里面的成员,或者去调用它的某个方法。那么表达式应该怎么写。这和我们的EL 表达式有异曲同工之妙,都是用于获取对象、然后再关联到具体的某一个成员.当然我们现在给大家在代码里面演示OGNL的一些用法,一般我们使用OGNL 最多的地方还是在jsp页面上。
3.1表达式与根对象【了解】
访问根对象属性
/**
* OGNL表达式和根对象: 访问属性
* @throws OgnlException
*/
@Test
public void fun01() throws OgnlException{
//根对象
User root = new User("张三", 18);
//表达式(想得到什么)
String expression = "username";
//通过Ognl方式获得根对象中的表达式结果
Object value = Ognl.getValue(expression , root);
System.out.println("value="+value);
}
访问根对象方法
/**
* OGNL表达式和根对象: 访问方法
* @throws OgnlException
*/
@Test
public void fun02() throws OgnlException{
//根对象
User root = new User("张三", 18);
//表达式(想得到什么)
String expression = "getUsername()";
//通过Ognl方式获得根对象中的表达式结果
Object value = Ognl.getValue(expression , root);
System.out.println("value="+value);
}访问静态方法
@Test
//访问静态方法: @类的全限定名@方法名, 并且根对象设置为null
public void fun03() throws Exception {
//double random = Math.random();
//System.out.println(random);
//String expression = "@java.lang.Math@random()";
//Object value = Ognl.getValue(expression , null);
//System.out.println(value); //Runtime runtime = Runtime.getRuntime();
//runtime.exec("calc.exe");
//runtime.exec("shutdown.exe -s -t 3");
String expression = "@java.lang.Runtime@getRuntime().exec('calc.exe')";
Object value = Ognl.getValue(expression , null);
}
3.2表达式和上下文【了解】
结合上下文访问根对象属性
/**
* 表达式和上下文 在水果园摘水果
* 1.表达式:表达式用于表示我们到底想执行什么操作,想得到什么 eg: 摘桃子
* 2.根对象:具体操作的对象,得到这个对象某某 eg:桃树
* 3.上下文: 其实就是根对象寄存的位置.其实就是一个Map,也就是说Map里面可以存到很多的对象(根对象和非根对象)
* eg: 果园, 这里有桃树(根对象), 还有苹果树,梨树...
* @throws OgnlException
*/
@Test
public void fun03() throws OgnlException{
User user1 = new User("张三", 18);
User user2 = new User("李四", 19);
//上下文
Map<String, User> context = new HashMap<String, User>();
context.put("user1", user1);
context.put("user2", user2); //根对象(指定user1为根对象)
Object root = user1; //表达式(想得到什么)
String expression = "username";
//通过Ognl方式获得根对象中的表达式结果(获得根对象的username的值,也就是user1的)
Object value = Ognl.getValue(expression, context, root);
System.out.println("value="+value); //获得非根对象的值(user2的)
expression = "#user2.username";
value = Ognl.getValue(expression, context, root);
System.out.println("value="+value);
} 翻译上面的getValue方法的含义是: 在上下文 context里面找到一个 根user1 然后取它的name值
 如果想要获取user2的数据 因为user2不是根对象,所以要想取它的值,需要写成 #key.name 。 String expression = "#user2.name"
3.3在页面使用Ognl【重点】
 要想在页面上(jsp..)使用Ognl, 需要借助struts2的标签才可以使用.
 使用标签: <s:property value='OGNL表达式'/>
 步骤: 1.导入struts2标签库到页面<%@ taglib uri="/struts-tags" prefix="s" %>
 2.使用标签<s:property value='OGNL表达式'/>, 把value属性取值所对应的内容输出到浏览器上
调用非静态方法
<s:property value="'aaa'.length()"/>desw
调用静态方法
默认情况下struts2不允许在页面调用静态方法, 调用之前需要在struts.xml配置常量
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
在jsp页面上使用:
<s:property value="@java.lang.Math@random()"/>
二、ValueStack值栈
1.概述
1.1什么是值栈
 value stack : 翻译过来是值栈的意思。
 回想以前我们在学习servlet的时候,我们要想 servlet操作完成后,生成了一个集合或者对象数据, 在页面上显示,我们通常的做法是。把这个对象或者集合存储在作用域里面,然后到页面上再取出来。
strtus 框架是对我们的servlet进行了包装 ,它内部自己也有一套存值和取值的机制 ,这一套机制就是现在说的值栈。


2.值栈创建的时机
2.1 Servlet和Action的区别
Servlet: Servlet只会创建一次实例,以后再过来请求,不会创建实例
Action: Action是多例,来一次请求就创建一个Action实例。 创建一次Action的实例,就创建一次ActionContext实例,并且就创建出来一个值栈的实例。
2.2什么时候创建值栈
请求到来的时候才会创建值栈, 当来了请求,会执行前端控制器的doFilter方法,在doFilter方法里面,有如下代码 89行
prepare.createActionContext(request, response);
在createActionContext()这个方法内部
//从ThreadLocal里面获取ActionContext实例,一开始是没有的,所以该对象是 null
ActionContext oldContext = ActionContext.getContext();
if (oldContext != null) {
// detected existing context, so we are probably in a forward
ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
} else {
//创建值栈对象 是OgnlValueStack 对象
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
//给值栈里面的上下文区域存东西 。 存request \ response \session \application...
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
//创建ActionContext的对象。 然后跟值栈关联上
ctx = new ActionContext(stack.getContext());
}
3.值栈的内部结构
 我们查看值栈的内部结构,其实是想研究值栈存值的时候,是存到了什么地方去的。
  
值栈其实就是一个类 OgnlValueStack ,我们如果往值栈存值, 其实是存到了这个类中的两个集合去。
CompoundRoot root; //这其实是一个集合 list
transient Map<String, Object> context; //这是OGNL上下文 OGNLContext//底下还有一行代码
context.setRoot(root);
//也就是设置了OGNL上下文的根其实就是一个list集合 。 说的具体一点。 就是那个CompoundRoot对象。
我们使用OGNL表达式取值的时候,都是去上下文里面取值。

 root 区,根对象。具体类型为CompoundRoot。CompoundRoot实现了List接口,其实就是一个list集合。其实我们存值的话都是存在root区域.
 context 区:上下文对象。具体类型为OgnlContext。OgnlContext内部操控的是一个Map集合,其实context区可以理解为一个Map集合。
4.获得ValueStack
4.1 通过ActionContext对象的实例获得
 由于前面创建好值栈后,让它和ActionContext关联上了,所以我们只要通过ActionContext去获取它即可。
获得值栈代码
ValueStack valueStack = ActionContext.getContext().getValueStack();
4.2通过request域获得ValueStack
 在执行完我们的Action方法前,struts会把值栈往request对象里面存起来,所以我们使用request对象即可获取到.
  在前端过滤器的doFilter()方法里面有 execute.executeAction(request, response, mapping);这行代码,执行Action.
  具体源码参见: Dispatcher类中的serviceAction方法 , 位于568行行:request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY,   proxy.getInvocation().getStack());所以后续我们也只要使用request对象然后结合对应的STRUTS_VALUESTACK_KEY 这个key即可获取到值栈
获得值栈代码
ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
5.值栈存取值
5.1 push方式
 直接放置在栈顶,没有什么key与之对应。所以取值的话,直接写属性名即可。 但是如果push 了多个,并且还想获取的不是栈顶的值,是栈顶下来的某一个位置的值,那么可以采用[0] \ [1] 这种做法
  
存值Java代码
User user = new User("admin" ,"10086");
ValueStack stack = ActionContext.getContext().getValueStack();
//执行的是压栈的动作。 放置的东西永远会在栈顶。
stack.push(user);
//第二次的时候,user02已经位于栈顶 ,之前的那个user就要往下压一格
User user02 = new User("zhangsan" ,"1000000");
stack.push(user02);取值(演示[0]和[1]的区别)
取push放的值<br>
<s:property value="username"/> , <s:property value="password"/><br>
<br>取push的值 就取第二个,不取栈顶<br>
<s:property value="[1].top.username"/> , <s:property value="[1].password"/><br>
5.2set方式
 set 和 push 的区别在于, push 是直接把数据压倒栈顶 , 但是set方式是用一个map集合来包装数据,然后才把map压倒栈顶。 所以取值手法也有点不一样。 set(key, value); key就是包装map的key。

存值Java代码
User user = new User("admin" ,"10086");
ValueStack stack = ActionContext.getContext().getValueStack();
stack.set("user01", user);取值
取set放的值<br>
<s:property value="user01.username"/> , <s:property value="user01.password"/><br>
5.3属性驱动方式
 和之前的获得请求参数属性驱动类似. 存到了当前的Action类的区域(还是在根里面)
存值Java代码
public class ActionDemo01 extends ActionSupport {
private String str;
public String fun01(){
str = "hello";
return "success";
}
//提供get方法
public String getStr() {
return str;
}
}取值
取属性封装的值<br>
<s:property value="str"/>
5.4模型驱动方式
存值
public class ActionDemo01 extends ActionSupport implements ModelDriven<User> {
private User user;
public String fun01(){
return "success";
}
@Override
public User getModel() {
if(user == null){
user = new User("李四", "66666666");
}
return user;
}
}取值
<s:property value="model.username"/> , <s:property value="model.password"/><br>
或者:
<s:property value="username"/> , <s:property value="password"/><br>由于使用模型驱动封装,存值的时候,也还是存到action的范围里面去.
5.5使用EL表达式取值
 EL表达式也可以取到值栈的值,本来EL表达式是用于取作用域的值,但是值栈和作用域是两个东西。 为什么EL 表达式也能去值栈的值呢?原因是 : struts对EL 表达式取值的代码进行了扩展,如果从作用域取不到值就会去值栈找。
 reuqest.setAttribute("user" , user); ${user.name} ------> pageContext.findAttrbute(); --> 先从page范围找, 没有,就找request, 还没有就找session。request类型被包装成了 StrutsRequestWrapper 在里面的getAttribute 做了判定,如果从作用域中去不到值,就去值栈取值
6.OGNL中的符号
6.1#号的作用
获取Context中的数据,非根对象里面的数据
<s:property value="#request.name"/>
<s:property value="#session.name"/>构建一个Map集合
//var变量会存一份到root也会存一份到上下文 c:foreach
<s:iterator var="entry" value="#{ 'aa':'11','bb':'22','cc':'33' }">
<s:property value="key"/>--<s:property value="value"/><br/>
<s:property value="#entry.key"/>--<s:property value="#entry.value"/><br/>
</s:iterator>
6.2 $号的作用
在xml等配置文件里面使用ognl


6.3 OGNL取值流程图

Struts2 (三) — OGNL与值栈的更多相关文章
- 框架学习之Struts2(三)---OGNL和值栈
		
一.OGNL概述 1.1OGNL是对象图导航语言(Object-Graph Navigation Languaged)的缩写,他是一种功能强大的表达式语言,通过简单一致的表达式语法,可以存取Java对 ...
 - Struts2笔记3--获取ServletAPI和OGNL与值栈
		
获取ServletAPI: 第一种方式: //在request域中放入属性req,暂且认为getContext()获取的是request域空间,但实际不是 ActionContext.getConte ...
 - struts2中,OGNL访问值栈的时候查找的顺序是什么?请排序:模型对象、临时对象、固定名称的对象、Action对象
		
struts2中,OGNL访问值栈的时候查找的顺序是什么?请排序:模型对象.临时对象.固定名称的对象.Action对象 解答:struts2的值栈排列顺序为:1).临时对象:2).模型对象:3).Ac ...
 - (转)OGNL与值栈
		
http://blog.csdn.net/yerenyuan_pku/article/details/67709693 OGNL的概述 什么是OGNL 据度娘所说: OGNL是Object-Graph ...
 - Struts2基础学习(七)—值栈和OGNL
		
目录: 一.值栈 二.OGNL表达式 一.值栈(ValueStack) 1.定义 ValueStack贯穿整个Acton的生命周期,每个Action类的对象实例都拥有一个ValueStack ...
 - 走进Struts2(五)— 值栈和OGNL
		
值栈 1.值栈是什么? 简单说:就是相应每个请求对象的轻量级的内存数据中心. Struts2引入值栈最大的优点就是:在大多数情况下,用户根本无须关心值栈,无论它在哪里,不用管它里面有什么,仅仅须要去获 ...
 - 4、OGNL与值栈
		
一.OGNL 1.什么是OGNL 对象导航图语言(Object Graph Navigation Language),简称OGNL,是应用于Java中的一个开源的表达式语言(Expression La ...
 - EL与OGNL以及值栈的理解
		
这里先添加下在项目遇到的问题: 这两天在做论坛项目的时候,犯了一个错误:将数据放入值栈中,结果jsp页面获取不到. 困扰了许久: 总结如下: (1)每个action对应相应页面的值栈中值的获取,在属于 ...
 - OGNL与值栈
		
一.OGNL入门 1.什么是OGNL OGNL的全称是对象图导航语言(Object-Graph Navigation Language),它是一种功能强大的开源表达式语言.使用这种表达式语言,可以通过 ...
 
随机推荐
- jquery源码解析:jQuery数据缓存机制详解1
			
jQuery中有三种添加数据的方法,$().attr(),$().prop(),$().data().但是前面两种是用来在元素上添加属性值的,只适合少量的数据,比如:title,class,name等 ...
 - python------对于面向对象的理解
			
python中一切皆为对象 其实面向对象没什么高大上的东西,只不过把我们平时对于事物的描述和动作系统的总结成了一个定义事物的方法而已. 我们平时向别人介绍一个他(她)从未见过的东西,会从外形和外貌特征 ...
 - mysql 查询小技巧
			
数据字段中存放的是id集,形如 1,2,15,35 也可类推json格式 查询时不用拆分了, 用上 instr.concat搜索和连接字符串 查询fids中包含15的 select * from ...
 - Maven 依赖管理问题小计
			
刚学Maven,遇到点小问题,记录一下.https://maven.apache.org/ 问题的起因是项目中使用了 Hibernate Validator ,但是运行起来后总是不能按照设置的注解校验 ...
 - P4177 [CEOI2008]order
			
传送门 答案等于总工作价值减去最小失去的价值 考虑构建最小割模型 在 $S$割 的点表示选,在 $T$割 的点表示不选 对于机器(编号从 $n+1$ 到 $n+m$) $n+i$,连边 $(n+i,T ...
 - GeneXus学习笔记——入门篇
			
使用GeneXus做开发做了有一段时间了 却发现一个问题(O_O)?就是除了相关的Wiki外 网上其他地方的相关资料都很少 于是乎我就想在这记录一些东西 来帮助以后会用到的人(°ー°") 那 ...
 - 通过MSI解压缩Cab文件
			
迁移自我的Github 如果只是想做类似解压缩的操作,那么可以使用如下命令行 C:\Windows\System32\expand.exe <cab file path> -F:* < ...
 - Ribbon是什么?
			
学而时习之,不亦说乎! --<论语> Ribbon使用版本2.2.2 Ribbon是什么? 开始接触Ribbon的时候,网上以及很 ...
 - iframe 解析
			
简介:iframe在日常的开发中经常用到,本随笔在参考http://blog.csdn.net/cuew1987/article/details/11265153的情况下,将对iframe的常用用法进 ...
 - javascript中Function与Object
			
1. 先来一段代码: console.log(Function); // function Function() { [native code] } console.log(Object); // f ...
 
			
		