OGNL
简介
OGNL(Object-Graph Navigation Language)对象图导航语言,用于存取对象的任意属性,调用对象方法,遍历整个对象的结构图。${user.addr.name} 这种写法就叫对象视图导航。
OGNL作用
对象方法调用
类静态方法调用和值访问
支持赋值操作和表达式串联
访问OGNL上下文(OGNL context)和ActionContext
操作集合对象
OGNL三要素
1.表达式
表示操作要做什么
2.根对象Root
规定了对谁操作。OGNL的所谓对象图,就是以任意一个对象为根,通过OGNL访问与这个对象关联的其他对象。
3.Context对象
root对象所在环境的就OGNL的上下文环境Context。规定了操作在哪儿进行。context是一个Map类型的对象,在表达式中访问context中的对象,需要用#号加上对象名称,"#对象名"
OGNL入门
1.OGNL调用对象的方法
//OGNL调用对象的方法
@Test
public void demo1() throws OgnlException{
//获得context
OgnlContext context=new OgnlContext();
//获得根对象
Object root=context.getRoot();
//执行表达式
Object obj = Ognl.getValue("'helloWord'.length()", context, root);
System.out.println(obj);
}
2.访问对象的静态方法
//访问对象的静态方法
@Test
public void demo2() throws OgnlException{
//获得context
OgnlContext context=new OgnlContext();
//获得根对象
Object root=context.getRoot();
//执行表达式:@类名@方法名
Object obj = Ognl.getValue("@java.lang.Math@random()", context, root);
System.out.println(obj);
}
3.访问Root中的数据:不用加#
//访问Root中的数据
@Test
public void demo3() throws OgnlException{
//获得context
OgnlContext context=new OgnlContext();
//向root存数据
context.setRoot(new User("aaa","123"));
//获得根对象
Object root=context.getRoot();
Object username=Ognl.getValue("username", context, root);
Object password=Ognl.getValue("password", context, root);
System.out.println(username+":"+password);
}
4.访问context中的数据:需要加#
//访问context中的数据 需要加#
@Test
public void demo4() throws OgnlException{
//获得context
OgnlContext context=new OgnlContext();
//获得根对象
Object root=context.getRoot();
//向context中存数据
context.put("name", "赵云");
Object name=Ognl.getValue("#name", context, root);
System.out.println(name);
}
值栈
值栈概述
ValueStack是struts2的一个接口,客户端每发起一个请求,框架就会创建一个action实例同时创建一个OGNLValueStack(接口的实现类),贯穿整个action生命周期。框架会用OGNL将请求的参数封装为对象存到值栈里面,再用OGNL表达式来取值栈的对象属性。所以说,值栈很像是一个临时的货架。
值栈内部结构
root:CompoundRoot
继承ArrayList实现压栈出栈功能、存储action实例以及请求的参数等
context:OGNLContext
是一个map结构,上下文中存储了一些引用:parameters、request、response、session、application等,attr(该map按照request、session、application顺序检索属性)。它的root就是那个CompoundRoot
ActionContext和ValueStack的关系
创建ActionContext时创建ValueStack对象,将ValueStack对象给ActionContext
ActionContext里有一个ValueStack的引用。ValueStack里也有一个ActionContext的引用
ActionContext获取ServletAPI的时候,依赖值栈了
//在StrutsPrepareAndExecuteFilter-doFilter中有一句
prepare.createActionContext(request, response);
//去PrepareOperations中去找到createActionContext,发现了以下代码
ValueStack stack=dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
ctx = new ActionContext(stack.getContext());
获得值栈
1.通过ActionContext(它里面有值栈,值栈里也有它,参考上边代码)
ValueStack valueStack=ActionContext.getContext().getValueStack();
2.通过request域获取值栈
ValueStack valueStack2=(ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
//key记不住就写:ServletActionContext.STRUTS_VALUESTACK_KEY
操作值栈
1.对action中的属性提供get方法
因为action本来就在值栈里,action的属性也在里面。
//操作ValueStack:方式一.利用action本身就在值栈中的特性来操作
public class ValueStackDemo3 extends ActionSupport{
private User user;
public User getUser() {
return user;
}
@Override
public String execute() throws Exception {
//向ValueStack存值
user=new User("张三","233");
return SUCCESS;
}
}
2.使用ValueStack本身的方法
调用之战的push和set方法对值栈进行操作
//操作ValueStack:方式二.调用值栈中的方法实现
public class ValueStackDemo4 extends ActionSupport{
@Override
public String execute() throws Exception {
//向值栈中保存数据
ValueStack valueStack=ActionContext.getContext().getValueStack();
//使用push(object);set(String key,Object obj);
User user=new User("李四","594");
//现在user在栈顶的位置
valueStack.push(user);
valueStack.set("myname", "刘振宇");//创建map集合,将map押入栈顶
return super.execute();
}
}
从值栈获取数据
0.准备工作:先存数据
public class ValueStackDemo5 extends ActionSupport{
@Override
public String execute() throws Exception {
//向值栈中保存一个对象
User user=new User("对象","888");
ActionContext.getContext().getValueStack().push(user);
//向值栈中保存一个集合
List<User> list=new ArrayList<User>();
list.add(new User("aaa","111"));
list.add(new User("bbb","222"));
list.add(new User("ccc","333"));
ActionContext.getContext().getValueStack().set("list", list);
//向context中存入数据
ServletActionContext.getRequest().setAttribute("name", "attribute_name");
ServletActionContext.getRequest().getSession().setAttribute("name", "session_name");
ServletActionContext.getServletContext().setAttribute("name", "application_name");
return super.execute();
}
}
1.1页面OGNL-获取对象数据
<!-- 获取一个对象的数据 -->
<s:property value="username" />
<s:property value="password" /><br/>
1.2页面OGNL-获取集合数据
<!-- 获取集合中的数据 -->
<s:property value="list[0].username"/>
<s:property value="list[0].password"/><br/>
<s:property value="list[1].username"/>
<s:property value="list[1].password"/><br/>
<s:property value="list[2].username"/>
<s:property value="list[2].password"/><br/>
2.获取context数据
<!-- 获取context中的数据 -->
<s:property value="#request.name"/>
<s:property value="#session.name"/>
<s:property value="#application.name"/><br/>
<s:property value="#attr.name"/>
<s:property value="#parameters.id"/>
EL为何能访问值栈
原码:StrutsRequestWrapper类-getAttribute方法
对request进行了包装,会自动去值栈中寻找
//StrutsRequestWrapper-getAttribute
public Object getAttribute(String key) {
if (key == null) {
throw new NullPointerException("You must specify a key value");
}
if (disableRequestAttributeValueStackLookup || key.startsWith("javax.servlet")) {
// don't bother with the standard javax.servlet attributes, we can short-circuit this
// see WW-953 and the forums post linked in that issue for more info
return super.getAttribute(key);
}
ActionContext ctx = ActionContext.getContext();
Object attribute = super.getAttribute(key);
if (ctx != null && attribute == null) {
boolean alreadyIn = isTrue((Boolean) ctx.get(REQUEST_WRAPPER_GET_ATTRIBUTE));
// note: we don't let # come through or else a request for
// #attr.foo or #request.foo could cause an endless loop
if (!alreadyIn && !key.contains("#")) {
try {
// If not found, then try the ValueStack
ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.TRUE);
ValueStack stack = ctx.getValueStack();
if (stack != null) {
attribute = stack.findValue(key);
}
} finally {
ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.FALSE);
}
}
}
return attribute;
}
EL的特殊字符使用
1.#号
1.1获取context数据
<h3>1.获取context的数据</h3>
<%
request.setAttribute("name", "李冰");
%>
<s:property value="#request.name"/>
1.2用于构建一个map集合
<h3>2.构建map集合</h3>
//对照组
<s:iterator var="i" value="{'aa','bb','cc'}">
<s:property value="i"/>--<s:property value="#i"/></br>
</s:iterator>
//使用#
<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>
2.%
2.1强制解析OGNL表达式
2.2强制不解析OGNL
<h1>%的用法</h1>
<%
request.setAttribute("name", "王五");
%>
<s:property value="#request.name"/><br/>//正常解析
<s:property value="%{'#request.name'}"/><br/><!-- 强制不解析 -->
<s:textfield name="name" value="%{#request.name}"></s:textfield><!-- 强制解析 -->
<s:textfield name="name" value="#request.name"></s:textfield>//没法解析(在textfield里)
3.$
在配置文件中使用。.XMl文件或者属性文件