JavaWeb开发知识总结(struts2-ognl_valueStack_interceptor)
1. ognl表达式
OGNL是Object-Graph Navigation Language(对象图导航语言)的缩写,它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。 类似EL表达式,但功能更强大。Ognl导航类似于Java中的链式编程,ognl表达式的导航本质是使用get和set方法进行获取/设置数据
ognl表达式的作用:
- 支持对象的操作,调用对象的方法;
- 支持静态成员访问;
- 支持赋值操作与表达串联;
- 访问OGNL上下文,访问OgnlContext对象;
- 操作集合对象。
ognl在struts2中的作用:
# ognl是表达式,和el表达式类似,el表达式用在jsp中获取域对象里面的值
1. ognl也是一种表达式,比el表达式功能更加强大;
2. 使用ognl主要作用:在struts2里面获取值栈中存储的数据。
# ognl本身不是struts2一部分,而是单独项目,经常和struts2一起使用
* 要使用ognl首先导入jar包,在struts2里面支持ognl,默认提供ognl的jar包。
# 在struts2里面获取值栈数据
* ognl在struts2里面和struts2标签一起使用,来获取值栈中的数据。
ognl作用案例:项目中需要导入ognl的jar包ognl-3.0.6.jar
// ognl表达式的作用:
public class StrutsOgnlDemo1 {
@Test
/**
* 功能1: ognl操纵对象
* ognl表达式的三要素:表达式,ognlContext对象,root对象
*/
public void demo1() throws OgnlException {
// 1.创建ognlContext上下文对象
OgnlContext context = new OgnlContext();
// 2.操作对象
// Object value = Ognl.getValue("'somnus'", context, new Object());
// 从root中获取的值是somnus字符串
// Object value = Ognl.getValue("'somnus'.length()", context, new
// Object()); // root中获取的值是somnus字符串的长度
// Object value = Ognl.getValue("#somnus", context, new Object());
// 从Context中获取的值是somnus属性,不存在返回null
// Object value = Ognl.getValue("somnus", context, new Object());
// 从root中获取的值是somnus属性,不存在时报错
// 在Context中有对root的引用
Object root = context.getRoot();
Object value = Ognl.getValue("somnus", context, root);
// 报错
System.out.println(value);
}
/**
* 功能2: ognl访问静态常量和静态方法
*/
@Test
public void demo2() throws OgnlException {
// 1.获取Context对象
OgnlContext context = new OgnlContext();
// 2.获取root对象
Object root = context.getRoot();
// 3.执行表达式,访问静态常量
Object value = Ognl.getValue("@java.lang.Math@PI", context, root);
System.out.println(value);
// 访问静态方法
Object value2 = Ognl.getValue("@java.lang.Math@random()", context, root);
System.out.println(value2);
}
/**
* 功能3:ognl的Context对象
* Context对象本质是Map集合
* 访问Context中的数据需要加#
*/
@Test
public void demo3() throws OgnlException{
// 1.获取Context对象
OgnlContext context = new OgnlContext();
// 2.获取root对象
Object root = context.getRoot();
// 3.向Context中存储数据
context.put("somnus", "sunmos");
// 4.通过表达式取出数据
Object value = Ognl.getValue("#somnus", context, root);
System.out.println(value);
}
/**
* 功能3:ognl的root对象
* root对象本质是list集合
* 访问Context中的数据不需要加#
*/
@Test
public void demo4() throws OgnlException{
// 1.获取Context对象
OgnlContext context = new OgnlContext();
// 2.获取root对象,获取root对象,需要在context设置root后才能获取
// Object root = context.getRoot();
// 3.向Context中存储Map集合数据
Map<String, String> map = new HashMap<String, String>();
map.put("username", "somnus");
context.put("username", "sunmos");
// 将map设为root
context.setRoot(map);
Object root = context.getRoot();
// 4.通过表达式取出数据,取出root中的数据,不能加#
Object value = Ognl.getValue("username", context, root); // 值是somnus
// 4.通过表达式取出数据,取出context中的数据,不能加#
// Object value = Ognl.getValue("#username", context, root); // 值是sunmos
System.out.println(value); // 输出时somnus
}
/**
* 功能4:操作集合
*/
@Test
public void demo5() throws OgnlException {
// 1.获取Context对象
OgnlContext context = new OgnlContext();
// 2.获取root
Object root = context.getRoot();
// 3.操作集合
Object value = Ognl.getValue("{'hello','good','well'}", context,root);
System.out.println(value.getClass()); // class java.util.ArrayList
// list集合的数据可以存储在root中
context.setRoot(value);
// 取出root中的数据
Object value2 = Ognl.getValue("[0]", context, root);// 取出List集合中索引为0的值
//相当于创建了一个Map集合,和上述创建List集合不能同时存在
// Object value3 = Ognl.getValue("#{'username':'tom','age':20}", context,context.getRoot());
// System.out.println(value3.getClass()); // class java.util.LinkedHashMap
// System.out.println(value3); // {username=tom, age=20}
}
/**
* 功能5:赋值及表达式串联
*/
@Test
public void demo6() throws OgnlException {
// 1.获取上下文对象OgnlContext
OgnlContext context = new OgnlContext(); // 它就是一个java.util.Map
//2.操作ognl表达式
//相当于创建了一个Map集合
Object value = Ognl.getValue("#{'username':'tom','age':20}", context,context.getRoot());
// 将值设为root
context.setRoot(value);
// 取出root中的值及为root中值赋值
// Object value2 = Ognl.getValue("username,age=45", context,context.getRoot()); // 值为45
// Object value3 = Ognl.getValue("username", context,context.getRoot()); // 值为tom
Object value3 = Ognl.getValue("username='somnus'", context,context.getRoot()); //值为somnus
System.out.println(value3);
}
}
struts2中使用ognl表达式:
在struts2框架中我们使用ognl表达式的作用是从valueStack中获取数据。我们在struts2框架中可以使用ognl+valueStack达到在页面(jsp)上来获取相关的数据。在jsp页面上使用ognl表达式,就需要结合struts2框架的标签需要导入strutss2的标签库,struts2-tags,简写是s,在jsp页面使用< s:property value=”表达式”>来使用。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html><head>
<title>ognl在struts2中的使用</title>
</head><body>
<!-- 获取somnus字符串的长度 -->
<s:property value="'somnus'.length()"/>
<!-- 获取Math类的常量值PI -->
<s:property value="@java.lang.Math@PI"/>
<!-- 获取Math类的随机数 -->
<s:property value="@java.lang.Math@random()"/>
</body></html>
2. valueStack值栈
在Servlet中是通过域对象(3个域对象)向前端页面传递数据,将数据存储到域中,在前端页面jsp中通过EL表达式获取域对象中的数据。
在struts2中action处理完数据后需要携带数据到前端页面上,struts2中使用valueStack值栈方式存储数据。valueStack本质是一个数据容器。valueStack类似Servlet的域对象,valueStack是存储在每一个action中,在前端页面jsp中可以通过ognl表达式(也可以是EL表达式)获取值栈中存储的数据。
2.1 Servlet与action的区别:(重点)
Servlet默认是在第一次被请求访问时创建对象,只创建一次,单实例对象,每次请求访问Servlet都会重新开启一个线程去执行service方法进行处理请求。
Action会在每次请求访问时都会重新创建一个对象,多实例对象。
2.2 action的生命周期:(个人理解)
valueStack被设计成是一个接口com.opensymphony.xwork2.util.ValueStack
,struts2基于ognl表达式提供了实现类com.opensymphony.xwork2.ognl.OgnlValueStack
,根据查看action执行过程可知:action的生命周期
当客户端向发送一个请求,服务器就会创建一个Action来处理请求,每一次请求都会有一个新的action对应(多实例,不存在线程安全问题);Struts2会根据每一次的http请求来创建对应的ActionContext(与当前线程绑定),每一次请求对应一个线程来处理request请求,同时为action实例创建一个单独的valueStack对象并将其存储到request域中;valueStack值栈和ActionContext对象都是随着Action实例的创建而存在,随着Action的销毁而消失,valueStack是存储在request域中,则valueStack的生命周期和request的生命周期相同,则Action生命周期和request生命周期相同,request的生命周期是一次请求一次响应范围。
rquest---->ActionContext---->Action---->ValueStack
是一一对应的关系,其生命周期都和request相同,是一次请求一次响应范围。
2.3 valueStack值栈内部结构
通过查看值栈的源码可知,值栈由两部分组成root和Context。并且root本质是ArrayList集合,Context本质是linkedHashMap集合。一般存储的数据都是存储在root中,Context中存储的是对象的引用。与action相关的数据存储到root中,与Servlet的API相关的数据存储到Context中。root模拟的是栈结构形式的存取数据。在jsp页面使用struts的<s:dubeg>
标签可以查看值栈的结构。同时Context中存储了root的引用。
root中存储数据类型:
root中存储的是action相关的数据,action每次被访问时创建实例对象,struts2会将当前action的实例存储到root的栈底,当action中有模型对象时会将模型对象也存储到root栈中,同时在action中声明的属性都会存储到root中栈中。如果action声明任何属性或模型时,action实例对象是位于root中的栈顶。
Context中存储的数据类型:本质是Map集合,存储的是对象引用,key值是固定的
Map集合的key | Map集合的value |
---|---|
request | 本次请求HttpServletRequest对象的引用 |
session | 本次会话HttpSession对象引用 |
application | web应用的ServletContext对象引用 |
parameters | 本次请求的所有参数的封装引用 |
attr | 获取Servlet三个域中存储的值,当存储的名称相同时,获取是域范围最小的中存储的值 |
对象(会变) | 当jsp页面使用< s:iterator value=”集合” var=”p”> 声明var变量时,会将p对象引用存储在Context中 |
2.4 valueStack值栈对象的获取
值栈是存储在每次请求的request域中,则可以先获取request对象,再从request中获取valueStack对象;每次action被访问时创建ActionContext对象,并向其中存储valueStack对象,则可以通过ActionContext对象获取valueStack对象。值栈对象针对每一个action实例只有一个实例对象。
// 在action中任意一个方法中均可以获取:
// 第一种方式:通过ServletActionContext获取request对象,再获取request域中存储的valueStack对象
ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
// 第二种方式:通过actionContext对象获取valueStack对象
ValueStack valueStack2 = ActionContext.getContext().getValueStack();
2.5 向valueStack中存储数据
向值栈中存储数据,主要是都是存储在root中的;struts2框架会自动向root中存储数据(两类);在action中向valueStack中存储数据有两种方式。
struts2框架自动向root中存储数据:
通过查看源码可知,struts2框架会向值栈的root中存储数据(两类):第一类是将当前的action对象存储到root的栈底;第二类是将action的模型驱动封装的模型(需要action实现ModelDriven接口)或成员属性(属性需要提供get方法)存储到root中。