(七)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中的数据
  1. <s:property value="#request.name"/>  
  2. <s:property value="#session.name"/>  
  3. <s:property value="#application.name"/>  
  4. <s:property value="#attr.name"/>  
  5. <s:property value="#parameters.id"/>  
  6. <s:property value="#parameters.name"/>  
  • 构建一个map集合
  1. <%-- list --%>  
  2. <s:radio name="sex" list="{'男','女'}"></s:radio>  
  3. <%-- map --%>  
  4. <s:radio name="sex" list="#{'0':'男','1':'女'}"></s:radio>  

% 符号的用法
  • 强制字符串解析成OGNL表达式。
例如:在request域中存入值,然后在文本框(<s:textfield>)中取值,写在value里。
  1. <s:textfield value="%{#request.msg}"/>  

 

  • { }中值用引号引起来,此时不再是ognl表达式,而是普通的字符串,到底使用单引号还是双引号是由外层引号决定的。
  1. <s:property value="%{'#request.msg'}"/>  

$ 符号的用法

     在配置文件中可以使用OGNL表达式,例如:文件下载的配置文件。EL表达式也是使用$

    1. <action name="download1" class="cn.itcast.demo2.DownloadAction">  
    2.      <result name="success" type="stream">  
    3.           <param name="contentType">${contentType}</param>  
    4.           <param name="contentDisposition">attachment;filename=${downFilename}</param>  
    5.      </result>  
    6. </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"/>

 

posted @ 2017-03-16 16:09  shyroke、  阅读(1404)  评论(0编辑  收藏  举报
作者:shyroke 博客地址:http://www.cnblogs.com/shyroke/ 转载注明来源~