深入分析JavaWeb Item48 -- Struts2中OGNL表达式与ValueStack
2016-01-11 15:50
639 查看
一、OGNL表达式
1、OGNL表达式介绍
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。OGNL相对其它表达式语言具有下面几大优势:
1、支持对象方法调用,如xxx.doSomeSpecial();
2、支持类静态的方法调用和值访问,表达式的格式:
@[类全名(包括包路径)]@[方法名 | 值名],例如:
@java.lang.String@format('foo %s', 'bar')
或
@tutorial.MyConstant@APP_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的接口.
简单使用示例:
[code]<%@ 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="@java.lang.Integer@MAX_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
为了更好的理解可以断点执行如下代码:
[code]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上下文对象中数据操作的方法.
[code] 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
[code]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方法
[code]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
[code]<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页面
[code]<%@ 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,如果不存在,压入一个新的Mapvoid 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中的那个Mapparameters:请求参数的数据。即request.getParameterMap得到
application:应用范围的数据。即ServletContext中的那个Map
session:会话范围的数据。即HttpSession中的那个Map
attr:也是一个Map。会从以下Map中依次搜索:request、session、application
五、OGNL的一些其他操作
1、投影
1、 集合的投影(只输出部分属性)[code] <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.“
$#”:过滤最后一个符合条件的元素,如:
users.{$#this.age > 19} 。
[code]<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]
[code]<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
[code]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页面
[code]<%@ 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页面:
[code] <s:radio list=“#{‘male’:‘男’,‘female’:‘女’}” name=“sex” label=“性别” />
运行结果是
[code]<input type="radio" name="sex" id="sexmale" value="male"/>男 <input type="radio" name="sex" id="sexfemale" value="female"/>女
Action中的代码:
[code]Map map=new HashMap(); map.put("male", "男"); map.put("female", "女"); ServletActionContext.getRequest().setAttribute("map", map);
jsp页面:
[code] <s:property value="#request.map.male"/><br> <s:property value="#request.map['female']"/><br>
运行结果是
男 女
[code] <%@ 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表达式
[code]<param name="contentDisposition">attachment;filename=${@java.net.URLEncoder@encode(fileName,"UTF-8")}</param>
在JSP中使用${}
有两个主要的用途
用于在国际化资源文件中,引用OGNL表达式
在Struts 2配置文件中,引用OGNL表达式
EL表达式还是原有功能,如果在域范围中找不到,{}内部的东西就变成了OGNL表达式。
5、EL获取数据
[code] <%-- <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>
相关文章推荐
- java-读取类中的属性名称和值
- 4.java成长之路——重读java编程思想04
- 关于java编写邮件注册
- JDK安装后 没有tools.jar 和dt.jar包的解决办法
- springMVC easyUI filebox 单个文件上传
- 【JVM系列】Java 虚拟机指令操作码的映射表
- JAVA 使用Dom4j 解析XML
- Spring调度
- Java中堆内存和栈内存_在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)。修改栈指针就可以把栈中的内容销毁.这样最快
- struts2标签radio和select中listKey与listValue
- axis2 gennrate java code
- springMvc 报错
- 【第七章】 对JDBC的支持 之 7.5 集成Spring JDBC及最佳实践 ——跟我学spring3
- Struts标签
- (转)Java中Properties类的操作
- java获取图片属性
- java获取任意指定日期后前后任一一天
- [javase学习笔记]-9.1 继承
- Spring加载完毕时,初始化参数
- Java正则表达式教程及示例