值栈详解(ValueStack)

来源:互联网 发布:js判断字符串不等于 编辑:程序博客网 时间:2024/04/28 12:23

什么是值栈

之前web阶段,在servlet里面进行操作,把数据放到域对象里面,在页面中使用el表达式获取到。域对象在一定范围内,存值和取值。

在struts2里面提供了本身的一种存储机制,类似于域对象,是值栈,可以存值和取值。

  • 在action里面把数据放到值栈里面,在页面中获取到值栈的数据。

注意:对于存值,你可以使用servlet中的三种域对象,使用这三种域对象都可以在jsp页面中获取到。同时你使用struts2里面的存储机制同样能够实现上面的效果,但是并不是一定要你使用struts2里面的存储方法。目的要让你知道struts2也能够办到同样存值取值的操作。只是你说会struts2 但是你不会值栈,这是说不过去的!!!

servlet和action的区别:

Servlet:

默认在第一次访问的时候创建,只创建一次,是一个单例对象!!

Action:

一样是访问的时候创建对象,每次访问action的时候都会创建新的action对象,创建多次,是一个多实例对象!!

值栈的存储位置:

  • 每次访问action的时候都会创建action对象。
  • 在每个action对象里面都会有一个值栈对象。(注意:每个action对象只有一个)

获取值栈

获取值栈对象有多种方式!

  • 常用的方式:使用ActionContext对象里面的方法(getValueStack())获取值栈对象。

目的验证在同一个action值栈只有一个:

public class pr_action{    public String execute(){        ActionContext context=ActionContext.getContext();        ValueStack vs1=context.getValueStack();        ValueStack vs2=context.getValueStack();        System.out.println(vs1==vs2);    }}

输出结果:

true

值栈内部结构

栈:先进后出!

最上面是栈顶的元素,向栈里面放数据的操作叫做压栈。

所以,你没有猜错,值栈也是用的这种数据结构!但是值栈分为两个部分,root和context,root就是栈的数据结构,同时context也是

root专业叫做ObjectStack(对象栈)
context专业叫做ContextMap(Map栈),Map类型的栈。(在我们访问里面的对象的时候,会通过出栈的方式取东西,效率比较低,一般我们不会用)

值栈分为两个部分:

  • 第一个部分 root ,平常我们用的值栈就是操作root里面的内容,很少去操作context。结构是List集合。

可以通过ctrl+shift+t搜索类,来查看它的父类。

很惊奇你会发现root对象类型继承了ArrayList这个类

  • 第二部分 context ,结构是Map集合

下面来讲讲context这个对象:

context里面存储的都是些固定的值,有以下几个:

key————value

request—>最底层是request(HttpServletRequest),但是在这里的request是RequestMap类型的。如果你在HttpServletRequest类型中赋了值,那么在RequestMap中照样能够读取到。
session—>HttpSession对象引用
application—>ServletContext对象引用
parameters—>传递相关的参数
attr—>使用attr操作,能够获取域对象里面的值,获取域范围最小里面的值。

要想查看到值栈的结构可以用调试(debug)的方法!

通过struts2的标签<s:debug></s:debug>,我们可以很清楚的看到值栈确实分为两个部分,一个root,一个context,上面我们已经讲解了context的存储内容

,下面我们就来讲讲root的存储内容:

我们已经了解到root是一个栈的存储结构。下面是root默认存储的内容:

  • action对象引用

  • DefaultTextProvider

也就是说在root的栈顶还存储着action的引用,为什么会这个存储呢??

其实它只是为了能够在值栈里面取出action,能够在action里面取出值栈,仅此而已!

向值栈里面放数据

向值栈里面放数据时,其实存储的位置是在root域里面

向值栈放数据有多种方式,往往我们只用其中一种

  • 第一种 获取值栈对象,调用值栈对象里面的 set 方法
ValueStack stack=ActionContext.getContext().getValueStack();stack.set("username","FireLang");

在用set方法添加值栈数据之后,会在root栈顶多一个HashMap对象

  • 第二种 获取值栈对象,调用值栈对象里面的 push 方法

调用push之后,就会在root栈顶放入一个String类型的对象!

  • 第三种 在action定义变量,生成变量的get方法(主要)
public class pr_action{private String name;public String getName(){    return name;}public String execute(){    name="FireLang";    return "success";}}

在用第三种方法之后,struts2并不会在值栈root的栈顶放入新的对象,它的存储路径还是存储在action里面,所以这就起到了资源的合理应用,当想要获取name属性的时候,就会在值栈里面调用action的getName方法。这也就是为什么会在值栈里面存储action的原因了。

向值栈中放对象

实现步骤:

第一步:定义对象变量

第二步:生成变量的get方法

第三步:在执行的方法里面向对象中设置值

向值栈中放List对象

第一步:定义List集合变量

第二步:生成变量的get方法

第三步:在执行的方法里面向List集合设置值

action的代码:

public class Pr_fangList {    private List<User> lu;    public String execute(){        lu=new ArrayList<User>();        User u1=new User();        u1.setName("胡艺宝");        u1.setPassword("123");        lu.add(u1);        User u2=new User();        u2.setName("胡家源");        u2.setPassword("456");        lu.add(u2);        System.out.println(lu);        return "success";    }    public List<User> getLu() {        return lu;    }}

jsp中代码:

<!-- 文章截止到目前还没有说到el表达式为什么能够取到值栈里面的数据,在文章后续会解释的 -->${lu[0].name }${lu[0].password }<br><hr><br>${lu[1].name }${lu[1].password }<s:debug></s:debug>

为什么EL表达式能够获取值栈里面的数据

从值栈的root里面取数据

使用struts2的标签+ognl表达式获取值栈数据

  • <s:property value="ognl表达式"/>

获取字符串

步骤:

服务端代码:

//服务端代码是为了让字段被存入ValueStack中//字段的前提条件是必须设置get方法public class Pr_getString {    private String name;    public String getName() {        return name;    }    public String execute(){        name="胡艺宝";        return "success";    }}

客户端jsp代码:

<s:property value="name"/><!-- 这里的name是ognl表达式。表示获取action中的name字段值,必须要写get方法,因为字段读或者写的功能按照规定,都必须通过读或者写方法来给变量赋值 -->

获取对象

aciton中的代码:

//再次强调必须要get方法。public class Pr_getUserObj {    private User us;    public User getUs() {        return us;    }    public String execute(){        us=new User();        us.setName("胡艺宝");        us.setPassword("FireLang");        return "success";    }}

jsp中的代码:

<s:property value="us.name" /><br><!-- value中的值是ognl表达式 --><s:property value="us.password" /><!-- 获取到us对象后,再获取us中的name属性和password属性,再次强调获取字段基本上都是按照规定通过get和set方法进行操作! -->

获取List集合

通过ognl+struts标签获取List集合共有三种方式

服务端代码:

public class Pr_getList {    private List<User> usl=null;    public List<User> getUsl() {        return usl;    }    public String execute(){        usl=new ArrayList<User>();        User tempUser=new User("胡艺宝", "123465");        usl.add(tempUser);        return "success";    }}

第一种:

客户端代码:

//这种代码非常不好,在很多时候你永远不可能知道服务端传来的List里面到底有多少参数。<s:property value="usl[0].name"/><s:property value="usl[0].password"/>

第二种方式:

类似jstl中的foreach标签

<s:iterator value="usl">    <s:property value="name"/>    <s:property value="password"/>    <br><hr><br></s:iterator>

第三种方式:

/*第三种方式较第二种方式多加了一个var,根本区别就是iterator把遍历出来的值放进了值栈的第二部分空间context,contex因为是Map结构的所以要加上一个键作为取值方式,也就是var的值作为context的键,其实这种方式算是一种优化,不用在root中去拿值了。而第二种方式还会到root里面去拿值。速度没有在context中的快*/<s:iterator value="usl" var="singleus">    <s:property value="#singleus.name"/>    <s:property value="#singleus.password"/></s:iterator>

set方法和push方法的取值(会用)

set方法的取值

//服务端代码:public class Pr_getUserSet {    private User us;    public String execute(){        us=new User("胡艺宝","789");        ActionContext.getContext().getValueStack().set("us", us);        ActionContext.getContext().getValueStack().set("lang", "FireLang");;        return "success";    }}
<!-- 客户端代码: --><!-- 直接从root域里面取值 --><s:property value="us"/><br><br><s:property value="lang"/>
//运行结果:User [name=胡艺宝, password=789]FireLang

push方法取值

//服务器端代码://这里要注意的是push方法是直接把数据存放在root中的。不像set一样可以通过key来取值。//push的取值方法有点特殊,是通过直接把栈顶元素取出来的。public class Pr_getUserSet {    private User us;    public String execute(){        us=new User("胡艺宝","789");        ActionContext.getContext().getValueStack().push(us);        ActionContext.getContext().getValueStack().push("FireLang");        return "success";    }}
<!-- 客户端jsp代码 --><s:property value="[0].top"/>//取第一个<s:property value="[1].top"/>//取第二个,这里的top是root的域实体对象名称,也就是List对象的名称
运行结果:FireLang User [name=胡艺宝, password=789]

增强一个类常用的方式:装饰者模式,动态代理,继承

用EL表达式取值:

el取值能够获取到action里面的值,具体原理就是,它重写的request,进行了request域的增强,里面进行了以下操作:

el取值时,如果request域里面能够找到目标值,那么就把值返回到页面。如果在request域里面不能够取到目标值,那么就通过值栈获取。

ActionContext.getContext().getValueStack().findValue(“key”);如果查找到值就返回数据。这里的request是通过HttpServletRequestWrapper重写过。

所以在el表达式获取Action里面存取的值的时候效率没有通过Struts标签来的快。推荐用struts标签和ongl来获取Action里面的数据。

el在获取Action里面的值时,action里面的字段也必须提供get方法。否则无法获取到值。

//服务端代码:public class Pr_getList {    private List<User> usl=null;    public List<User> getUsl() {        return usl;    }    public String execute(){        usl=new ArrayList<User>();        User tempUser=new User("胡艺宝", "123465");        usl.add(tempUser);        return "success";    }}
<!-- 客户端jsp代码: --><!-- 在使用jstl标签以前要先导入jstl标签库,这里使用的jstl标签库是1.2版本 --><c:forEach items="${usl }" var="temp">        ${temp.name }<br>        ${temp.password }</c:forEach>
//这个是增强request的源码:ActionContext ctx = ActionContext.getContext();        Object attribute = super.getAttribute(key);        if (ctx != null && attribute == null) {            boolean alreadyIn = isTrue((Boolean) ctx.get(REQUEST_WRAPPER_GET_ATTRIBUTE));            // note: we don't let # come through or else a request for            // #attr.foo or #request.foo could cause an endless loop            if (!alreadyIn && !key.contains("#")) {                try {                    // If not found, then try the ValueStack                    ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.TRUE);                    ValueStack stack = ctx.getValueStack();                    if (stack != null) {                        attribute = stack.findValue(key);//这就是el能够获取到值栈里面的关键                    }                } finally {                    ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.FALSE);                }            }        }        return attribute;

ognl两个符号的使用#号和%的使用

使用#来获取context中的数据

//服务端的代码:public class Pr_getRequestByContext {    private User us;    public User getUs() {        return us;    }    public String execute(){        us=new User("胡艺宝", "123456789");        ((RequestMap)ActionContext.getContext().getValueStack().getContext().get("request")).put("user", us);//把数据保存到context的request中//其中你也可以通过ActionContext.getContext().get("request")获取到的效果和上面的一样。        System.out.println(ServletActionContext.getRequest().getAttribute("user"));//验证是否能够在HttpServletRequest中获取到user,事实证明能够获取到。猜测RequestMap的最底层依赖了HttpServletRequest        return "success";    }}
<!-- 这个是jsp客户端的代码 --><s:property value="#request.user"/>

运行后能够成功获取user数据

%号的使用

场景:在struts2表单标签里面使用ognl表达式,如果直接在struts2表单标签里面使用ognl表达式会不识别,只有使用%号之后才会识别。

//服务端代码就用上面那个
<!-- 客户端jsp代码,成功运行输出后的代码: --><s:textfield name="username" value="%{#request.user.name}"></s:textfield>

OK!!!完成!!

2 1
原创粉丝点击