前言:
值栈的解析:
前面学到取web作用域对象:request,session,servletcontext,然后通过setAttribute()和getAttribute()存入或取出作用域中的数据。
那Struts2也提供了一套可以存放数据的地方:值栈,我们可以将数据存放在值栈上进行使用。ognl是针对值栈做些操作。
一、 ognl
1.ognl概述
在web阶段学过el表达式,用于jsp中获取域对象里面的值。
ognl也是一种表达式,功能强大。
(1)在struts2里面操作值栈数据 ValueStack
(2)一般把ognl在struts2操作,和struts标签一起操作值栈。
ognl不是struts2的一部分,单独的项目,只是和struts2一起使用。
(1)使用ognl时,先导jar包:ognl-3.0.6.jar
入门案例:
a. 引入struts2的标签库:和jsp中引用jstl差不多
<%@ taglib uri = "/struts-tags" prefix="s" %>
b. 使用struts2的标签
ognl与struts2中的<property>标签配合使用。
<s:property value="'haha'.length()" /> //可以调方法,测试'haha'的长度
2.OGNL的#、%的使用:
1. #使用
使用#来获取context里面的数据
<s:property value="#request.req" />
//格式:#context的key名称.域对象名称
2. %使用
在struts2标签中表单标签
(1)在struts2标签里使用ognl表达式,如果直接在struts2表单标签里面使用ognl表达式不识别,要加%
<s:textfield name="username" value="#request.req"></s:textfield>
运行后可以看它的源代码:即多了个<tr></tr>
value="#request.req"这样写,它不认识#request.req这种ognl表达式的写法,只有加上%才能识别
<s:textfield name="username" value="%{#request.req}"></s:textfield>
----------------------------------------------------------
二、值栈 ValueStack
2.1.值栈是干什么的?
存数据的,用在struts2里
相当于servlet中的域对象(page,request,session,application)
2. 2. servlet和action的区别(面试题):
servlet:单对象(单例模式)
默认在第一次访问时创建,创建一次;(生命周期)--------单实例对象
action:多实例对象
访问时创建,每次访问action时,都会创建action对象,每次访问都是不同对象。-------多实例对象
可通过构造方法输出测试。
2. 3.值栈存储位置:(每个对象中)
(1)每次访问action时,都会创建action对象
(2)在每个action对象里面都会有一个值栈对象。(也就是说实例化一个action对象,就会配一个值栈,没有第二个哦)
2.4.获取值栈对象
有多种方式获取,其中常用就两种:
第一种:使用ActionContext类中的方法获得值栈对象。
public class OneAction extends ActionSupport{
public String execute(){
ActionContext ac = ActionContext.getContext();
ValueStack stack1 = ac.getValueStack(); //获得值栈 Stack值栈
//可以验证一个action对象中只有一个值栈
ValueStack stack2 = ac.getValueStack();
System.out.println(stack1 == stack2);
return "success";
}
}
2.5.值栈内部结构
栈的特点:先进后出
将后来的数据压在前来的数据上叫:压栈
栈最上面的叫:栈顶
内部结构分两部分:
第一部分:root,结构是list集合
一般都是操作root结构。
第二部分:context,结构是map集合
******************************
通过debug方式可以查看内部结构:
context OgnlContext(id = xx) --- 没源代码,但上面写了implement Map
root CompoundRoot(id = xx) --- 查看这个CompoundRoot类源代码(ctrl+shift+t)
oqnlUtil
overrides
……
******************************
画图解析context存储的对象引用(不是真正的对象,只是对象的引用):
key value
--------------------------------------------------------
request request对象的引用
session HttpSession对象引用
application ServletContext对象引用
parameters 传递相关的参数
attr 这里一个操作----------------》三个域对象,向三个域对象放值,名称都相同:setAttribute("name",value);,使用attr操作时获取域对象里的值,获取最小域对象里的值。
2.6.向值栈放数据
(1)<s:debug>标签用于查看值栈,用于测试。
访问action,执行action的方法返回值,配置返回值到jsp页面中,在jsp页面中使用这个标签。
查看结果:上面是root ,下面是context
主要看root会发现:
Object项里放着action类,这和前面讲的action对象中有值栈,值栈中难道又放了action对象?错,这里放的是对象的引用,并非真正的对象
(2)如何向值栈中放数据
有多种方式:主要讲三种,第三种最常用。
第一种:获取值栈对象 getValueStack();
调用值栈对象里面的 set()方法。
public String execute() throws Exception{
//1.获取值栈对象
ActionContext ac = ActionContext.getContext();
ValueStack stack = ac.getValueStack();
//2.调用值栈对象的set方法
stack.set("username", "administrator");
return "success";
}
第二种:获取值栈对象,调用值栈对象里的 push()方法
public String execute() throws Exception{
//1.获取值栈对象
ActionContext ac = ActionContext.getContext();
ValueStack stack = ac.getValueStack();
//2.调用值栈对象的set方法
stack.set("username", "administrator");
//3.push方法
stack.push("abcd"); //栈顶
return "success";
}
第三种:在action定义变量,生成变量的get()方法
//1.定义变量
private String name;
//2.生成变量的get方法
public String getName(){
return name;
}
public String execute() throws Exception{
//3.向变量设置值
name = "abcd";
return "success";
}
案例:向值栈中放对象
向值栈中放list集合
---------------------------------------------
//1.定义对象
private User user = new User();
//2.生成get方法
public User getUser(){
return user;
}
public String execute() throws Exception{
//3.向值栈中放数据
user.setUserName("admin");
user.setPassWord("123456");
return "success";
}
---------------------------------------------
//1.定义list集合
private List<User> list = new ArrayList<User>();
//2.生成get方法
public List<User> getList(){
return list;
}
public String execute() throws Exception{
//3.向值栈中放数据
//生成两个用户对象放入集合
User user1 = new User();
user1.setUserName("admin1");
user1.setPassWord("1234");
User user2 = new User();
user2.setUserName("admin2");
user2.setPassWord("1234");
list.add(user1);
list.add(user2);
return "success";
}
2. 7.从值栈中获取
7.1 使用struts2的标签+ognl表达式获取值栈数据
<s:property value="ognl表达式" />
(1)取字符串
------------------------------------------------
//1.定义变量
private String name;
//2.生成变量的get方法
public String getName(){
return name;
}
public String execute() throws Exception{
//3.向变量设置值
name = "abcd";
return NONE;
}
--------------------------------------------------
<s:property value="name" /> //获取值栈中字符串name的值
(2)取对象
------------------------------------------------
//1.定义对象
private User user = new User();
//2.生成对象的get方法
public User getUser(){
return user;
}
public String execute() throws Exception{
//3.向对象设置值
user.setName("admin");
user.setAge(18);
return NONE;
}
--------------------------------------------------
<s:property value="user.name" /> //获取值栈中user对象name属性的值
<s:property value="user.age" />
(3)取list集合
有多种方式获取值栈中的list集合。
第一种:
<s:property value="list[0].name" />
<s:property value="list[0].age" />
<s:property value="list[1].name" />
<s:property value="list[1].age" />
缺点:写死了
第二种:使用struts2标签,类似jstl中的<c:foreach>标签
<s:iterator value="list"> //相当于遍历list集合中的一个对象
<s:property value="name" />
<s:property value="age" />
</s:iterator>
第三种:使用较多,稍复杂(推荐)
<s:iterator value="list" var="user">
//遍历值栈list集合,得到每个user对象
//底层实现机制:把每次遍历出来的user对象放到context里面。
//获取 context里面数据特点:写ognl表达式,使用#
<s:property value="#user.name" />
<s:property value="#user.age" />
</s:iterator>
值栈 root
context
注意:<!-- -->不能注释<s:>标签。
2. 8.获得set方法设置的值:根据名称获取值
<s:property value="username" />
获得push方法设置的值:
push没有名称,只有设置的值,怎么取?
向值栈放数据,把向值栈放数据存到数组里面,数组名称为top
<s:property value="[0].top" /> //[0]表示数组中的第一个值,也就是栈顶的值
2. 9.el表达式也能获取值栈数据
使用<s:foreach>+el表达式也能取到值栈的数据。