Struts2框架自学之路——值栈

来源:互联网 发布:淘宝店组织架构 编辑:程序博客网 时间:2024/06/02 00:41

目录

  • 目录
  • Servlet和Action的区别
  • 什么是值栈
  • 获取值栈对象
  • 值栈的内部结构
  • 向值栈存放数据
    • 向值栈存放对象
    • 向值栈存放List集合
  • 从值栈获取数据
    • 获取字符串
    • 获取对象
    • 获取List集合
    • 其他操作
  • EL表达式获取值栈数据

Servlet和Action的区别

  在正式讲解值栈之前,我们先来了解下Servlet和Struts2中Action中的区别:

  • 对于Servlet而言,默认在第一次请求访问时被创建,创建成功后驻留在内存中,直到Web应用关闭。后续对该Servlet的请求,将都使用原先创建的Servlet对象进行处理。
  • 对于Action,则每次请求访问Action时,均会创建新的Action对象处理请求。

什么是值栈

   在Servlet中,我们使用域对象存放数据,然后在页面中使用EL表达式获取数据。在Struts2中同样支持这样的存取操作。同时,Struts2中还提供了另一种存储机制——值栈,类似于域对象,可以存值也可以取值。如在Action中我们可以数据存储到值栈中,在页面中我们可以获取到值栈中的数据。
值栈存储的位置
  每次请求访问Action时,均会创建新的Action对象处理请求。此时,在每个Action对象中都有且只有一个值栈对象。

获取值栈对象

  获取值栈对象有多种方式,常用方式是通过调用ActionContext对象的getValueStack方法获取值栈对象。如:

// 1. 获取ActionContext对象ActionContext context = ActionContext.getContext();// 2. 调用ActionContext对象的方法获取值栈对象ValueStack valueStack = context.getValueStack();

  在每个Action对象中只有一个值栈对象。如:

ActionContext context = ActionContext.getContext();ValueStack valueStack1 = context.getValueStack();ValueStack valueStack2 = context.getValueStack();System.out.println(valueStack1 == valueStack2); // 同一值栈对象

值栈的内部结构

debug标签的使用
  使用struts2中的标签debug,我们可以查看值栈结构和存储值。使用如下:
  通过访问Action,执行Action中的方法,返回结果,匹配返回值对应转发的JSP页面,在该JSP页面中使用该标签。(为什么不“将该标签放置为JSP页面后直接访问JSP页面”呢?因为值栈对象存在于Action中,访问Action同时也是创建Action的过程,Action对象将拥有一个值栈对象。)
  VSDebugAction.java

package com.wm103.action;import com.opensymphony.xwork2.ActionSupport;public class VSDebugAction extends ActionSupport {    @Override    public String execute() throws Exception {        return SUCCESS;    }}

  在struts.xml中的配置:

<package name="demo1" extends="struts-default" namespace="/">    <action name="debug" class="com.wm103.action.VSDebugAction">        <result name="success">/debug.jsp</result>    </action></package>

  debug.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %><%--导入struts标签库--%><%@ taglib uri="/struts-tags" prefix="s"%><html><head>    <title>debug标签的使用</title></head><body>    <%--使用struts2标签查看值栈结构--%>    <s:debug></s:debug></body></html>

  将应用部署到服务器后访问VSDebugAction,显示debug.jsp页面,如下:

使用debug标签查看值栈结构和存储值

值栈内部结构
  值栈分为两部分:
  第一部分为root部分,以List集合方式存储数据(我们一般也是操作root这一部分的数据),如:
  
值栈中的root部分

  第二部分为context部分,以Map集合方式存储数据。如:
  
值栈中的context部分
  
  注:我们会发现值栈的root部分:在Action没有进行任何操作时,root部分栈顶为Action对象的引用。我们可以知道Action对象中有值栈对象,同时值栈对象中也存放着Action对象的应用。

向值栈存放数据

  在Struts2中,向值栈中存放数据有多种方式:
(1)获取值栈对象,调用值栈对象的set方法存放数据。如:

ActionContext context = ActionContext.getContext();ValueStack valueStack = context.getValueStack();valueStack.set("vs-key", "vs-value");

  debug标签显示调用set方法存放数据后值栈的结构如下(数据被存到Map集合中,最后压入值栈中,如果后续还有调用set方法存值的话,将用刚保存数据的Map集合再次保存数据):

set方法存放数据到值栈

(2)获取值栈对象,调用值栈对象的push方法存放数据。如:

ActionContext context = ActionContext.getContext();ValueStack valueStack = context.getValueStack();valueStack.push("abcd");

  debug标签显示调用push方法存放数据后值栈的结构如下:

push方法存放数据到值栈

(3)【常用方式!】在Action定义变量,生成变量的get方法。如:
  这里定义了一个username变量。

package com.wm103.action;import com.opensymphony.xwork2.ActionContext;import com.opensymphony.xwork2.ActionSupport;import com.opensymphony.xwork2.util.ValueStack;/** * Created by DreamBoy on 2017/5/27. */public class ValueStackAction extends ActionSupport {    private String username;    public String getUsername() {        return username;    }    @Override    public String execute() throws Exception {        // 第一种 调用值栈的set方法存放数据        ActionContext context = ActionContext.getContext();        ValueStack valueStack = context.getValueStack();        valueStack.set("vs-key", "vs-value");        // 第二种 调用值栈的push方法存放数据        valueStack.push("abcd");        // 第三种 在Action定义变量,生成变量的get方法        this.username = "DreamBoy";        return SUCCESS;    }}

  debug标签显示存放数据后值栈的结构如下:
  
定义成员变量存放到值栈中

  可以发现数据以Action成员变量的形式存入值栈中。

向值栈存放对象

  实现步骤:
(1)定义对象变量(可以初始化);
(2)生成变量的get方法;
(3)在Action执行方法中给对象设置值。
  案例如下:
  ObjectAction.java

package com.wm103.action;import com.opensymphony.xwork2.ActionSupport;import com.wm103.entity.User;public class ObjectAction extends ActionSupport {    private User user = new User();    public User getUser() {        return user;    }    @Override    public String execute() throws Exception {        this.user.setUsername("DreamBoy");        this.user.setPassword("123456");        return SUCCESS;    }}

向值栈存放List集合

  实现步骤:
(1)定义List集合变量(可以初始化);
(2)生成变量的get方法;
(3)在Action执行方法中给List集合设置值。
  案例如下:
  ListAction.java

package com.wm103.action;import com.opensymphony.xwork2.ActionSupport;import com.wm103.entity.User;import java.util.ArrayList;import java.util.List;public class ListAction extends ActionSupport {    private List<User> list = new ArrayList<>();    public List<User> getList() {        return list;    }    @Override    public String execute() throws Exception {        User user1 = new User();        user1.setUsername("DreamBoy");        user1.setPassword("123456");        User user2 = new User();        user2.setUsername("DreamBoy223333");        user2.setPassword("223333");        list.add(user1);        list.add(user2);        return SUCCESS;    }}

从值栈获取数据

  使用struts2的标签property和OGNL表达式获取值栈中的数据:<s:property value="OGNL表达式"/>
  以下案例的Action类:
  VSGetDataAction.java

package com.wm103.action;import com.opensymphony.xwork2.ActionSupport;public class VSGetDataAction extends ActionSupport {    // 在这里定义存入值栈中的变量    // 对应的get方法    @Override    public String execute() throws Exception {        // 在这里设置变量的值        return SUCCESS;    }}

  在struts.xml中Action的配置:

<package name="demo1" extends="struts-default" namespace="/">    <action name="vsGetData" class="com.wm103.action.VSGetDataAction">        <result name="success">/data.jsp</result>    </action></package>

  data.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %><%--导入struts标签库--%><%@ taglib uri="/struts-tags" prefix="s"%><html><head>    <title>从值栈中获取数据</title></head><body>    <%--在这里写从值栈中获取数据的案例--%></body></html>

获取字符串

  在VSGetDataAction中定义字符串并存入值栈,如下:

    private String username;    public String getUsername() {        return username;    }    @Override    public String execute() throws Exception {        this.username = "Hello Struts2!";        return SUCCESS;    }

  在data.jsp中获取值栈中的数据:

<s:property value="username"/>

获取对象

  在VSGetDataAction中定义对象并存入值栈,如下:

    private User user = new User();    public User getUser() {        return user;    }    @Override    public String execute() throws Exception {        this.user.setUsername("DreamBoy");        this.user.setPassword("123456");        return SUCCESS;    }

  在data.jsp中获取值栈中的数据:

<s:property value="user.username"/><s:property value="user.password"/>

获取List集合

  在VSGetDataAction中定义List集合对象并存入值栈,如下:

    private List<User> list = new ArrayList<>();    public List<User> getList() {        return list;    }    @Override    public String execute() throws Exception {        User user1 = new User();        user1.setUsername("DreamBoy");        user1.setPassword("123456");        User user2 = new User();        user2.setUsername("DreamBoy223333");        user2.setPassword("223333");        list.add(user1);        list.add(user2);        return SUCCESS;    }

  在data.jsp中获取值栈中的数据:

<%--从值栈中获取List集合数据--%><!--第一种方式--><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/><!--第二种方式--><%--使用struts2标签中类似jstl的forEach标签的 iterator 标签,用来遍历值栈中的List集合--%><s:iterator value="list">    <%--遍历List得到List中每个User对象--%>    <s:property value="username"/> <s:property value="password"/><br/></s:iterator><br/><!--第三种方式--><s:iterator value="list" var="user">    <%--        遍历值栈中的List集合对象,得到每个user对象。每次遍历出来的user对象会被放到值栈中的context部分进行存储。        根据context部分数据的特点,需要写OGNL表达式,使用特殊符号#    --%>    <s:property value="#user.username"/> <s:property value="#user.password"/><br/></s:iterator>

其他操作

(1)调用值栈的set方法向值栈中存数据后从值栈中获取,如:
  向值栈中存数据

ActionContext context = ActionContext.getContext();ValueStack valueStack = context.getValueStack();valueStack.set("setKey", "setValue");

  从值栈中取数据

<%--调用值栈的set方法向值栈中存数据后从值栈中获取--%><s:property value="setKey"/>

(2)调用值栈的push方法向值栈中存数据后从值栈中获取,如:
  向值栈中存数据

ActionContext context = ActionContext.getContext();ValueStack valueStack = context.getValueStack();valueStack.push("pushValue");

  从值栈中取数据

<%--调用值栈的push方法向值栈中存数据后从值栈中获取--%><%--**注:向值栈中放入数据后,值栈会放数据存放到一个数组中去,并把数组名称为top,可以数组的名称获取值。**--%><s:property value="[0].top"/>

EL表达式获取值栈数据

  这里以上述中提及的从值栈中获取List集合数据为例,使用JSTL标签和EL表达式的方式中值栈中获取数据,如:

    <!--使用EL表达式获取-->    <c:forEach items="${list}" var="user">        ${user.username} ${user.password}<br/>    </c:forEach>

  这我就好奇了?EL表达式不是从域对象获取到数据吗?它居然可以从值栈中获取到数据?
  需要明确的是,EL表达式确实是从域对象中获取值的,且是通过调用域对象的getAttribute方法获取到值的。那么它为什么能从值栈中获取值呢?
  原因是struts底层增强了request对象中的getAttribute方法:从request域对象获取值时,调用了原来的getAttribute方法,如果获取到则直接返回;如果获取不到则从值栈中把值取出来(ValueStack的findValue方法获取值),再返回结果值。
  那struts底层在哪里增强request对象的getAttribute方法呢?在struts2的过滤器 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilterdoFilter方法中开始增强的操作。
  

原创粉丝点击