Struts2总结之ognl与valueStack
ognl介绍
-
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。 Struts2框架使用OGNL作为默认的表达式语言。
xwork 提供 OGNL表达式
ognl-3.0.5.jar -
OGNL 是一种比EL 强大很多倍的语言
-
OGNL 提供五大类功能
- 支持对象方法调用,如xxx.doSomeSpecial();
- 支持类静态的方法调用和值访问
- 访问OGNL上下文(OGNL context)和ActionContext; (重点 操作ValueStack值栈 )
- 支持赋值操作和表达式串联
- 操作集合对象。
-
在struts2中使用ognl表达式
-
需要结合struts2的标签使用<s:property value=“ognl表达式”>
-
<s:property value="'abc'.length()"/> 演示对象调用方法 <s:property value="@java.lang.Math@max(10,20)"/> 演示静态成员访问.
-
-
注意:在struts2中使用静态成员访问,必须设置一个常量:
struts.ognl.allowStaticMethodAccess=false
#号的用法
-
用法一:
代表 ActionContext.getContext() 上下文
<s:property value="#request.name" /> 相当于相当于下面的一句 ActionContext().getContext().getRequest().get("name"); #request #session #application #attr #parameters
-
用法二
不写# 默认在 值栈中root中进行查找
<s:property value=“name” /> 在root中查找name属性
查询元素时,从root的栈顶元素 开始查找, 如果访问指定栈中元素
<s:property value="[1].name" /> 访问栈中第二个元素name属性
访问第二个元素对象 <s:property value="[1].top" /> -
用法三 :进行投影映射 (结合复杂对象遍历 )
1)集合的投影(只输出部分属性
<h1>遍历集合只要name属性</h1> <s:iterator value="products.{name}" var="pname"> <s:property value="#pname"/> </s:iterator>
2)遍历时,对数据设置条件
<h1>遍历集合只要price大于1500商品</h1> <s:iterator value="products.{?#this.price>1500}" var="product"> <s:property value="#product.name"/> --- <s:property value="#product.price"/> </s:iterator>
3)综合
<h1>只显示价格大于1500 商品名称</h1> <s:iterator value="products.{?#this.price>1500}.{name}" var="pname"> <s:property value="#pname"/> </s:iterator>
代码示例:
<title>演示:进行投影操作</title> </head> <body> <s:property value="ps" /> <br> 1.使用iterator进行遍历 <br> <s:iterator value="ps" var="p"> 名称:<s:property value="#p.name" /> <br> 价格:<s:property value="#p.price" /> <br> 数量:<s:property value="#p.count" /> <br> <hr> </s:iterator> <hr> 2.对集合进行投影,只得到指定的属性 <br> <s:iterator value="ps.{name}" var="pname"> <s:property value="#pname" /> </s:iterator> <hr> 3.将ps中价格大于1000的商品得到 <br> <s:iterator value="ps.{?#this.price>1000}" var="p"> 名称:<s:property value="#p.name" /> <br> 价格:<s:property value="#p.price" /> <br> 数量:<s:property value="#p.count" /> <br> <hr> </s:iterator> <hr> 4.将ps中价格大于1000的商品名称得到: <br> <s:iterator value="ps.{?#this.price>1000}.{name}" var="pname"> <s:property value="#pname" /> <br> <hr> </s:iterator> <s:debug /> </body>
-
用法四:使用#构造map集合
-
经常结合 struts2 标签用来生成 select、checkbox、radio
-
代码示例:
<title>演示:构造集合</title> </head> <body> 1.可以使用#来构建一个Map集合 <br> <s:iterator value="#{'name':'tom','age':20}" var="entry"> <s:property value="#entry.key" />----<s:property value="#entry.value" /> <br> </s:iterator> <hr> 2.可以构建List集合 <br> <s:iterator value="{'aa','bb','cc'}" var="v"> <s:property value="#v" /> <br> </s:iterator> <hr> 3.手动创建一个集合,在struts2中是结合表单标签使用的 <br> <s:form> <s:radio list="{'男','女'}" name="sex"></s:radio> <s:radio list="#{'男':'male','女':'female'}" name="sex"></s:radio> <s:select list="{'a','b','c'}" name="aa"></s:select> <s:select list="#{'a':'aaa','b':'bbb','c':'ccc'}" name="aa"></s:select> </s:form> </body> </html>
-
%号的用法
-
%作用:就是用于设定当前是否要解析其为 ognl表达式.
- %{表达式} 当前表达式会被做为ognl解析.
- %{ ’ 表达式 ’ } 当前表达式不会被做为ognl解析。
-
<s:property value=“表达式”> 对于s:property标签,它的value属性会被默认做为ognl.
-
以后,所有表达式如果想要让其是ognl %{表达式}
<title>演示:%号用法</title>
</head>
<body>
<%
request.setAttribute("username", "tom");
%>
<s:property value="#request.username" default="hello"/><br>
<s:property value="%{#request.username}"/><br> //ognl
<s:property value="%{'#request.username'}"/><br>
<hr>
<s:textfield name="abc" value="%{#request.username}"/>
</body>
$号的用法
$作用:就是在配置文件中使用ognl表达式来获取valueStack中数据.
-
struts.xml(文件的上传下载)
<action name="download" class="com.syj.action.DownloadAction"> <result name="input">/download.jsp</result> <result>/success.jsp</result> <result type="stream" > <param name="contentType" > ${contentType}</param> <!-- 调用当前action中的getContentType()方法 --> <param name="contentDisposition">attachment;filename=${downloadFileName}</param><!-- 调用当前action中的getDownloadFileName()方法 --> <param name="inputStream">${inputStream}</param><!-- 调用当前action中的getInputStream()方法 --> </result> </action>
-
在校验文件中使用ActionClassName-regist-validation.xml
<!--配置名字长度时 --> <field-validator type="stringlength"> <param name="maxLength">12</param> <param name="minLength">6</param> <message>用户名必须在${minLength}-${maxLength}位之间</message> </field-validator>
配置年龄范围时
<!-- 对age进行校验,规定年龄必须在10-40之间 --> <field name="age"> <field-validator type="int"> <param name="min">10</param> <param name="max">40</param> <message>年龄必须在${min}--${max}之间</message> </field-validator> </field>
-
在国际化文件中使用
配置临时的国际化配置文件:my.properties
username=${#request.username}
jsp
<title>演示:$用法</title> </head> <body> <% request.setAttribute("username", "张三"); %> 在国际化文件中使用:<br> <s:i18n name="cn.itcast.resource.my"> <s:text name="username"/> </s:i18n> </body>
Ognl的总结:总结: #就是用于获取数据 %就是用于设置是否是ognl表达式 $就是在配置文件中使用ognl.
ValueStack介绍
首先记住此图后面会做解释
-
它是一个接口com.opensymphony.xwork2.util.ValueStack。
我们使用它是将其做为一个容器,用于携带action数据到页面.
在在页面上通过ognl表达式获取数据。 -
我么将从下面的留个问题去了解ValueStack
- 什么是valueStack?
- valueStack结构?
- 值栈对象的创建 ,ValueStack 和 ActionContext 是什么关系 ?
- 如何获得值栈对象?
- 向值栈保存数据 (主要针对 root)
- 在JSP中获取值栈的数据
问题一:什么是valueStack?
- valueStack主要是将action数据携带到页面上,通过ognl获取数据
1.ValueStack有一个实现类叫OgnlValueStack.
2.每一个action都有一个ValueStack.(一个请求,一个request,一个action,一个valueStack) valueStack生命周期就是request生命周期。
3.valueStack中存储了当前action对象以及其它常用web对象(request,session,application.parameters)
4.struts2框架将valueStack以“struts.valueStack”为名存储到request域中。
-
代码查看
-
每一次执行请求都会执行过滤器,进而执行StrutsPrepareAndExecuteFilter过滤器中的doFilter方法,让然后执行:
execute.executeAction(request, response, mapping);
-
执行进入到ExecuteOperations类中的executeAction方法:
dispatcher.serviceAction(request, response, mapping);
-
执行进入Dispatche类中的serviceAction
每一个action都有一个ValueStack.
struts2框架将valueStack以“struts.valueStack”为名存储到request域中。
-
问题二:valueStack结构?
-
ValueStack中主要保存有两部分内容:
- root属性 (CompoundRoot),CompoundRoot 就是ArrayList
- context 属性 (OgnlContext ),OgnlContext 就是 Map
-
两部分内容分别存储的内容
- list集合中存储的是action相关信息
- map集合中存储的是相关映射信息,包含 paramters,request,session,application attr等。
-
分别从两部分中取值的方式:
- 我们想要从list中获取数据,可以不使用#号.(它就是ognl的root)
- 如果从map中获取数据,需要使用#. (其实在struts2中的map–context其实就是ognlContext)
-
总论:
- ValueStack它有两部分 List Map
- 在struts2中List就是ognlContext.里的root在Map中有引用 Map就是ognlContext.
- 默认情况下,在struts2中从valueStack获取数据从root中获取。
-
查看代码
-
ValueStack中的两个方法可以看出保存有两部分内容:分别是List(Root) 和Map(Context)
-
public abstract Map<String, Object> getContext();
-
public abstract CompoundRoot getRoot(); public class CompoundRoot extends CopyOnWriteArrayList<Object>
-
问题三:值栈对象的创建 ,ValueStack 和 ActionContext 是什么关系 ?
valueStack是每一次请求时,都会创建.
在ActionContext中持有了valueStack的引用。
问题四:如何获得值栈对象?
-
对于valueStack获取有两种方式:
-
1.通过 request获取
ValueStack vs=(ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
-
2.通过ActionContext获取.
ValueStack vs=ActionContext.getContext().getValueStack();
-
问题五:向值栈保存数据 (主要针对 root)
-
主要有两个方法
-
push(Object obj)------->底层就是 root.add(0,obj) 将数据存储到栈顶。
-
set(String name,Object obj);----->底层是将数据封装到HashMap中,在将这个HashMap通过push存储。
-
在jsp中 通过 <s:debug /> 查看值栈的内容
- 大致分为两大块分别为root和context
- 大致分为两大块分别为root和context
-
问题六:在JSP中获取值栈的数据
-
root中数据不需要#,而context中数据需要#
- 如果栈顶是一个Map集合,获取时,可以直接通过Map集合的key来获取value.
<s:property value="username"/>
- 如果栈顶数据不是一个Map,没有key值,可以使用序号来获取。
<s:property value="[0]"> 从0的位置向下查找所有。 <s:property value="[0].top"> 只查找0位置上数据。
-
如果获取OgnlContext中数据:
1.request数据 request.setAttribute()
2.session数据 session.setAttribute()
3.application数据 application.setAttribute()
4.attr 依次从request,session.application中查找
5.parameters 获取请求参数 -
配置一个简单的Struts2请求
ValueStackDemo2Action
public class ValueStackDemo2Action extends ActionSupport { @Override public String execute() throws Exception { // 向valueStack中存储数据(root) ValueStack vc = ActionContext.getContext().getValueStack(); //下面两行位置不同取值方式不同 vc.set("username", "tom"); vc.push("syj"); return SUCCESS; } }
struts.xml配置
<action name="demo2" class="com.syj.action.ValueStackDemo2Action" > <result >/ognl2.jsp</result> </action>
ognl2.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>使用ognl来获取valueStack的数据</title> </head> <body> <h2>1.获取栈顶的map</h2><br> 如果栈顶是一个Map集合,获取时,可以直接通过Map集合的key来获取value.<br> <s:property value="username" /><br> <hr> <h2>2.获取栈顶非map集合数据</h2><br> 从0的位置向下查找所有。<br> <s:property value="[0]" /><br> 只查找0位置上数据。<br> <s:property value="[0].top" /><br> 只查找1位置上数据。<br> <s:property value="[1].top" /><br> <hr> <h2>3.获取OgnlContext中数据</h2> <br> <% request.setAttribute("rname", "rvalue"); session.setAttribute("sname", "svalue"); application.setAttribute("aname", "avalue"); %> <s:property value="#request.rname"/><br> <s:property value="#session.sname"/><br> <s:property value="#application.aname"/><br> <!-- 依次从request,session.application中查找 --><br> <s:property value="#attr.sname" /> <hr> <h2>4.获取请求中携带的参数</h2> <!-- #parameters.username获取请求中属性为username的值 --> <s:property value="#parameters.username" /><br> <!-- #parameters.password获取请求中属性为username的值 --> <s:property value="#parameters.password" /><br> <hr> <s:debug/> </body> </html>
ValueStack主流应用:解决将action数据携带到jsp页面。
-
action向jsp携带数据,都是什么样的数据?
-
文本(字符串)
1.fieldError 校验数据错误信息提示
2.actionError 关于逻辑操作时错误信息(例如登录失败)
3.message 就是一个信息.this.addFieldError(“msg”, “字段错误信息”);
this.addActionError(“Action全局错误信息”);
this.addActionMessage(“Action的消息信息”);fieldError 针对某一个字段错误信息 (常用于表单校验)、actionError (普通错误信息,不针对某一个字段 登陆失败)、 actionMessage 通用消息
在jsp中使用 struts2提供标签 显示消息信息
<s:fielderror fieldName="msg"/> <s:actionerror/> <s:actionmessage/>
-
复杂数据
可以使用valueStack存储.
在action中存储数据:
List<User> users = new ArrayList<User>(); users.add(new User("tom", "123", 20, "男")); users.add(new User("james", "456", 21, "男")); users.add(new User("fox", "789", 26, "男")); vs.push(users);
在页面上获取数据:
使用了<s:iterator>标签来迭代集合。
<s:iterator value="[0].top" var="user"> 这是将集合中迭代出来每一个元素起个名称叫user,而user是存储在context中,不在root中.
起别名
<s:iterator value="[0].top" var="user"> username:<s:property value="#user.username"/><br> password:<s:property value="#user.password"/> <hr> </s:iterator>
如果我们在使用<s:iterator>进行迭代时,没有给迭代出的元素起名.
<s:iterator value="[0].top"> username:<s:property value="username"/><br> password:<s:property value="password"/> <hr> </s:iterator>
-
代码示例:
在acition中往valueStack中存储数据,用到一个User的javabean
ValueStackDemo3Action
public class ValueStackDemo3Action extends ActionSupport { @Override public String execute() throws Exception { // 向valueStack中存储数据(root) ValueStack vc = ActionContext.getContext().getValueStack(); List<User> users = new ArrayList<User>(); users.add(new User("tom", "123", 22, "男")); users.add(new User("jame", "abc", 22, "女")); users.add(new User("lucy", "321", 23, "男")); users.add(new User("xiaohong", "ABC", 20, "女")); // vc.push(users); vc.set("users", users); return SUCCESS; } }
配置struts.xml
编写jsp代码:
ognl3.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>使用ognl来获取valueStack中存取的复杂数据</title> </head> <body> <h1>1.使用push存储数据获取</h1> <s:property value="[0].top" /> <hr> 给获取到的复杂数据起一个别名<br> <font color="red">这是将集合中迭代出来每一个元素起个名称叫user,而user是存储在context中,不在root中.l</font><br> <s:iterator value="[0].top" var="user" > username:<s:property value="#user.username" /><br> password:<s:property value="#user.password" /><br> age:<s:property value="#user.age"/><br> gender:<s:property value="#user.gender"/><br> </s:iterator> <hr> 不给获取到的复杂数据起别名 <s:iterator value="[0].top" > username:<s:property value="username"/><br> password:<s:property value="password"/><br> age:<s:property value="age"/><br> gender:<s:property value="gender"/><br> </s:iterator> <hr> <h1>2.使用set存储数据</h1> <hr> <s:property value="users" /><br> <s:iterator value="users" var="user" > username:<s:property value="#user.username"/><br> password:<s:property value="#user.password"/><br> age:<s:property value="#user.age"/><br> gender:<s:property value="#user.gender"/><br> </s:iterator> <hr> <s:iterator value="users" > username:<s:property value="username"/><br> password:<s:property value="password"/><br> age:<s:property value="age"/><br> gender:<s:property value="gender"/><br> </s:iterator> <hr> <s:debug/> </body> </html>
运行结果:
-
默认压入到valueStack中的数据.
在执行action和拦截器之前,会将action压入栈中
-
访问的action对象会被压入到valueStack中.
-
DefaultActionInvocation 的 init方法 stack.push(action);
-
Action如果想传递数据给 JSP,只有将数据保存到成员变量,并且提供get方法就可以了
我们提供的getXXX方法在默认压栈时会提供XXX的一个Property Name值
-
我们在jsp页面可以通过ognl表达是获取值
- ModelDriveInterceptor会执行下面操作
将实现了ModelDrive接口的action中getModel方法的返回值,也就是我们所说的model对象压入到了valueStack.
-
有一种情况是声明之后重新赋值,我们应该如何取值:
我们应该从model中取得新赋予的值