值栈

值桟&OGNL


OGNL概述

  1. 之前web阶段,学习过EL表达式,EL 表达式在JSP中获取域对象里面的值,不能在HTML页面使用
  2. OGNL 是一种更强大的表达式
    1. 主要用途 在Struts 中,操作值桟数据
    2. 一般把OGNL在Struts2 操作,和Struts2标签一起使用
  3. OGNL不是Struts2的一部分,它是单独的一部分.

什么是OGNL

全称是 对象图导航语言 (Object-Graph Navigation Language),它是一种强大的开源表达式语言,使用这种表达式语言,可以通过某种语法,存取java对象的任意属性,调用属性的时候,可以自动实现类型的转换.

OGNL作用:

在这里插入图片描述

ognl 的三要素

  • 表达式

表达式是核心,OGNL会根据表达式去对象中取值,所有的OGNL操作都是针对表达式解析后进行的

  • 根对象 root

OGNL的操作对象。可以以任意一个对象为根,通过OGNL可以访问与这个对象有关联的其他对象

  • Context对象

实际上,OGNL的取值还需要一个上下文环境,设置了root对象,OGNL可以对root对象进行取值写值操作,上下文规定了OGNL的操作在哪里进行。上下文Context是一个Map类型的对象,表达式中,加上“#”加上对象名称

使用Struts2的标签

使用jstl的时候,导入jar包以后,在JSP页面中,引入标签库

使用Struts标签的时候,也要在JSP中 引入标签库

<%@ taglib uri="/struts-tags" prefix="s" %>

<s:property value=""/> //value写的是表达式

入门案例

<!--字符串的长度-->
<s:property value="'我的长度'.length()"/>

值桟

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

  • 在Struts中,提供了一种存储机制,类似于域对象,是值桟,可以存值和取值

    • 在action中,可以把数据放到值栈里面,在页面中获得值栈数据
  • servlet和action 的区别

    • servlet: 默认在第一次访问的时候创建,只创建一次(单实例对象)
    • Action: 访问的时候创建,每次访问的时候,都会创建action对象,创建多次(多实例对象)
    • 通过构造函数 输出内容,看看是否创建多次

什么是 值栈:

值栈 ValueStack 是Struts的一个接口,OgnlValueStack是它的实现类,客户端发起一个请求的时候,创建一个action,实例的同时,创建一个值栈实例,这个实例贯穿整个action的生命周期,Struts用OGNL将请求action的参数封装为对象,储存到值栈中,通过表达式,取其中的值

位置在action 中,有且只有一个


获取值栈对象:

  • ActionContext类里面的方法得到值栈对象
ActionContext context = ActionContext.getContext();
ValueStack stack = context.getValueStack();
  • 每个action中只有一个值栈对象,get 两个的话,两个地址也是相等的

值栈的内部结构

  • 值栈分为两个部分
    • 第一部分:root 结构为list结构 一般都是执行root结构
    • 第二部分:context 结构为Map集合

在这里插入图片描述
context 结构: 存储对象的引用

key 固定value
requestrequest对象的引用
SessionHttpSession对象的引用
applicationServletContext对象的引用
parameters传递的相关参数
attr获取三个域中相同key的最小域的值

向值栈中放标签

  • <s:debug> 访问action,执行action方法的返回值,配置返回值到JSP页面中,在JSP页面中 使用这个标签,开发时候应用

在页面中,会生成[debug]的链接。点进去后,会看到两部分内容

代码实现

通过JSP页面,发送action请求,访问OgnlAction的execute方法,然后跳转到ognl页面
通过页面的<s:debug></s:debug> 查看debug 信息(值栈对象)

<!-- jsp 代码-->
<s:debug></s:debug>
<!-- struts.xml 代码内容-->
<package name="ognl" extends="struts-default" namespace="/">
    <action name="debuga" class="cn.lenks.action.OgnlAction">
        <result>/jsp/ognl.jsp</result>
    </action>
</package>

I.Value Stack Contents — 我们一般用到的是这里面的内容(root部分,list集合)

Object 的名字为包名+action类名 ::空action的object(本次debug栈顶元素)具体内容如下:

Property NameProperty Value
containerThere is no read method for container
errorMessages[]
actionErrors[]
actionMessages[]
fieldErrors{}
textsnull
localezh_CN
errors{}

action对象里面有值栈对象

值栈对象里面有action对象的引用


II.Stack Context (context map集合)


向值栈中放数据的方式

1、获取值栈对象,调用对象的set方法

2、获取值栈对象,调用对象里面的push方法

3、在action里面定义变量,生成变量的get方法----(最常用的方法)

代码实现:

// 第三种方法,定义变量。生成get方法。在执行的方法里面为变量赋值。
private String name ;
public String getName() {
    return name;
}
@Override
public String execute() throws Exception {
    // 获取值栈对象里面的set方法
    // 获取值栈对象
    ActionContext context = ActionContext.getContext();
    ValueStack stack = context.getValueStack();
    // 页面显示 Object----- Property Name----- Property Value

    // 方法一:调用Set方法
    // 此方法是生成一个map对象,key为java.util.HashMap
    // 两次set只有一个map生成,对应的是: empty---null  ???原因待定
    stack.set("username", "lenks");
    stack.set("password", "124");
    // 方法二:调用push方法
    // 此方法,生成两个String对象,对应的内容为:bytes---[B@44324010
    //									  empty---false
    stack.push("kenksss");
    stack.push("kenk124");
    // 方法三:没有生成新的对象,只是在action对象里面多了内容: name---123
    name = "123";
    return SUCCESS;
}


向值栈中放入对象:

放入对象实现步骤:

  1. 定义或者声明对象变量
  2. 生成变量的get方法
  3. 在执行的方法里面对对象设定值
  4. 代码实现 同上。省略
  5. 效果:

同上面的数据一样

在action对象中,生成内容: 对象名字—对象.tostring()


放入list集合方法

  1. 定义或者声明list变量
  2. 生成变量的get方法
  3. 在执行的方法里面向list集合设置值
  4. 效果同上.生成内容:list集合名—[集合内对象,集合内对象,…]

从值栈中获取数据

a.获取字符串

  1. 在action中,里面,设置该变量的get方法。

  2. JSP标签:<s:property value="字符串名称"> 就是前面说的Property Value

b.获取对象

  1. 在action里面,设置对象的get方法
  2. JSP标签<s:property value="对象名.属性名">

c.获取list集合

I.第一种方法

  1. 在action里面,设置集合的get方法
  2. JSP标签<s:property value="集合名[0].属性名">
  3. 上述方法针对明确list集合的数量的时候使用

II.第二种方法

  1. 在action里面,设置集合的get方法
  2. JSP标签
<s:iterator value="list集合名字">
	<!-- 遍历list集合的对象-->
    <!--不同于foreach标签 c:foreach item="users" var="user" ... ${user.username}-->
	<s:property value="属性名"/>
	<s:property value="属性名"/>
</s:iterator>

这里注意:JSP中的注释里面如果有标签的话,那个标签不会被注释,容易报错。用<%----%>

III.第三种方法

  1. 在action里面,设置集合的get方法
  2. JSP标签
<s:iterator value="list集合名字" var="user">
	<!--
		遍历list,得到每个User对象后,
		机制:遍历得到的一个user 存到context中,key值为var的User名,value为引用
		作用:充分利用空间,提高速度和效率
		获取context的数据特点:写ognl表达式
		使用特殊符号#  表示是从context中读取
	-->
	<s:property value="#user.username"/>
	<s:property value="#user.username"/>
</s:iterator>

set和push方法放进去的值得取值方法

<s:property value="key名称">

因为push方法放数据,因为没有key-value 机制。只是放进去了值

只能通过这种方法取值.top语法。这里的[0,1,2,3,4] 代表的是从栈顶开始的第几层元素

<s:property value="[0].top"/>
<s:property value="[1].top"/>
<s:property value="[2].top."/>
<s:property value="[3].top"/>
<s:property value="[4].top"/>

很少用到

具体详解请参考Struts2相关书籍


使用el表达式和foreach标签获取list数据

先导入两个jar包 jstl.jar standard.jar

引入标签 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" perfix="c" %>

<c:foreach items="${list对象名,这里是users}" var="user">
	${user.username}
</c:foreach>

重点:为什么可以获取到

  1. el 表达式,获取域对象
  2. 向域对象放值,使用setAttribute()方法
  3. 底层增强request对象里面的getAttribute()方法
    • 首先从request域中获取值,如果获取到,直接返回
    • 如果获取不到值,把值栈中的数据获取出来,放到域对象中
  4. 不建议这么做。性能比较低

查看源代码

进入org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
在StrutsPrepareAndExecuteFilter中找到doFilter(ServletRequest req, ServletResponse res, FilterChain chain)方法
进入request = prepare.wrapRequest(request);
再进入
request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);
public class StrutsRequestWrapper extends HttpServletRequestWrapper
通过继承HttpServletRequestWrapper增强request方法
public Object getAttribute(String key) {
        if (key == null) {
            throw new NullPointerException("You must specify a key value");
        }

        if (disableRequestAttributeValueStackLookup || key.startsWith("javax.servlet")) {
            // don't bother with the standard javax.servlet attributes, we can short-circuit this
            // see WW-953 and the forums post linked in that issue for more info
            return super.getAttribute(key);
        }

        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);
                    }
                } finally {
                    ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.FALSE);
                }
            }
        }
        return attribute;
    }


# 和% 使用

#的使用

  • #可以获取context中的值

因为context中,有request,Session 等key名称,可以通过他们的引用,获取他们的对象

<s:property value="#request.放入域中的名称"/>

%的使用

场景

在Struts2标签中使用ognl表达式,如果直接在表达式中使用标签,不会识别,只有加了%才会识别

表单项使用时,想在输入窗口显示username的值得时候
<s:textfield name="username" value="%{#request.username}">
如果不加%{},会显示那个字符串

更新时间 2018.11.02

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值