Struts2 OGNL表达式、ValueStack
OGNL简介
OGNL,即Object-Graph Navigation Language,对象视图导航语言,是一种数据访问语言,比EL表达式更加强大:
- EL只能从11个内置对象中取值,且只能获取属性,不能调用对象的方法。
- OGNL可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图。
OGNL是可以单独使用的。OGNL并不属于Struts2,只不过Struts2觉得OGNL不错,把OGNL给整合进来了。
Struts2的8个核心jar包中已经包含了OGNL的jar包,不需要我们再导包。
ValueStack(值栈) 简介
ValueStack是Struts2的一个接口,用于存储Struts中的数据。ValueStack接口有一个实现类OgnlValueStack。
创建一个Action实例时,会自动为这个Action实例创建一个ActionContext实例,创建ActionContext实例时,又会自动创建一个ValueStack(OgnlValueStack)的实例,将OgnlValueStack的引用放到ActionContext中,将ActionContext的引用放到Action中。
这三者的生命周期是一致的。
ActionContext存储数据,实际是用ActionContext中的ValueStack来存储数据。
ValueStack的内部结构
ValueStack由2部分组成:root、context。
root部分是一个CompoundRoot对象,extends ArrayList,内部维护了一个列表。
context部分是一个OgnlContext对象,implements Map,内部维护了一个Map,用来存放常用对象的引用,其中也包括root部分的引用,所以OgnlContext其实是可以访问到所有的数据的。
Ognl表达式使用的数据就是ValueStack中的数据。
ActionContext能获取到域对象(Map)、获取到原生的Servlet对象(request、response、session),就是因为ActionContext内部有ValueStack的引用,可以访问ValueStack中的数据(对象)。
可以在jsp中用<s:debug>标签显示ValueStack的调试信息。
平时说的操作值栈,是指操作root部分,因为context中这些常用对象的引用由struts自动管理,无需我们操作。
ValueStack,顾名思义,是一个栈,ValueStack的栈指的是root部分,栈是通过ArrayList实现的。
获取ValueStack对象的2种方式
ValueStack valueStack = ActionContext.getContext().getValueStack(); Object attribute = ServletActionContext.getRequest().getAttribute("struts.valueStack"); Object attribute1 = ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
获取ValueStack对象有2种方式:通过ActionContext对象、通过request对象。
AtionContext中有ValueStack的引用,能获取到;
Struts2对request进行了包装、增强(拦截器|过滤器),request中也有ValueStack的引用,也能获取到。
但request是以属性获取,返回值是Object,需要强转,且字符串常量不好记,通过ActionContext获取更方便。
字符串常量 ServletActionContext.STRUTS_VALUESTACK_KEY 即 “struts.valueStack”。
一个Action实例中,只有一个对应的ActionContext对象,只有一个对应的ValueStack对象。
ValueStack的数据存取
1、将数据设置为Action的成员变量,并提供getter方法
public class LoginAction extends ActionSupport{ private String name; private User user; public String getName() { return name; } public User getUser() { return user; } @Override public String execute() throws Exception { name = "chy"; user = new User("张三", 19); return "index"; } }
root部分是栈,Struts默认会把当前Action的实例压入栈中,所以能从ValueStack中取到此Action实例的属性。
取Action的属性,底层是调用此Action的getter方法,所以需要提供对应的getter方法。
在jsp中取值:
<s:property value="name" /> <s:property value="user.name" />
<s:property>是struts2的标签,value中写ognl表达式。
user.name底层是调用user对象的getter方法,所以User类需要提供对应的getter方法。
Struts2对request对象进行了增强,request中也有ValueStack的引用,所以request中也能获取到ValueStack中的数据。
${requestScope.name} ${requestScope.user.name} <%=request.getAttribute("name") %> <%=(User)request.getAttribute("user") %> <% String name = (String) request.getAttribute("name"); out.print(name); User user = (User)request.getAttribute("user"); out.print(user.getName()); %>
EL表达式可以不指定requestScope:
${name}
${user.name}
默认先在requestScope中找,找不到就到ValueStack中找。
此种方式是将数据存到ValueStack的root部分。
如果存储的数据个数较多,Action中会有大量的成员变量、getter方法,很冗杂,一般不用这种。
2、直接操作ValueStack
public class LoginAction extends ActionSupport{ @Override public String execute() throws Exception { ValueStack valueStack = ActionContext.getContext().getValueStack(); String name = "chy"; User user = new User("张三", 18); valueStack.set("user",user); valueStack.set("name",name); return "index"; } }
JSP中取值:
<s:property value="name" /> <s:property value="user.name" />
此种方式是将数据都放到一个Map中,将此Map压入栈顶,在JSP直接通过Map的key来获取对应的value。
或者使用push():
public class LoginAction extends ActionSupport{ @Override public String execute() throws Exception { ValueStack valueStack = ActionContext.getContext().getValueStack(); User user = new User("张三", 18); valueStack.push(user); return "index"; } }
<s:property value="name" />
直接将push的对象压入栈顶,JSP中通过属性名来获取值。
底层是调用对象的getter方法来获取属性值,所以对象需要提供getter方法。
一次push就向栈顶压入一个对象,取对象属性的时候不能带对象名,是从栈顶开始向栈底寻找这个属性,找到就返回。
valueStack.push(user1);
valueStack.push(user2);
比如push2次, <s:property value=”name” /> ,取到的是靠近栈顶的user2的name属性。
int、String、Array、List、Map等对象push到栈中,能获取到length、size这些属性值,但获取不到这些对象本身,也获取不到数组、集合中的元素,因为这些数组、集合中的元素不是属性,没有属性名。
set()是将数据都放到一个Map中,将此Map压入栈顶,数据都在栈顶。
set()根据Map中的key来取元素,能获取到对象本身,如果对象是数组、集合,也能获取到数组、集合中的元素。
public class LoginAction extends ActionSupport{ @Override public String execute() throws Exception { ValueStack valueStack = ActionContext.getContext().getValueStack(); ArrayList<String> list = new ArrayList<>(); list.add("刘"); list.add("关"); list.add("张"); valueStack.set("list",list); return "index"; } }
<s:property value="list" /> <s:property value="list[0]" />
可通过key获取对象本身,也可以通过下标获取Array、List的某个元素,可通过key获取Map对象中某个键值对的value。
set()比push()更优,推荐使用。
既然是栈,操作的自然是root部分,存取的是root部分的数据。
3、存取context中的数据
public class LoginAction extends ActionSupport{ @Override public String execute() throws Exception { HttpServletRequest request = ServletActionContext.getRequest(); request.setAttribute("user",new User("chy",18)); request.setAttribute("group","vip"); return "index"; } }
<s:property value="#request.group" /> <s:property value="#request.user" /> <s:property value="#request.user.name" />
需要加#,#表示是从context(几个域对象)中取数据。
获取原生的Servlet对象,setAttribute()存入数据,在JSP从对应的对象中取出来即可。
请求参数封装在request对象的parameters对象中,不是直接封装在request对象中,request中直接封装的是setAttribute()存入的数据。
把request作为一个大Map,里面还有一个小Map,小Map用于封装请求参数。 Map<String, String[]> parameterMap = request对象.getParameterMap(); 。
比如表单字段: 用户名:<input type=”text” name=”user” />
Object obj=request对象.getAttribute(“user”); 获取的是setAttribute()存入request中的数据。
String user = request对象.getParameter(“user”); 获取的才是请求参数(从小Map中查找数据)。
在JSP中获取请求参数:
<s:property value="#parameters.user" /> <s:property value="#request.parameters.user" /> <%=request.getParameter("user")%>
<s:property value=”#request.user” /> 获取的不是请求参数,而是setAttribute()存入request中的数据。
OGNL表达式
OGNL是一种数据访问语言,可直接使用(写在Java代码中,即可获取值,也能设置值),也可配合Struts2标签使用,写在Struts2标签中叫做OGNL表达式,用于获取值。
访问ValueSatck中的数据:
<s:property value="name" /> <s:property value="user.name" /> <s:property value="#parameters.user" />
不带#的是从root中取值,带#的是从context中取值。
特殊字符#:从context中取值,构建Map。
遍历List:
<s:iterator var="item" value="{'刘备','关羽','张飞'}"> <s:property value="item" /> </s:iterator>
var指定临时变量,表示一项,value指定Array、List。<s:iterator>的元素体即循环体。
不能 <s:property value=”hello+item” /> 或者 <s:property value=”hello”+”item” /> 这样来指定格式,这样解析不了临时变量。
可以配合其他标签来指定格式,比如:
hello <s:property value="item" />!
<div class=""> <p>
hello <s:property value="item" />
</p> <img src="" /> </div>
构建Map:
<s:iterator var="entry" value="#{'name':'刘备','age':40,'group':'蜀'}"> <s:property value="entry" />
<s:property value="entry.key" />
<s:property value="entry.value" /> </s:iterator> <s:iterator value="#{'name':'刘备','age':40,'group':'蜀'}"> <s:property value="key" />:<s:property value="value" /> </s:iterator>
#表示这玩意儿是Map。Map默认var为key、value,所以可缺省 var=”key,value” 。
如果指定了var=”entry”,可通过entry.key,entry.value来引用属性。
IDEA下可能会报红,这是IDEA的问题,代码本身没有错误。
单选按钮:
<s:radio list="{'男','女'}" name="gender" label="性别" /> <s:radio list="#{'male':'男','female':'女'}" name="gender" label="性别" />
使用List时,<input type=”radio” />的value、选项文字都是list中的元素。
使用Map时,Map的key作为<input type=”radio” />的value,Map的value作为选项文字。
构建Map时,key必须引,value是字符、字符串才引。
特殊符号%:强制解析、不解析OGNL表达式
有的Struts标签默认会解析OGNL表达式,有的Struts标签默认不会解析OGNL表达式。
比如<s:textfield />默认不会解析OGNL表达式,会将#session.user作为字符串原样显示。
<% session.setAttribute("user","chy"); %> <s:textfield name="user" value="#session.user" label="用户名" />
将OGNL表达式放在%{ }中,会作为OGNL表达式处理,强制解析。
<s:textfield name="user" value="%{#session.user}" label="用户名" />
有的Struts标签默认会解析OGNL表达式,如果不想解析OGNL表达式:
%{'#session.user'}
放在%{‘ ‘}中即可,会作为字符串处理。
特殊符号$:在配置文件中使用OGNL表达式取值
将OGNL表达式放在${ }中即可。
.properties配置文件:
user=${#session.user.name}
xml配置文件:
<param>${#session.user.name}</param>
使用OGNL表达式访问成员属性、调用成员方法
<%
User user=new User("曹操",40);
session.setAttribute("user",user);
%>
<s:property value="#session.user.name" />
<s:property value="#session.user.getName()" />
<s:property value="#session.user.setName('chy')" />
<s:property value="'hello'.length()" />
访问成员变量,底层其实是调用对应的getter方法。
如果方法有返回值,会用返回值替换原来的OGNL表达式。
使用OGNL表达式访问静态成员(static)
<s:property value="@java.lang.Math@PI" /> <s:property value="@java.lang.Math@abs(-10)" />
以 @全限定类名@静态变量|静态方法 的方式调用,必须是全限定类名,可以是java自带的类,也可以是自定义的类。
访问静态变量不用进行常量配置,但调用静态方法必须在struts.xml中进行常量配置:
<struts> <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant> <package name="action" namespace="/" extends="struts-default"> </package> </struts>
允许静态方法调用,输入ognl就出来了,将value设置true(默认为false)。
和EL表达式一样,OGNL表达式没有空指针异常、没有数组越界,如果没有指定的变量、方法,不会出现异常。
OGNL比EL更强大。
原文地址: https://www.cnblogs.com/chy18883701161/p/11469786.html
本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
相关文章