OGNL表达式是一个独立的语言,strut2将其引入共同构造struts2。
OGNL语言强大于EL表达式,其可以访问java类中的对象,也可以访问对象的静态方法。
public class OgnlDemo1 {
@Test
public void test1() throws OgnlException{
OgnlContext context = new OgnlContext();
Object value = Ognl.getValue("'hello OGNL'.length()", context, context.getRoot());
System.out.println(value);
}
@Test
public void test2() throws OgnlException{
OgnlContext context = new OgnlContext();
Object root = context.getRoot();
Object value = Ognl.getValue("@java.lang.Math@random()", context, root);
System.out.println(value);
}
}
root中存放的是java对象(一般写实体对象),访问Java类中的属性的demo:
@Test
public void test3() throws OgnlException{
OgnlContext context = new OgnlContext();
//通过context对象和User的有参构造给root设置值
context.setRoot(new User("aloha","keep moving!"));
//获取root
Object root = context.getRoot();
Object username = Ognl.getValue("username", context, root);
Object password = Ognl.getValue("password", context, root);
System.out.println(username);
System.out.println(password);
}
获取context中的数据,需要加#:
@Test
public void test4() throws OgnlException{
OgnlContext context = new OgnlContext();
//往Map集合context中添加键值对
context.put("name", "aloha");
Object name = Ognl.getValue("#name", context, context.getRoot());
System.out.println(name);
}
Struts2中使用OGNL
主要使用在:在页面中获取数据。
首先配置struts2环境,然后配置核心过滤器:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
在jsp页面中引入strut2的标签库:
<%@ taglib uri="/struts-tags" prefix="s" %>
如何使用ognl表达式访问?
<!-- property表示ognl表达式,value属性中写表达式 -->
<s:property value="'aloha'.length()"/>
注意:strut2中默认关闭访问静态成员。在struts2-core-2.3.24.jar/org.apache.struts2/default.properties的第208行中约定:struts.ognl.allowStaticMethodAccess=false,所以我们可以在struts.xml常量配置中开启:
<constant name="struts.ognl.allowStaticMethodAccess" value="true" />
这样就可以调用静态方法了。
OGNL在struts2中获取值栈(ValueStack)
什么是值栈(ValueStack):实际上是一个容器,由当前页面发送一个请求的时候,struts的默认拦截器会将请求中的数据进行封装,并入ValueStack栈顶。相当一个数据中转站(Struts2的框架中数据实际就保存到了ValueStack中。
ValueStack 是struts的一个接口,OgnlValueStack是ValueStack的实现类。客户端发起一个请求,struts2架构后创建一个action实例同时创建一个OgnlValueStack值栈实例,这个实例贯穿Action的整个生命周期,Struts2中使用OGNL将亲求Action的参数封装为对象存储到值栈中,并通过OGNL表达是读取值栈中的对象属性值。
为啥要使用ValueStack?
存入ValueStack中的数据,我们在任何地方都可以把它取出来。(页面、action、配置文件)所以建议把数据存放到值栈中。
ValueStack的内部结构
structs2引用OGNL的时候并没有引入源代码,所以我们在导入struts2源码后并不能查看OGNL的源码,我们需要再导入ognl的源码。
ValueStack中有两个主要的区域:
root区和context区:root区是CompoundRoot类继承了ArrayList集合,其实就是一个ArrayList集合,,实现了压栈和出栈的功能,存储action实例及请求参数,其中保存对象Object;context区是OgnlContext类实现Map集合的一个Map集合,其键值为常用web开发对象的引用。
ValueStack和ActionContext的关系
类比于ServletContext, ActionContext对象是获取action上下文的对象。可以通过下面的方法获取
struts2-core-2.3.24.jar/StrutsPrepareAndExecuteFilter.class/doFilter(ServletRequest req, ServletResponse res, FilterChain chain)方法中,如果要经过过滤器,则执行有这句:prepare.createActionContext(request, response); 翻看源码:其中有一个逻辑:如果老的ActionContext为空, ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack(); 这句话创建了一个ValueStack对象。 |
分析源码得出结论:
当请求过来的时候,执行过滤器中的doFilter方法,这个方法中创建ActionContext,再ActionContext创建的过程中,创建了ValueStack对象,然后将ValueStack的对象传递给了ActionContext。这就是可以通过ActionContext获取值栈对象的原因。
ValueStack stack = ActionContext.getContext().getValueStack();
获取值栈对象的两种方法
1.通过ActionContext的方法,如上面的代码所示。
2.通过request。struts2将值栈存入request一份。如下
ValueStack stack = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
向值栈中存入数据
1.在action中提供属性get方法的方式:(部分代码) 默认将数据不存在栈顶
public class ValueStackDemo3 extends ActionSupport {
//action会被压入值栈中,所以属性也会被压入值栈中,我们只需要提供get方法即可
User user ;
public User getUser() {
return user;
}
@Override
public String execute() throws Exception {
user = new User("mlgg", "213");
return SUCCESS;
}
}
取值:
<s:debug></s:debug>
<s:property value="user.username"/>
<s:property value="user.password"/>
2.使用ValueStack中本身的方法方式(部分代码)这种方式将数据存在栈顶
public class ValueStackDemo4 extends ActionSupport {
@Override
public String execute() throws Exception {
ValueStack valueStack = ActionContext.getContext().getValueStack();
User user = new User("aloha","keep moving");
//方式一
valueStack.push(user);//压栈
//方式二:这种方式可以压栈,但是在debug标签中显示为null,我们可以手动获取到jsp页面
valueStack.set("zyf", "upup");
return super.execute();
}
}
debug标签执行结果(未写方式二前):
可以看到此时的栈顶是User对象 username aloha ,这时我们在jsp中获取值不能使用user.username等的方式,只能使用下面的方式直接获取:
<s:property value="username"/>
<s:property value="password"/>
<!-- 方法二 -->
<s:property value="zyf"/>
在root中获取数据&在context中获取数据
public class ValueStackDemo5 extends ActionSupport {
@Override
public String execute() throws Exception {
//向堆栈中保存数据
User user = new User("java","no.1");
ValueStack valueStack = ActionContext.getContext().getValueStack();
valueStack.push(user);
//向集合中保存数据
List<User> list = new ArrayList<User>();
list.add(new User("python", "to be no.1"));
list.add(new User("javascript", "you don't know me"));
list.add(new User("go", "small King"));
ActionContext.getContext().getValueStack().set("list", list);
//向context中保存数据,这些数据可以在debug标签中找出
ServletActionContext.getRequest().setAttribute("name", "aloha");
ServletActionContext.getRequest().getSession().setAttribute("name", "mlgg");
ServletActionContext.getServletContext().setAttribute("name", "sugarcane");
return super.execute();
}
}
<s:property value="username"/>
<s:property value="password"/><br/>
<!-- 获取集合中的数据 -->
<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/>
<!-- 获取context中的数据 -->
<s:property value="#request.name"/>
<s:property value="#session.name"/>
<s:property value="#application.name"/>
<s:property value="#attr.name"/>
<s:property value="#parameters.id"/>
结果:
注意:attr获取的是request中的数据。如果request中没有数据,那么获取的是session中的数据;如果session中没有数据,那么获取的是application中的数据。
struts2对EL表达式进行了增强
对request.getAttribute(String name)方法进行了增强。所以在struts2框架中EL表达式也可以进行获取对象。
OGNL中的特殊字符
#
获取context的数据
构建map集合
<body>
<h1>#的用法</h1>
<h3>1.获取context数据</h3>
<%
request.setAttribute("aloha", "go down");
%>
<s:property value="#request.aloha"/>
<h3>2.创建map集合</h3>
<s:iterator var="i" value="{'aa','bb','cc','dd'}"><!-- 默认把这个装为list集合 -->
<s:property value="i"/> -- <s:property value="#i"/><br/>
</s:iterator><br/>
<h3>3.创建map集合</h3>
<s:iterator value="#{'aa':'11','bb':'22','cc':'33'}">
<s:property value="key"/> -- <s:property value="value"/><br/>
</s:iterator><br/>
<s:iterator var="entry" value="#{'aa':'11','bb':'22','cc':'33'}">
<s:property value="#entry.key"/> -- <s:property value="#entry.value"/><br/>
</s:iterator>
<hr/>
<s:radio list="{'男','女'}" name="sex" label="性别" /><br/>
<s:radio list="#{'1':'男','2':'女' }" name="sex1" label="性别(Map)" />
<s:debug></s:debug>
</body>
radio中的1,2构成map集合的键,作用同于<input>标签中的value的值。map集合的值相当于input标签外的显示。
%(基本没什么卵用)
1.强制解析OGNL
应用场景:struts2标签不能嵌套,比如我们要在s:textfield文本框的value赋值,那么是 不能直接把一个OGNL表达式放里边的,直接写也不解析。
我们给它写在%{}中,可强制解析
<%
request.setAttribute("aloha", "love_java");
%>
what aloha love?<s:textfield name="aloha" value="%{#request.aloha}" />
2.强制不解析OGNL(不常用)
我们把表达式写在%{' '}中强制不解析
$
在配置文件(xml、属性文件)使用,属性文件主要在国际化中使用,我国做的软件不做国际化!咋地?
在配置文件中从值栈中获取变量的值。比如在配置文件中提供下载链接提供值栈中的链接。