![5763525-dccae9b3af058a54.jpg](https://i-blog.csdnimg.cn/blog_migrate/b1c24c8538dfc30e617ab74b5fdf7fc2.webp?x-image-process=image/format,png)
1.OGNL
1.1 OGNL 概述
OGNL,全称是 Object-Graph Navigation Language(对象导航语言),是一种功能强大的开源表达式语言,通过简单的表达式语法就可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。OGNL 是被集成到 struts2中的,并不是 struts2 项目的一部分,struts2 用 OGNL 来取代原始 java web 项目中的 EL 表达式。
1.2 OGNL 结构
- 表达式(Expression)
表达式是整个 OGNL 的核心,OGNL 解析表达式才知道该做什么,如:从对象中取值的操作。
- 根对象(Root Object)
根对象可以理解为 OGNL 的操作对象,而表达式只是说明了对这个对象所做的操作。OGNL 称为对象导航语言,也即是把对象放在一张图中,通过任意一个对象作为根,可以找到与这个对象关联的其他对象。
- 上下文环境(Context)
OGNL 在取值操作的过程中还需要一个上下文的环境,有了根对象和表达式,OGNL就知道改对谁做什么操作,但还需知道这个操作执行的位置,也就是上下文环境。上下文环境 Context 是一个 Map 类型的对象,在表达式中访问 Context 中的对象需要使用 # 号加上对象的名称。
1.3 OGNL 的简单使用
OGNL 在 struts2 中,要结合 struts2 的标签一起使用,首先要 导入 OGNL 的 jar 包,在jsp页面中我们就可以直接使用 OGNL 表达式完成之前需要使用 EL 表达式结合 java 代码才能做到的事情。
牛刀小试:
记得在 web.xml 文件中配置 struts2 的拦截器。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- 引入struts2的标签库 -->
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 在struts2 标签中使用 ognl 表达式 -->
<s:property value="'ognl'.length()"/>
<!-- 页面的输出结果是 ognl 这个字符串的长度 4 -->
</body>
</html>
2.Struts2 中的值栈
2.1 值栈是什么
在原始的 java web 项目,在 servlet 中操作完后,把数据放到域对象中,然后在 jsp 页面中使用 EL 表达式来获取数据值,域对象在一定的范围内可以进行存值和取值。在 struts2 中也提供了一种存储机制,类似于域对象,就是值栈,同样可以存值和取值。这样在 Action 类中把数据放到值栈中,就可以在页面中获取值栈中的数据。
值栈英文是 ValueStack,它其实是 struts2 的一个接口,而 OgnlValueStack 是 ValueStack 的实现类,浏览器访问 一个 action 请求时,struts2 架构会创建一个 action 的实例同时也会创建一个 OgnlValueStack 的值栈实例,OgnlValueStack 贯穿整个 Action 实例的生命周期,struts2 使用 OGNL 来把请求 action 中的参数封装成对象存储在值栈中,并通过 OGNL 表达式来读取值栈中的对象的属性值。
2.2 值栈对象
之前说到的 ActionContext 和 ServletActionContext 中都有获取 ValueStack的方法,可以去看这两个类的源码,所以通过这两个类就可以获取到 ValueStack对象的引用。
servlet 和 action的区别:
-
- Servlet 默认是在第一次访问是创建的,只创建一次,是单实例的对象。
-
- Action 是在每次访问时创建的,每访问一次 action,就会创建一个 action 对象,对应创建一个值栈对象。
示例代码如下:
证明每个 action 对象中的 valueStack 对象只有一个。
package cc.wenshixin.action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class ActionDemo1 extends ActionSupport {
@Override
public String execute() throws Exception {
//1.获取ActionContext类的对象
ActionContext context = ActionContext.getContext();
//2.调用方法获得值栈对象的引用
ValueStack stack1 = context.getValueStack();
ValueStack stack2 = context.getValueStack();
//3.比较两个值栈引用是否为同一个值栈对象的引用
System.out.println(stack1==stack2); //结果为true
return NONE;
}
}
2.3 值栈的内部结构
从名字上就可以看出值栈具有数据结构中栈的结构特性,先进后出(后进先出)。可以查看 OgnlValueStack 类的源码或者 Debug 的方式查看值栈的结构,会发现值栈具有 root 和 context 两个属性,这就是值栈主要的两部分。root 部分基于List集合,root属性继承 ArrayList,实现压栈和出栈的功能,用来存储 action 和实例以及请求的参数,我们一般操作的都是 root 中的数据。context 基于Map集合,也即是 OnglContext(上下文),存储了一些对象的引用,如 parameters、request、response、session、application等。
context 部分的存储结构表
key(固定) | value |
---|---|
request | request对象的引用 |
session | httpSesion对象的引用 |
application | servletContext对象的引用 |
parameter | 传递相关的参数 |
att | 三个域对象的值 |
在 struts2 中提供了一个调试值栈的标签,s:debug,使用这个标签就可以查看到值栈结构以及存储的值。
相关代码如下:
jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- 引入struts2的标签库 -->
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 使用debug标签查看值栈中的值 -->
<s:debug></s:debug>
</body>
</html>
Action 类
package cc.wenshixin.action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.util.ValueStack;
public class ActionDemo2 extends ActionSupport {
@Override
public String execute() throws Exception {
return "success";
}
}
struts 配置
<package name="demo1" extends="struts-default" namespace="/">
<action name="action2" class="cc.wenshixin.action.ActionDemo2">
<result name="success">/debug.jsp</result>
</action>
</package>
![5763525-4cea98af0985232d.png](https://i-blog.csdnimg.cn/blog_migrate/a7b29bee9e2f540159c8a7c807e0e121.webp?x-image-process=image/format,png)
3.值栈的相关操作
3.1 值栈存放数据
向值栈中放数据的方式很多,下面介绍几种常见的方式。
3.1.1 使用值栈对象,调用对象中的 set() 方法
将上面 Action 类中的部分代码改为下面的,重新刷新页面,查看debug标签中的结构,发现值栈中多出一个 map对象,使用 set() 方法,会将对象存储到一个map集合中。
public String execute() throws Exception {
//1.获取ActionContext类的对象
ActionContext context = ActionContext.getContext();
//2.调用方法获得值栈对象的引用
ValueStack stack = context.getValueStack();
//调用set方法
stack.set("name", "小明");
return "success";
}
![5763525-184b08db4b16c57a.png](https://i-blog.csdnimg.cn/blog_migrate/6e7ae4dd1f197735d0053bf50acfd193.webp?x-image-process=image/format,png)
3.1.2 使用值栈对象,调用对象中的 push() 方法
将上面 Action 类中的部分代码改为下面的,重新刷新页面,查看debug标签中的结构,发现值栈中多出一个 String 对象,使用 push() 方法,会将对象存储到一个 String 字符串中。
public String execute() throws Exception {
//1.获取ActionContext类的对象
ActionContext context = ActionContext.getContext();
//2.调用方法获得值栈对象的引用
ValueStack stack = context.getValueStack();
//调用push方法
stack.push("小明");
return "success";
}
![5763525-10294168945c52cc.png](https://i-blog.csdnimg.cn/blog_migrate/fc6514ec7d0d3f560472d81b31b8c6cb.webp?x-image-process=image/format,png)
3.1.3 在 Action 中定义变量,生成变量的 get 方法
将上面 Action 类中的代码改为下面的,重新刷新页面,查看debug标签中的结构,发现值栈的属性中多了一个定义的变量的名称,并且该属性的值就是 Action 中赋予该变量的值。
package cc.wenshixin.action;
import com.opensymphony.xwork2.ActionSupport;
public class ActionDemo5 extends ActionSupport {
private String name; //定义变量
//生成变量的get方法
public String getName() {
return name;
}
@Override
public String execute() throws Exception {
name = "小明"; //给变量赋值
return "success";
}
}
![5763525-b2a9030c5069bdcb.png](https://i-blog.csdnimg.cn/blog_migrate/32b4e40943dd09531404ac39cb98bba4.webp?x-image-process=image/format,png)
3.1.4 向值栈中放对象
实现步骤:
-
- 创建实体类对象
-
- 定义实体类对象的变量
-
- 生成变量的get方法
将上面 Action 类中的代码改为下面的,实体类的对象自己创建,重新刷新页面,查看debug标签中的结构,发现值栈的属性中多了一个定义的变量的名称,并且该属性的值就是 Action 中赋予该变量的值。
package cc.wenshixin.action;
import com.opensymphony.xwork2.ActionSupport;
import cc.wenshixin.entity.Student;
public class ActionDemo6 extends ActionSupport {
//1.定义对象变量,这里也可以不创建对象内容,在下面new
private Student student = new Student();
//2.生成对象的get方法
public Student getStudent() {
return student;
}
@Override
public String execute() throws Exception {
//给对象赋值
student.setName("小明");
student.setSex("女");
return "success";
}
}
![5763525-b2a9030c5069bdcb.png](https://i-blog.csdnimg.cn/blog_migrate/32b4e40943dd09531404ac39cb98bba4.webp?x-image-process=image/format,png)
3.1.5 向值栈中放 list 集合
实现步骤:
-
- 创建实体类对象
-
- 定义实体类对象的 list 集合变量
-
- 生成变量的get方法
将上面 Action 类中的代码改为下面的,实体类的对象自己创建,重新刷新页面,查看debug标签中的结构,发现值栈的属性中多了一个定义的list集合的名称,并且该属性的值就是 Action 中添加到list集合的对象。
package cc.wenshixin.action;
import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import cc.wenshixin.entity.Student;
public class ActionDemo7 extends ActionSupport {
private List<Student> list = new ArrayList<Student>();
public List<Student> getList() {
return list;
}
@Override
public String execute() throws Exception {
Student student1 = new Student();
student1.setName("小明");
student1.setSex("男");
Student student2 = new Student();
student2.setName("小红");
student2.setSex("女");
list.add(student1);
list.add(student2);
return "success";
}
}
![5763525-ce209bb0efd4dbe4.png](https://i-blog.csdnimg.cn/blog_migrate/31f91328a8407da3933f041a1063a317.webp?x-image-process=image/format,png)
3.2 获取值栈数据
上面实在 Action 中向值栈中放数据,下面在 jsp 页面中使用 Struts2标签 + OGNL 表达式把这些数据显示出来。
3.2.1 获取字符串的值
使用上面 3.1.3 中的 Action 类的代码,显示 Action 中字符串里面的值。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- 引入struts2的标签库 -->
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<!-- 获取字符串的值 -->
<s:property value="name"/>
</body>
</html>
3.2.2 获取对象的值
使用上面 3.1.4 中的 Action 类的代码,显示 Action 中对象的值,在上面的jsp代码中追加下面的代码访问。
<!-- 获取对象的值 -->
<s:property value="student.name"/>
<s:property value="student.sex"/>
3.2.3 获取 list 集合的值
获取 list 集合中的值,有三种方式。
使用上面 3.1.5 中的 Action 类的代码,显示 Action 中 list 集合的值,在上面的jsp代码中追加下面的代码访问。
- 第一种方式需要知道 list 集合中的元素的个数。
<!-- 第一种方式获取list集合中的值 -->
<s:property value="list[0].name"/>
<s:property value="list[0].sex"/>
<s:property value="list[1].name"/>
<s:property value="list[1].sex"/>
- 第二种方式通过
<s:iterator>
标签遍历 <s:property>标签中的值
<!-- 第二种方式获取list集合中的值 -->
<s:iterator value="list">
<s:property value="name"/>
<s:property value="sex"/>
</s:iterator>
- 第三种方式使用 # 取出list种的元素
机制:把每次遍历出来的元素对象放到 context 里面,用 OGNL 表达式获取 context 中的数据需要特殊符号 #
<!-- 第三种方式获取list集合中的值 -->
<s:iterator value="list" var="student">
<s:property value="#student.name"/>
<s:property value="#student.sex"/>
</s:iterator>
3.2.4 其他获取方式
使用上面 3.1.1 中的 Action 类的代码,获取用 set() 方法存到值栈中的值,使用 set() 方法中设置的值的名称。
<!-- 其他获取方式 -->
<s:property value="name"/>
使用上面 3.1.2 中的 Action 类的代码,获取用 push() 方法存到值栈中的值,因为使用 push() 方法设置值没有名称,只有设置的值,使用值栈的数据名 top,根据数据名来获取值,不过写法有点怪,数组名在后面。
<!-- 其他获取方式 -->
<s:property value="[0].top"/>
1.4 OGNL 中 # 和 % 的使用总结
1.4.1 # 的使用
在前面获取 list 集合中的第三种方式,使用了 # 来获取 context 种的数据。
1.4.2 % 的使用
struts2 中还定义了表单输入标签,如果在 struts2 的表单标签中使用 OGNL 表达式是不能识别的,需要 %{OGNL表达式}
才能识别。
使用上面 3.1.3 中的 Action 类的代码,将字符串中的值放在一个表单输入项中。
<!-- struts2的表单输入项 -->
<s:textfield name="name" value="%{name}"></s:textfield>