时间:2017-1-12 12:02
——OGNL
1、OGNL表达式是什么
OGNL的全称是Object-Graph Navigation Language的缩写,中文名是对象图导航语言,它是一种功能强大的表达式语言。
比EL表达式功能强大。
Struts2将OGNL表达式语言集成到Struts2框架中,作为它的默认表达式语言。
2、OGNL表达式的功能
1)支持对象方法调用,如:xxx.doSomeMethod()
2)支持类静态的方法调用和值访问。
3)访问OGNL上下文(OGNL Context)和ActionContext:(重点,操作ValueStack)
4)支持赋值操作和表达式串联。
5)操作集合对象。
3、OGNL中“根”与“非根”的区别
root:只能有一个根,用于存储Action相关数据
contextMap:用于存储Web相关数据
4、示例代码
在Java代码中使用ognl表达式。
根中数据不需要使用#获取。
非根中数据需要使用#获取。
OGNL叫做对象图导航语言。
Person.java
public class Person {
private String name;
private Dog dog;
public Dog getDog() {
return dog;
}
public void setDog(Dog dog) {
this.dog = dog;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
----------------------------------------------------------------------------------------------------------------------------
Dog.java
public class Dog {
private String name;
private Person p;
public Person getP() {
return p;
}
public void setP(Person p) {
this.p = p;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
----------------------------------------------------------------------------------------------------------------------------
OgnlDemo1.java
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
public class OgnlDemo1 {
public static void main(String[] args) throws OgnlException{
// ognl可以通过对象调用方法
System.out.println("aaa".length());
/*
* 使用ognl来实现上面操作
* 1、创建一个ognl上下文对象
* 2、调用getValue()方法
*/
OgnlContext context = new OgnlContext();
Object o1 = Ognl.getValue("'aaa'.length()", context.getRoot());
System.out.println(o1);
// 调用Math的静态方法和静态成员
System.out.println(Math.max(10, 20));
System.out.println(Math.PI);
/*
* 在ognl表达式中调用静态方法
*/
Object o2 = Ognl.getValue("@java.lang.Math@max(10, 20)", context.getRoot());
System.out.println(o2);
Object o3 = Ognl.getValue("@java.lang.Math@PI", context.getRoot());
System.out.println(o3);
}
}
----------------------------------------------------------------------------------------------------------------------------
OgnlDemo2.java
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
public class OgnlDemo2 {
public static void main(String[] args) throws OgnlException {
/*
* 创建一个ognl上下文 OgnlContext是一个Map集合
* public class ognl.OnglContext implements java.util.Map
*/
OgnlContext context = new OgnlContext();
Person p = new Person();
p.setName("张三");
Dog dog = new Dog();
dog.setName("王五");
p.setDog(dog);
// 设置根
context.setRoot(p);
Dog d = new Dog();
d.setName("李四");
Person p2 = new Person();
p2.setName("赵六");
d.setP(p2);
// 设置属性(非根)
context.put("dog", d);
// 使用ognl来获取根中数据
// 获取根中数据,不需要加#
Object o1 = Ognl.getValue("name", context, context.getRoot());
System.out.println(o1);
// 使用ognl来获取非根中的数据
// 获取非根中数据,需要使用#
Object o2 = Ognl.getValue("#dog.name", context, context.getRoot());
System.out.println(o2);
// 获取p中的dog对象
// p表示根,所以可以省略p
Object o3 = Ognl.getValue("dog.name", context, context.getRoot());
System.out.println(o3);
// 获取dog中的person
Object o4 = Ognl.getValue("#dog.p.name", context, context.getRoot());
System.out.println(o4);
}
}
——在Struts2中使用ognl表达式
需要结合Struts2的标签使用:
* value:
书写ognl表达式
* default:
默认值
* escapeHtml
是否解析HTML
* escapeJavaScript
是否解析JS
* escapeXml
是否解析XML
注意:在Struts的JSP页面中访问静态成员时,必须设置一个常量值:
struts.ognl.allowStaticMethodAccess=false
示例代码:
——ValueStack
1、ValueStack是什么
从技术角度来讲,ValueStack是一个接口(com.opensymphony.xwork2.util.ValueStack)。
从实用角度来讲,ValueStack是一个容器,用于将数据携带到action数据页面,然后在页面通过ognl表达式来获取。
ValueStack是Struts2提供的一个接口,实现类是OgnlValueStack。
OGNL表达式是从ValueStack中获取数据的。
每个Action实例都有一个ValueStack对象(一个请求对应一个ValueStack对象)
ValueStack中保存当前Action对象和其他常用Web对象,例如request, session, application, parameters(值栈中是有Action引用的)
Struts2框架把ValueStack对象保存在key为“struts.valueStack”的request域中(request中值栈对象是request的一个属性)
一个request一个Action,一个Action一个ValueStack,request - Action - ValueStack是一一对应的。
ValueStack生命周期就是一个request的生命周期。
流程(源码分析):
从第一个请求开始,被StrutsPrepareAndExecuteFilter拦截后执行doFilter()中的execute.executeAction(request, response, mapping);方法,然后一直调用,进入到Dispatcher中的serviceAction()方法,在该方法中通过request获取ValueStack对象:ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);如果是第一次获取,则ValueStack对象为null,然后创建一个新的ValueStack,因为每次请求都是一个新的请求,所以每次请求都会创建一个新的ValueStack。当请求结束后,ValueStack就被释放了,所以ValueStack的生命周期等同于request的生命周期。
然后通过Dispatcher类中的serviceAction()方法中的:request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());来获取一个新的ValueStack对象,并保存到request域中,保存的key为:ServletActionContext.STRUTS_VALUESTACK_KEY,在ServletActionContext中可以查看,该常量的值为:struts.valueStack。
2、ValueStack的内部结构
查看ValueStack接口,可以看到两个方法:
* public abstract Map
* public abstract CompoundRoot getRoot();
> public class CompoundRoot extends ArrayList
> CompoundRoot类继承了ArrayList,提供了peek() pop() push()方法,相当于栈。
ValueStack接口中声明了root属性(CompoundRoot)、context属性(OgnlContext):
* CompoundRoot就是ArrayList。
* OgnlContext就是Map
值栈由两部分组成:
ObjcetStack:Struts2把Action和相关对象压入ObjectStack中,用一个List保存
保存Action相关信息。
ContextMap:Struts2把各种各样的映射关系(一些Map类型的对象)压入ContextMap中。
比较常见的映射关系就是常见Web对象。
Struts2会把下面这些映射压入ContextMap中:
* parameters:该Map中包含当前请求的请求参数
* request:该Map中包含当前request对象的所有属性
* session:该Map中包含当前session对象的所有属性
* application:该Map中包含当前application对象的所有属性。
* attr:该Map按如下顺序来检索某个属性:request、session、application,相当于全域查找
* 对象引用
通过断点可以发现:
其中root在ContextMap中也有一个映射关系:
ValueStack中包含ContextMap和Root,而在ContextMap中又持有了Root的引用。
可以在断点调试中发现:
OGNL表达式访问root(List)中数据时,不需要使用#访问。
访问(Map)request、session、application、attr、parameters、对象引用时,必须写#。
操作ValueStack时,默认是指操作root元素。
其实ContextMap就是一个OgnlContext,可以查看ValutStack接口的实现类:OgnlValueStack
protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticMethodAccess) {
this.root = compoundRoot;
this.securityMemberAccess = new SecurityMemberAccess(allowStaticMethodAccess);
this.context = Ognl.createDefaultContext(this.root, accessor, new OgnlTypeConverterWrapper(xworkConverter), securityMemberAccess);
context.put(VALUE_STACK, this);
Ognl.setClassResolver(context, accessor);
((OgnlContext) context).setTraceEvaluations(false);
((OgnlContext) context).setKeepLastEvaluation(false);
}
结论:
ValueStack有两个部分,一个是List,一个是Map。
在Struts2中List就是Root,Map就是OgnlContext。
在Struts2中,默认情况下(不加#)从ValueStack中的Root获取数据。
3、ValueStack对象的创建,ValueStack和ActionContext是什么关系?
当请求发出时,会被doFilter()拦截然后调用Dispatcher类中的serviceAction()方法,方法中可以创建ValueStack对象:
// 刚开始会到request域中获取
ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
// 如果获取不到ValueStack对象
boolean nullStack = stack == null;
if (nullStack) {
// 会到ActionContext中获取
ActionContext ctx = ActionContext.getContext();
if (ctx != null) {
stack = ctx.getValueStack();
}
}
ValueStack和ActionContext的关系:
ActionContext中持有了ValueStack的引用。
4、如何获取ValueStack对象
有两种方式可以获取ValueStack对象:
1)通过request获取
2)通过ActionContext获取
public class OgnlDemo1Action extends ActionSupport {
@Override
public String execute() throws Exception {
/*
* 获取ValueStack
*/
// 1、通过request获取
ValueStack stack1 = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
System.out.println(stack1);
// 2、通过ActionContext获取(推荐)
ValueStack stack2 = ActionContext.getContext().getValueStack();
System.out.println(stack2);
return null;
}
}
5、向ValueStack中保存数据(主要针对root)
在ValueStack接口中有两个方法:
有两种方式:
* push(Object obj)
* set(String key, Object obj)
1)push(Object obj)
在ValueStack接口的实现类OgnlValueStack中,重写了此方法:
public void push(Object obj) {
root.push(obj);
}
底层调用的是:root.add(0, obj);
所以push方法会将对象压入栈顶。
如果当前位置存在元素,那么当前元素会先后移,然后再在栈顶压入元素。
2)set(String key, Object obj)
在ValueStack接口的实现类OgnlValueStack中,重写了此方法:
public void set(String key, Object o) {
Map setMap = retrieveSetMap();
setMap.put(key, o);
}
private Map retrieveSetMap() {
Map setMap;
Object topObj = peek();
if (shouldUseOldMap(topObj)) {
setMap = (Map) topObj;
} else {
setMap = new HashMap();
// 将数据封装到一个新的HashMap中
setMap.put(MAP_IDENTIFIER_KEY, "");
// 将Map集合压入栈顶
push(setMap);
}
return setMap;
}
底层是将数据封装到HashMap中,再将这个HashMap压入栈顶,保存到List集合中。
示例代码:
public String execute() throws Exception {
ValueStack stack = ActionContext.getContext().getValueStack();
stack.push("1");
stack.push("2");
stack.push("3");
stack.set("username", "张三");
List list = stack.getRoot();
System.out.println(list);
return null;
}
输出结果:
[3, 2, 1, com.wyc.action.OgnlDemo2Action@320859ec, com.opensymphony.xwork2.DefaultTextProvider@5c1958e7]
6、在JSP页面中使用
7、在JSP页面中获取ValueStack数据
获取Root中数据不需要#
获取ContextMap中数据需要#
获取Root中的数据:
1)如果栈顶是一个Map集合,那么获取时可以直接通过Map集合的key来获取value。
如果只写属性名,那么即使栈顶不是Map集合,也会从栈顶开始依次往下查找。
2)如果栈顶元素不是Map,不能通过key来获取,可以使用下标来获取元素:
只查找0位置上的数据。
JSP页面示例代码:
1、获取栈顶的Map
2、获取栈顶非Map集合数据
获取OgnlContext中的数据:
1)request数据
request.setAttribute()
2)session数据
session.setAttribute()
3)application数据
application.setAttribute()
4)attr
依次从request, session, application域中查找。
相当于pageContext的全域查找。
5)parameters数据
获取请求参数。
JSP页面代码:
3、获取OgnlContext中的数据
<%
request.setAttribute("rname", "rvalue");
session.setAttribute("sname", "svalue");
application.setAttribute("aname", "avalue");
%>
8、ValueStack有什么作用
使用ValueStack最大的作用就是将Action相关的数据以及Web相关的对象,传递到页面上。
简单来讲,在Struts2中通过ValueStack将Action中的数据携带到页面上进行展示。
1)Action向JSP携带的数据都是什么类型的数据?
* 普通文本(字符串)
> fieldError:校验数据错误信息提示(常用于表单校验),this.addFieldError("msg", "字段错误信息")
> actionError:关于逻辑操作时的错误信息(普通错误信息,例如登录失败),this.addActionError("Action全局错误信息")
> message:通用信息,this.addActionMessage("Action普通消息信息")
在JSP中使用Struts2标签显示错误信息:
*
*
*
* 复杂数据
可以使用ValueStack存储:
Action中存储数据:
public String execute() throws Exception {
ValueStack stack = ActionContext.getContext().getValueStack();
List
users.add(new User("zhangsan", "111", 20, "男"));
users.add(new User("lisi", "222", 30, "女"));
users.add(new User("wangwu", "333", 40, "男"));
// stack.push(users);
stack.set("users", users);
return SUCCESS;
}
JSP中获取数据:
1、使用push()存储时获取数据(保存到OgnlContext中,是一个Map集合)
username:
password:
sex:
age:
username:
password:
sex:
age:
2、使用set存储数据(保存到List集合中)
username:
password:
sex:
age:
9、关于默认压入到ValueStack中的数据分析
当前的Action会被默认压入ValueStack
1)属性驱动
每次请求访问Action对象,Action对象都会被压入ValueStack,在DefaultActioninvocation的init()方法中:
stack.push(action);
DefaultActioninvocation源码:
public void init(ActionProxy proxy) {
this.proxy = proxy;
Map
// Setting this so that other classes, like object factories, can use the ActionProxy and other
// contextual information to operate
ActionContext actionContext = ActionContext.getContext();
if (actionContext != null) {
actionContext.setActionInvocation(this);
}
createAction(contextMap);
if (pushAction) {
stack.push(action);
contextMap.put("action", action);
}
……
}
在拦截器被调用之前就压入ValueStack了。
作用:
当Action被压入ValueStack之后,Action如果向传递数据给JSP,只要将数据保存为成员变量,并且提供get()方法就可以了。
当Action中声明了一个getXxx()方法后,ValueStack会将get之后的JavaBean的名称放到ValueStack的key中,然后在页面中可以直接使用JavaBean对象的key值来获取value值。
示例代码:
public class OgnlDemo4Action extends ActionSupport {
private List
public List
return users;
}
public void setUsers(List
this.users = users;
}
@Override
public String execute() throws Exception {
ValueStack stack = ActionContext.getContext().getValueStack();
users = new ArrayList
users.add(new User("zhangsan", "111", 20, "男"));
users.add(new User("lisi", "222", 30, "女"));
users.add(new User("wangwu", "333", 40, "男"));
return SUCCESS;
}
}
JSP代码:
2)模型驱动
ModelDriven接口有一个单独的拦截器
在拦截器中,将Model对象压入了ValueStack:stack.push(model);
如果Action实现了ModelDriven接口,ValueStack默认栈顶对象就是Model对象。
因为Action对象在拦截器执行前就已经压入,而Model对象在ModelDrivenInterceptor拦截器执行时才会压入。
ModelDrivenInterceptor源码:
public String intercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
// 将实现了ModelDriven接口的Action中的getModel()方法的返回值也就是内部封装的JavaBean对象压入到了ValueStack
if (action instanceof ModelDriven) {
ModelDriven modelDriven = (ModelDriven) action;
ValueStack stack = invocation.getStack();
Object model = modelDriven.getModel();
if (model != null) {
stack.push(model);
}
if (refreshModelBeforeResult) {
invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
}
}
return invocation.invoke();
}
在同一个请求中,Action中声明的private User user = new User();和getModel()方法中返回的User是相同的,也就是说在JSP页面中通过ValueStack获取到的User对象是同一个对象。但是,如果在execute()方法中对user重新赋值,那么push到ValueStack中的Action所包含的User就是后赋值的user对象,因为这个User对象是通过getXxx()方法获得的。而Model依然是private User user = new User();对象,因为Model是在execute()方法执行之前被压入的,所以在获取值时需要注意,这是两个对象。(new了两次,是两个对象。)
ModelDriven中保存的是初始化时压入的对象。
Action中保存的是execute()方法中赋值的对象。
示例代码:
public class OgnlDemo4Action extends ActionSupport implements ModelDriven {
private User user = new User();
@Override
public String execute() throws Exception {
return SUCCESS;
}
public Object getModel() {
return this.user;
}
}
10、为什么EL表达式可以访问ValueStack中的数据
Action中:
stack.set("username", "张三");
JSP中:
ognl获取:
EL获取:${username }
Struts2框架中所使用的request对象,是增强后的request对象,重写了getAttribute()方法。
StrutsPreparedAndExecuteFilter的doFilter()方法中:
request = prepare.wrapRequest(request)
* 对Request对象进行了包装,包装类:StrutsRequestWrapper
* 重写了request的getAttribute()方法
Object attribute = super.getAttribute(key)
if(attribute == null) {
attribute = stack.findValue(key);
}
增强后的request,会首先在request范围查找,如果查找不到,会到ValueStack中查找。
——OGNL表达式的常见使用
1、#
1)#相当于ActionContext.getContext()上下文
相当于:ActionContext.getContext().getRequest().get("name");
2)不写#默认在ValueStack的Root中进行查找
3)进行投影映射以及过滤操作(结合复杂对象遍历)
映射:
1、使用iterator进行遍历
2、对集合进行投影,只得到指定的属性
过滤:
3、对集合进行过滤操作
名称: 价格: 数量:
4、对集合进行过滤操作
名称:
4)使用#构造Map集合
经常结合Struts2标签用来生成select、checkbox、radio
示例代码:
1、使用#构造一个Map集合
2、构造一个List集合
3、手动创建一个集合,在Struts2中结合表单标签使用
2、%
作用是用来设定当前字符串是否要解析为ognl表达式。
<%
request.setAttribute("username", "tom");
%>
3、$
作用是在配置文件中写ognl表达式来获取ValueStack中的数据。
1)struts.xml
2)在校验文件中使用
校验文件会引入校验器,校验器会被加载,所以数据也会保存到ValueStack中。
3)在国际化文件中使用
在properties文件中:
username=${#request.username}
在JSP页面:
<%
request.setAttribute("username", "李四");
%>
在properties文件中也可以使用%获取值:
username=%{request.username}
——总结
1、ognl介绍
2、ValueStack介绍
3、什么是ValueStack
4、ValueStack内部结构
5、ValueStack创建以及ActionContext关系
6、如何获取ValueStack
7、如何向ValueStack存储数据(Root)
8、JSP页面中如何获取ValueStack数据
9、关于ValueStack携带数据分析
* 携带数据类型
* 如何在页面上获取复杂数据
* ValueStack默认压入数据
10、如何使用OGNL表达式
* #
* %
* $
手机扫一扫
移动阅读更方便
你可能感兴趣的文章