(七)Action之ActionContext(OGNL表达式的使用)
一、ActionContext的重要性
- struts中的数据都存放在ActionContext里,所以这部分是Action中的核心。
- ActionContext又称广义值栈,既然有广义值栈就有侠义值栈。
1.广义值栈,指的是ActionContext对象,是Action运行的上下文,包含着request、session、application、attr、parameters、和侠义值栈(Value Stack)等。以后说值栈的时候默认指的就是广义值栈。
2.侠义值栈,ActionContext以侠义值栈作为被OGNL访问的根,在没有特别指明(即用#打头)情况下,默认访问的的侠义值栈的数据。
二、ActionContext组成
- 分为上述六大块。
- 用代码查看上述ActionContext结构
- index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <%String path=request.getContextPath(); %> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <a href="<%=path%>/action/actioncontext">查看ActionContext结构内容</a> </body> </html>
- struts.xml
<struts> <constant name="struts.enable.DynamicMethodInvocation" value="true" /> <constant name="struts.devMode" value="true" /> <package name="default" namespace="/action" extends="struts-default"> <action name="actioncontext" class="action.ActionContext_1"> <result name="actionContext">/actionContext.jsp</result> </action> </package> </struts>
- ActionContext_1.java, 执行成功返回到<result name="actionContext">/actionContext.jsp</result>页面,我
package action; public class ActionContext_1 { public String execute() {
/**
*我们在ActionContext的request里存放一个值
*/
request.setAttribute("hello", "world"); return "actionContext"; } }
- actionContext.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <s:debug></s:debug> </body> </html>
-
<s:debug></s:debug>标签用于显示ActionContext的结构内容。
结果:
三、ActionContext各组成部分简介
3.1 值栈(ValueStack)位于最顶端,即上图的:
- a上述例子中的ction包的ActionContext_1类的对象引用存放在这里。
3.2 其他5个组成部分(request、session、application、attr、parameters)存在值栈的下方。
四、取出ActionContext中的数据(使用OGNL表达式)
4.1 如果要取出valueStack值栈中的值,直接<s:property value="值栈中的变量名"/> 即可。
4.2 如果要取出非valueStack值栈中的值,就得用<s:property value="#非值栈中的变量名"/>才能获取,而"#变量名" 这种表达方法为OGNL表达式。
4.3 示例(上述例子在ActionContext_1.java中我们对request设置了一个值"hello"="world",现在我们把它取出来):
- 除了actionContextjsp页面外其他文件均与上述例子一样。 actionContextjsp:
<body> <pre> <h3>用debug标签显示ActionContext结构</h3> <s:debug></s:debug> <h3>取出ActionContext中的值</h3> <s:property value="#request.hello"/> </pre> </body>
结果:
五、OGNL表达式
5.1 OGNL表达式的特点
5.2 OGNL表达式的使用:
- 获得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"/>
- <s:property value="#parameters.name"/>
- 构建一个map集合
- <%-- list --%>
- <s:radio name="sex" list="{'男','女'}"></s:radio>
- <%-- map --%>
- <s:radio name="sex" list="#{'0':'男','1':'女'}"></s:radio>
- 强制字符串解析成OGNL表达式。
- <s:textfield value="%{#request.msg}"/>
- { }中值用引号引起来,此时不再是ognl表达式,而是普通的字符串,到底使用单引号还是双引号是由外层引号决定的。
- <s:property value="%{'#request.msg'}"/>
$ 符号的用法
在配置文件中可以使用OGNL表达式,例如:文件下载的配置文件。EL表达式也是使用$
- <action name="download1" class="cn.itcast.demo2.DownloadAction">
- <result name="success" type="stream">
- <param name="contentType">${contentType}</param>
- <param name="contentDisposition">attachment;filename=${downFilename}</param>
- </result>
- </action>
5.2.1 访问成员变量和成员方法
- 成员变量和成员方法都是存放在侠义值栈里的,可以直接访问。
- 成员方法不现实在侠义值栈里,但是也可以直接访问。
- index.jsp
<body> <a href="<%=path%>/action/actioncontext">OGNL访问成员变量和成员方法</a> </body>
-
- struts.xml
<struts> <constant name="struts.enable.DynamicMethodInvocation" value="true" /> <constant name="struts.devMode" value="true" /> <package name="default" namespace="/action" extends="struts-default"> <action name="actioncontext" class="action.ActionContext_1"> <result name="actionContext">/actionContext.jsp</result> </action> </package> </struts>
-
- ActionContext_1.java
package action; import actionUtil.BaseAction; /** * OGN表达式访问成员变量和成员方法 * @author hj * */ public class ActionContext_1 extends BaseAction{ //BaseAction类提供了servlet的原生对象 private String userName="张三"; //成员变量并设值 public int add(int x,int y){ //成员方法 return x+y; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String execute() { return "actionContext"; } }
-
- actionContext.jsp
<body> <pre> <h3>用debug标签显示ActionContext结构</h3> <s:debug></s:debug> <h3>OGNL访问成员变量和成员方法</h3> userName=<s:property value="userName"/> 年龄=<s:property value="add(10,25)"/> </pre> </body>
结果:
5.2.2 访问静态变量和静态方法(访问静态方法的功能在struts高版本被取消,无法使用)
- 访问静态变量语法:<s: property value="@完整包名@静态变量名"/> 如 <s:property value="@actionUtil.StaticClass@PI"/> 表示输出actionUtil包下的StaticClass类中的PI静态变量。
- 访问静态方法语法与上面类似。
5.2.3 访问集合
- index.jsp
<body> <a href="<%=path%>/action/actioncontext">OGNL访问集合</a> </body>
- struts.xml
<struts> <constant name="struts.enable.DynamicMethodInvocation" value="true" /> <constant name="struts.devMode" value="true" /> <package name="default" namespace="/action" extends="struts-default"> <action name="actioncontext" class="action.ActionContext_1"> <result name="actionContext">/actionContext.jsp</result> </action> </package> </struts>
-
ActionContext_1.java(集合的定义与初始化)
package action; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import actionUtil.BaseAction; import bean.UserBean; /** * OGNL表达式访问集合Set/List/Map * @author hj * */ public class ActionContext_1 extends BaseAction{ private Set<String> strSet; private List<UserBean> list; private Map<String,Object> map; public Set<String> getStrSet() { return strSet; } public void setStrSet(Set<String> strSet) { this.strSet = strSet; } public List<UserBean> getList() { return list; } public void setList(List<UserBean> list) { this.list = list; } public Map<String, Object> getMap() { return map; } public void setMap(Map<String, Object> map) { this.map = map; } public String execute() { //set集合的初始化 this.strSet=new HashSet<String>(); this.strSet.add("a"); this.strSet.add("b"); this.strSet.add("c"); //List的初始化 this.list=new ArrayList<UserBean>(); for(int i=0;i<4;i++){ UserBean user=new UserBean(i+1,"姓名"+i); list.add(user); } //Map的初始化 this.map=new HashMap<String,Object>(); this.map.put("map中的第一个值", "key1"); this.map.put("map中的第二个值", this.list); return "actionContext"; } }
- actionContext.jsp (获取广义值栈中的Set、map、list的值)
<body> <pre> <h3>用debug标签显示ActionContext结构</h3> <s:debug></s:debug> <h3>OGNL访问集合</h3> set元素共有:<s:property value="strSet"/> set长度=<s:property value="strSet.size()" /> add=<s:property value="strSet.add('d')" /> set元素共有:<s:property value="strSet"/> remova=<s:property value="strSet.remove(\"b\")" /> set元素共有:<s:property value="strSet"/> Map Map全部值=<s:property value="map"/> map中的第一个值:<s:property value="map. map中的第一个值"/> map中的第二个值:<s:property value="map.get('map中的第二个值')"/> map中的第二个值中的UserBean的userName:<s:property value="map.get('map中的第二个值').get(0).getUserName()"/> map中的第二个值中的UserBean的userId::<s:property value="map['map中的第二个值'].get(0).getUserId()"/> List List全部元素:<s:property value="list"/> list的第一个UserBean的ID:<s:property value="list.get(0).getUserId()"/> </pre> </body>
- Map中数字开头或者特殊符号要用[]来获取值。例:<s:property value="map['map中的第二个值'].get(0).getUserId()"/>
结果:
- 注意:
上述的actionContext.jsp文件中的remova=<s:property value="strSet.remove(\"b\")" />如果没有加“\”反斜杠,则该函数调用错误。
5.2.4 访问作用域中的数据
- index.jsp
<body> <a href="<%=path%>/action/actioncontext">OGNL访问作用域</a> </body>
- struts.xml
<struts> <constant name="struts.enable.DynamicMethodInvocation" value="true" /> <constant name="struts.devMode" value="true" /> <package name="default" namespace="/action" extends="struts-default"> <action name="actioncontext" class="action.ActionContext_1"> <result name="actionContext">/actionContext.jsp</result> </action> </package> </struts>
-
ActionContext_1.java(在这个Action中把值存放到作用域中,以供jsp获取显示)
package action; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import actionUtil.BaseAction; import bean.UserBean; /** * OGNL表达式访问作用域中的数据,要用#来获取 * @author 夏洛克的喵~ * */ public class ActionContext_1 extends BaseAction{ public String execute() { request.setAttribute("request_key", "request_value"); session.setAttribute("session_key", "session_value"); servletContext.setAttribute("sevletContext_key", "servletContext_value"); return "actionContext"; } }
- actionContext.jsp(在这个页面显示出作用域中的数据)
<body> <pre> <h3>用debug标签显示ActionContext结构</h3> <s:debug></s:debug> <h3>OGNL作用域中的数据</h3> request作用域中的数据=<s:property value="#request.request_key"/> session作用域中的数据=<s:property value="#session.session_key"/> application作用域中的数据=<s:property value="#application.sevletContext_key"/> </pre> </body>
5.2.5 访问请求参数
- 请求参数存放在广义值栈组成部分之一的parameters中。
- struts封装的作用域,把reuqest、session、servletContext作用域封装成Map,
所以struts的OGNL表达式在获取请求参数值的时候是并不是调用request.getParameter()或getParameterValues方法 而是调用request.getParameterMap()方法。
要解决struts2中OGNL获取parameters中请求参数值乱码的问题,只要覆盖request.getParameterMap()方法既可。
- index.jsp(设置请求参数)
<body> <a href="<%=path%>/action/actioncontext?userId=001&userName=张三">OGNL访问请求参数</a> </body>
- struts.xml
<struts> <constant name="struts.enable.DynamicMethodInvocation" value="true" /> <constant name="struts.devMode" value="true" /> <package name="default" namespace="/action" extends="struts-default"> <action name="actioncontext" class="action.ActionContext_1"> <result name="actionContext">/actionContext.jsp</result> </action> </package> </struts>
- ActionContext_1.java
package action; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import actionUtil.BaseAction; import bean.UserBean; /** * @author 夏洛克的喵~ * */ public class ActionContext_1 extends BaseAction{ public String execute() { return "actionContext"; } }
-
actionContext.jsp(读取请求参数的值)
<body> <pre> <h3>用debug标签显示ActionContext结构</h3> <s:debug></s:debug> <h3>OGNL访问请求参数</h3> 请求参数中的为userId=<s:property value="#parameters.userId"/> 请求参数中的userName=<s:property value="#parameters.userName"/> </pre> </body>
- EncodingFilter.java(编码过滤器)
package filter; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; /** * 此过滤器用于解决get和post请求中问乱码的问题。 */ public class EncodingFilter implements Filter { public EncodingFilter() { } public void destroy() { } /** * 要解决乱码问题首先区别对待POST方法和GET方法, * 1.如果是POST方法,则用request.setCharacterEncoding("UTF-8"); 即可 * 2.如果是GET方法,则麻烦一些,需要用decorator设计模式包装request对象来解决 */ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request=(HttpServletRequest)req; HttpServletResponse response=(HttpServletResponse)res; //获取request请求是get还是post String method=request.getMethod(); if(method.equals("GET") || method.equals("get")){ //注意大小写都要判断,一般来说是大写的GET /** * request请求为get请求,则用包装类对request对象的getParameter方法进行覆盖。 */ response.setContentType("text/html;charset=UTF-8"); MyGetHttpServletRequestWrapper requestWrapper=new MyGetHttpServletRequestWrapper(request); chain.doFilter(requestWrapper, response); }else{ //post请求 response.setContentType("text/html;charset=UTF-8"); request.setCharacterEncoding("UTF-8"); chain.doFilter(request, response); } } public void init(FilterConfig fConfig) throws ServletException { } } class MyGetHttpServletRequestWrapper extends HttpServletRequestWrapper{ HttpServletRequest request; public MyGetHttpServletRequestWrapper(HttpServletRequest request) { super(request); this.request=request; } /** * servlet API中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper, * (HttpServletRequestWrapper类实现了request接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的 * request对象的对应方法) 以避免用户在对request对象进行增强时需要实现request接口中的所有方法。 * 所以当需要增强request对象时,只需要写一个类继承HttpServletRequestWrapper类,然后在重写需要增强的方法即可 * 具体步骤: *1.实现与被增强对象相同的接口 *2、定义一个变量记住被增强对象 *3、定义一个构造函数,接收被增强对象 4、覆盖需要增强的方法 5、对于不想增强的方法,直接调用被增强对象(目标对象)的方法 */ @Override public String getParameter(String name) { String old_value=super.getParameter(name); String new_value=null; if(old_value!=null && !old_value.equals("")){ try { new_value=new String(old_value.getBytes("ISO-8859-1"),"UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return new_value; } @Override public String[] getParameterValues(String name) { String[] old_value=request.getParameterValues(name); String[] new_value=new String[old_value.length]; if(old_value!=null && !old_value.equals("")){ String temp_value=null; for(int i=0;i<old_value.length;i++){ try { temp_value=new String(old_value[i].getBytes("ISO-8859-1"),"UTF-8"); new_value[i]=temp_value; } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } return new_value; } /** * 使用struts封装的作用域,把reuqest、session、servletContext作用域封装成Map, * 所以struts的OGNL表达式在获取请求参数值的时候是并不是调用request.getParameter()或getParameterValues方法 * 而是调用request.getParameterMap()方法。 * 要解决struts2中OGNL获取parameters中请求参数值乱码的问题,只要覆盖request.getParameterMap()方法既可。 */ @Override public Map getParameterMap() { try { if (!this.request.getMethod().equals("GET")) {// 判断是否是get请求方式,不是get请求则直接返回 return this.request.getParameterMap(); } Map<String, String[]> map = this.request.getParameterMap(); // 接受客户端的数据 Map<String, String[]> newmap = new HashMap(); for (Map.Entry<String, String[]> entry : map.entrySet()) { String name = entry.getKey(); String values[] = entry.getValue(); if (values == null) { newmap.put(name, new String[] {}); continue; } String newvalues[] = new String[values.length]; for (int i = 0; i < values.length; i++) { String value = values[i]; value = new String(value.getBytes("iso8859-1"), this.request.getCharacterEncoding()); newvalues[i] = value; // 解决乱码后封装到Map中 } newmap.put(name, newvalues); } return newmap; } catch (Exception e) { throw new RuntimeException(e); } } }
结果:
- 注意: 虽然OGNL表达式取值并非通过request.getParameter()或者getParameterValues()方法,但是El表达式确实通过这两种方法取值。
5.2.6 访问valueStack广义值栈和操作valueStack广义值栈
5.2.6.1
-
读取、修改狭义值栈中的值。
- 要修改狭义值栈中的值,首先要获得ValueStack狭义值栈对象,这个对象封装了一系列的方法可以来操作这个狭义值栈。 方法如下:
A、 ValueStack.set() //新增狭义值栈中的属性与值并产生一个新的Object为java.util.HashMap
B、 ValueStack.setValue() //修改狭义值栈中的值。
C、 valueStack.push() //往栈顶压入一个对象,这里的对象为java.util.LinkedHashMap
- index.jsp
<body> <a href="<%=path%>/action/actioncontext">OGNL访问valueStack狭义值栈和操作valueStack狭义值栈</a> </body>
- struts.xml
<struts> <constant name="struts.i18n.encoding" value="UTF-8"></constant> <constant name="struts.multipart.maxSize" value="209715200"></constant> <constant name="struts.action.extension" value="action,,"></constant> <constant name="struts.enable.DynamicMethodInvocation" value="true" /> <constant name="struts.devMode" value="true" /> <constant name="struts.i18n.reload" value="true"></constant> <constant name="struts.ui.theme" value="xhtml" /> <constant name="struts.configuration.xml.reload" value="true"></constant> <constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant> <constant name="struts.handle.exception" value="true"></constant> <package name="default" namespace="/action" extends="struts-default"> <action name="actioncontext" class="action.ActionContext_1"> <result name="actionContext">/actionContext.jsp</result> </action> </package>
-
ActionContext_1.java
package action; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.util.ValueStack; import actionUtil.BaseAction; import bean.UserBean; /** * 访问狭义值栈与操作狭义值栈 * 步骤: * 获取ActionContext对象,ActionContext对象再获取ValueStack值栈。 * @author 夏洛克的喵~ */ public class ActionContext_1 extends BaseAction{ public String execute() { ActionContext actionContext=ActionContext.getContext(); ValueStack valueStack=actionContext.getValueStack(); valueStack.set("userName", "张三"); //新增狭义值栈中的属性与值,在狭义值栈中产生新的Object为java.util.HashMap valueStack.set("userName2", "傻逼"); valueStack.setValue("userName2", "你才是傻逼"); //修改狭义值栈中某一个属性的值 Map<String,String> m=new LinkedHashMap<String,String>(); m.put("map_key", "map_value"); valueStack.push(m); //往栈顶压入一个对象,这里的对象为java.util.LinkedHashMap return "actionContext"; } }
- actionContext.jsp
<body> <pre> <h3>用debug标签显示ActionContext结构</h3> <s:debug></s:debug> <h3>OGNL访问valueStack狭义值栈和操作valueStack狭义值栈</h3> userName=<s:property value="userName"/> 修改后的userName2=<s:property value="userName2"/> 栈顶元素=<s:property value="map_key"/> </pre> </body>
结果:
- 注意狭义值栈中的属性名可以重复,如果取值越靠近栈顶的被取到。
比如上图中的java.util.LinkedHashMap有一个属性为userName=“张三”而java.util.HashMap有一个属性为userName=“李四”,此时LinkedHashMap靠近栈顶,所以如果
<s:property value="userName"/> 取到的值为:张三 。栈的排序原理为:先进后出。 如果想要取到HashMap中的userName的值的话,可以用<s:property value="[1].userName"/>
[1].userName表示狭义值栈中的第二个对象的userName属性
- 值栈中最好不要出现属性名相同的属性。
5.2.6.2 访问非侠义值栈和操作非狭义值栈
- 操作非狭义值栈可以用ActionContext类的put()方法往非侠义值栈添加对象。
如:
ActionContext actionContext=ActionContext.getContext();
actionContext.put("userName", "libai");
获取这个对象:
<s:property value="#userName"/>