Struts2框架自学之路——值栈

目录

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方法中开始增强的操作。
  

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值