Struts2学习笔记__值栈存值取值

介绍下servlet和action的区别

  1. Servlet:默认在第一次访问的时候创建对象,但是它只会创建一次对象,无论后面访问多少次这个servlet,都只有一开始创建的那一个servlet对象,因此,我们说Servlet是单对象的。
  2. Action:也是默认第一次访问的时候创建,但是每一次访问它的时候,都会创建一个新对象,因此,我们说Action是多对象的。

什么是值栈

ValueStack是Struts的一个接口,字面意义为值栈,OgnlValueStack是ValueStack的实现类。客户端发出一个请求,struts2框架会为我们创建一个action,同时创建一个OgnlValueStack的实例,我们可以在Action中将数据封装到OgnlValueStack中,在页面中通过Ognl表达式或者EL表达式将其取出。

值栈的存储结构:

临时对象。该对象是在程序执行过程中,由容器自动创建并存储到值栈中。该对象的值并不固定,会随着应用不同而发生变化,当应用结束的时候,对象会被清空。(struts2标签输出迭代的值的时候,这些值都将以临时对象的形式放到值栈中)

模型对象。在action使用模型驱动方式传值的时候被用到。当jsp页面需要用到这些对象所携带的数据时,也会到值栈中去找对应的模型对象,获取数值。

Action对象:当每个action请求到来时候,容器会先创建一个此action的对象并存入值栈中,该对象携带所有与action执行过程有关的信息。

命名对象:主要包括servlet作用范围内相关的对象信息,比如request,session,application。

值栈中对象的遍历顺序

   栈顶--临时对象--模型对象--action对象--命名对象

在Action中获取值栈对象的方法:

获取值栈对象的方法很多,最常用的是使用ActionContext类来获取:
//1、获取ActionContext类的对象
ActionContext context = ActionContext.getContext();

//2、调用ActionContext中的方法获取值栈对象
ValueStack stack = context.getValueStack();

值栈的内部结构:

值栈分为两个部分:root和context
其中,root部分是list结构,context部分是map结构
我们存值和取值一般都是操作root部分的数据,而context部分存储的是一些对象的引用,如图:
在这里插入图片描述

root部分的结构如下:

在这里插入图片描述

context部分

在这里插入图片描述

可以明显的看出root部分的结构是一个list集和
这里需要注意的是:在action没有做任何操作的情况下,栈顶元素是 action引用
因为action对象里面有值栈对象, 值栈对象里面有action引用。这样的设计方式有利于action对象和值栈对象的互相调用。

值栈就是struts2为我们提供的一套类似于域对象的用来存值和取值的机制,自此我们在struts2框架中传递数据就有了两种方式:值栈和域对象。这两种方式都能实现功能,没有孰高孰低,根据我们的业务需求灵活使用即可。

如何在值栈中存储值

1. 使用值栈对象中的set方法
  1. 在action中获取值栈对象
  2. 调用值栈对象的set方法存值
public class ValueStackDemoAction extends ActionSupport {
     
    public String execute() throws Exception {
        //1 获取值栈对象
        ActionContext context = ActionContext.getContext();
        ValueStack stack = context.getValueStack();

        //2 调用值栈对象中的set方法
        stack.set("demo", "DemoData");      
        return "success";
    }
}

set方法使用map方式储存值,第一个元素为String类型的key,第二个元素为Object类型的value,value中可以存放任意对象。

使用set方法向值栈中存值后,我们用debug标签来查看一下值栈的结构,如图:
在这里插入图片描述
如图我们清晰的看到,set方式是使用HashMap的方式将值存入到值栈中,然后取值的时候更具Map中的key值即可将其value取出。

使用值栈对象中的push方法

  1. 在action中获取值栈对象

  2. 调用值栈对象的push方法存值
    public class ValueStackDemoAction extends ActionSupport {

    public String execute() throws Exception {
    //1 获取值栈对象
    ActionContext context = ActionContext.getContext();
    ValueStack stack = context.getValueStack();

     //2 调用值栈对象中的push方法
     stack.push("abcd"); 
     return "success";
    

    }
    }
    push方式是自动将所存的数据转为对应的对象类型,然后放入值栈中,如图
    在这里插入图片描述

在action中定义变量(或对象),使用相应的get方法(最常用,最重要的方法)

  1. 在action定义变量
  2. 生成变量的get方法
  3. 在执行方法里面为变量赋值
public class ValueStackDemoAction extends ActionSupport {
    
    //1. 声明变量
    public String name;

    //2. 生成变量的get方法
    public String getName(){
        return name;
    }

    //3. 在执行方法中为变量赋值
    public String execute() throws Exception {
        name = "DemoData";
        return "success";
    }
}

在这里插入图片描述

可以看出这种方式存值不会在值栈中创建新的对象,而是直接将值存进了值栈中原有的action对象中,这种方式的好处就是避免了值栈中存储空间的浪费,不用为每个值都分别设置存储空间。因此这种方式更加常用。

jsp页面中获取值栈中的数据

1. 获取使用set方法存入值栈中的值

使用set方法向值栈中存值,使用的是map方式,那么就有key和value,取值时我们只需根据key来取就可以了。
代码演示如下:

首先我们使用set方法向值栈中存入一个值,关键代码:
//1 获取值栈对象
ActionContext context = ActionContext.getContext();
ValueStack stack = context.getValueStack();

//2 调用值栈对象中的set方法
stack.set("demo", "DemoData");        
return "success";

然后我们在jsp页面中使用struts标签获取值栈中的这条数据,关键代码:
<!--导入struts标签库-->
<%@ taglib uri="/struts-tags" prefix="s"%>

<!-- 获取set方法设置的值  根据名称获取值-->
<s:property value="demo"/>

2. 获取使用push方式存入值栈中的值

push方式存值是没有map结构的,那么没有key,我们如何取值呢?事实上struts2中将push方法存入值栈中的值都放在一个名为top的集合中,那么我们只需利用这个集合便可获取到其中的值
代码示例:

首先我们还是先用push方法将数据放入到值栈中去,关键代码:
 //1 获取值栈对象
ActionContext context = ActionContext.getContext();
ValueStack stack = context.getValueStack();

//2 调用值栈对象中的push方法
stack.push("abcd");    
return "success";

然后在jsp页面中获取值栈中的值,关键代码:
<!--导入struts标签库-->
<%@ taglib uri="/struts-tags" prefix="s"%>

<!-- 获取push方法设置的值 -->
<s:property value="[0].top"/>

这里需要特别注意jsp页面中使用ognl表达式获取list集合中元素的写法,不是 top[0] 而是 [0].top ,和我们在java代码中取list集合元素的写法是有区别的。

3. 获取使用变量的get方法存入值栈中的值

这是往值栈中存值最常用的方法,这里我们将其分为取字符串,取对象和取list集合三种方式分别演示。

1 获取get方式存入值栈中的字符串
首先我们先使用变量方法存入一个字符串到值栈中,关键代码:
private String username;
public String getUsername(){
  return username;
}

public String execute() throws Exception {
  username = "demo";
  retrun "success";
}

在这里插入图片描述

然后我们在jsp页面中获取这个字符串的值,关键代码:

<!--导入struts标签库-->
<%@ taglib uri="/struts-tags" prefix="s"%>

<!--使用s标签获取username的值-->
<s:property value="username" />

2 获取get方式存入值栈中的对象

首先将对象存入到值栈中去,关键代码:
private User user = new User();
public User getUser(){
  return user;
}

public String execute() throws Exception {
  user.setUsername("demo");
  user.setPassword(123456);
  user.setAddress("beijing");

  return "success";
}

在这里插入图片描述
然后在jsp页面中获取这个对象,关键代码:

<!--导入struts标签库-->
<%@ taglib uri="/struts-tags" prefix="s"%>

<!--使用struts2标签获取对象的数据-->
<s:property value="user.username"/>
<s:property value="user.password"/>
<s:property value="user.address"/>

3 获取get方式存入值栈中的list集合
首先将list集合存入值栈,关键代码:

private List<User> list = new ArrayList<User>();
public List<User> getList() {
    return list;
}

public String execute() throws Exception {
    User user1 = new User();
    user1.setUsername("小奥");
    user1.setPassword("123");
    user1.setAddress("美国");
    
    User user2 = new User();
    user2.setUsername("小王");
    user2.setPassword("250");
    user2.setAddress("越南");
    
    list.add(user1);
    list.add(user2);
    
    return "success";
}

在这里插入图片描述
然后在jsp页面中获取list中的数据,在jsp中获取值栈中的list有三种方式,下面我们分别演示,关键代码:

    <!--导入struts标签库-->
        <%@ taglib uri="/struts-tags" prefix="s"%>

<! 获取值栈list集合数据 -->

获取list的值第一种方式:

    <br/>
<s:property value="list[0].username"/>
<s:property value="list[0].password"/>
<s:property value="list[0].address"/>
<br/>
<s:property value="list[1].username"/>
<s:property value="list[1].password"/>
<s:property value="list[1].address"/>

获取list的值第二种方式:

<br/>
<!-- 使用struts2标签 类似jstl的foreach标签
    s:iterator:遍历值栈的list集合
 -->
 <s:iterator value="list">
    <!-- 遍历list得到list里面每个user对象 -->
    <s:property value="username"/>
    <s:property value="password"/>
    <s:property value="address"/>
    <br/>
 </s:iterator>
    获取list的值第三种方式:
    <br/>
<s:iterator value="list" var="user">
    <!-- 
        遍历值栈list集合,得到每个user对象
        机制: 把每次遍历出来的user对象放到 context里面
        获取context里面数据特点:写ognl表达式,需要
        使用特殊符号 #
     -->
    <s:property value="#user.username"/>
    <s:property value="#user.password"/>
    <s:property value="#user.address"/>
    <br/>
</s:iterator>

第三种方法需要注意的地方在:在s:iterator 标签时如果使用了var属性,那么struts2会在值栈的context部分新开辟一个临时空间,并将遍历出来的list集合中的元素放到这个临时空间中,那么这时候再使用ognl表达式获取数据要使用#号,否则无法取出数据。
这样可以避免浪费root部分的空间,加快检索的速度,因为我们通常操作数据都是操作root部分。

参考:https://www.cnblogs.com/keyi/p/6230242.html

如何得到值栈:

在自定义的拦截器中,使用ActionInvocation.getStack()方法( ActionInvocation 是拦截器的方法参数)。

在Action类中,让拦截器注入ValueStack或者使用ActionContext.getContext().getValueStack()来值栈(ActionContext.getContext()为静态方法)。注意:ActionContext分配context的方式是基于线程的,如果使用这种方法,请确保它不会出错。

在JSP中,直接使用标签即可获得值栈里的数据,而一般不用获取值栈本身。

如何将对象存入值栈:

Struts2自动存入Action:之前已经提到,Struts2在执行一次请求的过程中会把当前的Action对象自动存入值栈中。

ModelDrivenInterceptor会存入Action的model属性:如果你使用了Struts2提供的 ModelDrivenInterceptor,则它会把Action对象的getModel()方法得到的对象存入值栈中。此时,值栈最底层为Action类,其次为这个model。

在自定义的拦截器中存入值栈:得到值栈对象后调用ValueStack.put(Object object)方法。

在Action类中存入值栈:得到值栈对象后调用ValueStack.put(Object object)方法。

在JSP中存入值栈:标签<s:push value="…"></s:push>是专门用来在JSP中把指定的value放入值栈的,但value被放入值栈的时间仅在s:push标签内,即程序运行到</s:push>标签处会把value从值栈中移出。另外,还有一些标签比如<s:iterator/>由于其功能的需要也会把一些对象放到值栈中。

让值栈执行表达式来获得值:

在自定义的拦截器中,获得值栈后,使用ValueStack.findValue(…)等方法。

在Action类中,获得值栈后,使用ValueStack.findVlaue(…)等方法。

在JSP中,一些标签的属性是直接在值栈上执行Ognl表达式的,比如<s:property/>的value属性。如果标签的属性不是直接执行Ognl表达式的,则需要使用“%{}”将表达式括起来,这样Struts2就会以Ognl表达式来执行了。至于到底哪些标签是直接执行Ognl而哪些不是,请参考完整的官方文档。

在JSP中跳过栈顶元素直接访问第二层:

在JSP中,使用[0]、[1]等表达式来指定从栈的第几层开始执行表达式。[0]表示从栈顶开始,[1]表示从栈的第二层开始。比如表达式“name”等价于“[0].name”。参见此处。

在JSP中访问值栈对象本身(而不是它们的属性)

在表示式中使用top关键字来访问对象本身。比如,表达式“name”等价于“top.name”,表达式“[0].top”等价于“top”,表达式“[1].top.name”等价于“[1].name”。

总之,值栈主要目的是为了让JSP内能方便的访问Action的属性。

ValueStack与ActionContext的联系和区别:

相同点:它们都是在一次HTTP请求的范围内使用的,即它们的生命周期都是一次请求。
不同点:值栈是栈的结构,ActionContext是映射(Map)的结构。

联系:ValueStack.getContext()方法得到的Map其实就是ActionContext的Map。查看Struts2的源代码可知(Struts2.3.1.2的org.apache.struts2.dispatcher.ng.PrepareOperations的第79行,createActionContext方法),在创建ActionContext时,就是把ValueStack.getContext()作为ActionContext的构造函数的参数。所以,ValueStack和ActionContext本质上可以互相获得。

注意:在一些文档中,会出现把对象存入“stack’s context”的字样,其实就是把值存入了ActionContext。所以在阅读这些文档时,要看清楚,到底是放入了栈结构(即值栈),还是映射结构(值栈的context,即ActionContext)。

首先给出三者的定义

  1. valueStack: 里面存放的是Action类中通过set方法设置的属性值(表单传过来的值等),由OGNL框架实现;
  2. stackContext: 也是用来存值的,stack上下文,它包含一些列对象,包括request/session/attr/application map等。
  3. actionContext: 是action的上下文,可以得到request,session,application等.
       我们在JSP页面中访问value stack的内容时,是不用加#,而如果是访问stack context的其他对象则要加上#。

如何获得ActionContext:

在自定义的拦截器中:使用ActionInvocation.getInvocationContext()或者使用ActionContext.getContext()。

在Action类中:让拦截器注入或者使用ActionContext.getContext()。

在非Action类中:让Action类传递参数、使用注入机制注入或者使用ActionContext.getContext()。注意:只有运行在request线程中的代码才能调用ActionContext.getContext(),否则返回的是null。

在JSP中:一般不需要获得ActionContext本身。

如何向ActionContext中存入值:

在拦截器、Action类、非Action类等Java类中:使用ActionContext.put(Object key, Object value)方法。

在JSP中:标签<s:set value="…"/>默认将值存入ActionContext中(当然,<s:set>标签还可以把值存到其他地方)。另外,许多标签都有var属性(以前用的是id属性,现在id属性已被弃用),这个属性能向ActionContext存入值,key为var属性的值,value为标签的value属性的值。(有些文档写的是向ValueStack的context存入值,其实是一样的)

如何从ActionContext中读取值:

在拦截器、Action类、非Action类等Java类中:使用ActionContext.get(Object key)方法。

在JSP中:使用#开头的Ognl表达式,比如<s:property value="#name"/>会调用ActionContext.get(“name”)方法。注意:如果某标签的属性默认不作为Ognl表达式解析,则需要使用%{}把表达式括起来,于是就会出现类似“%{#name}的表达式”。(“#”的更多用途参见这里)

总之,在JSP中使用ActionContext一方面是由于它是映射结构,另一方面是能读取Action的一些配置。当你需要为许多Action提供通用的值的话,可以让每个Action都提供getXXX()方法,但更好的方法是在拦截器或JSP模板中把这些通用的值存放到ActionContext中(因为拦截器或JSP模板往往通用于多个Action)。

猜你喜欢

转载自blog.csdn.net/qq_40803626/article/details/88757564