1.OGNL的用法详解
Ognl表达式一般是结合struts2框架的标签来使用的,最重要的作用,就是获取值栈中存储的数据。
在下面的示例,咱们将对ognl的各种用法进行测试,分析OGNL是如何使用的。
- 1.Java对象的直接访问
例如:’itcast’ - 实例方法调用
语法:object.doSomething(),object是对象实例
例如:’itcast’.toUpperCase() - 静态方法调用(类的静态方法或静态变量的访问)–不常用
语法:@[类全名(包括包路径)]@[方法名|值名]
例如:@java.lang.Math@max(10,20)
注意:必须通过配置struts2的常量来开启静态方法调用功能:
struts.ognl.allowStaticMethodAccess=true
- 赋值操作或表达式串联。–可以运算
如计算1+2或者2*2 - 对集合对象的操作
<s:property value="#{‘上海’:‘浦东’,‘北京’:‘丰台’}"/> Ognl对值栈的操作
示例:
搭建struts2的环境:导入jar、struts.xml、web.xml,引入struts标签库,编写表达式代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="/struts-tags" prefix="s" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<!-- s:property标签中的value可以用来解析ognl表达式获取各类数据 -->
<!-- ognl表达式的用法 -->
<!-- s:property标签中的value 可以解析ognl表达式 -->
<s:property value="'itcast'"/><br>
<!-- 可以操作对象的实例方法 -->
<s:property value="'itcast'.toUpperCase()"/><br>
<!-- 可以操作静态方法 -->
<s:property value="@java.lang.Math@max(1,20)"/><br>
<!-- 操作集合 -->
<s:property value="{'上海','北京'}"/><br>
<!-- 操作map集合 -->
<s:property value="#{'上海':'浦东','北京':'丰台' }"/><br>
<!-- 操作表达式 -->
<s:property value="10%3"/><br>
</body>
</html>
应用ognl 表达式调用对象方法或者静态方法,需要结合struts2的\<s:property > 标签,其中提供的value属性支持ognl。
2.值栈概述
2.1 值栈(ValueStack)是什么
值栈(ValueStack),是Struts2的数据中转站(类似于超市)
他自动保存了当前Action对象和其他相关对象(包括常用的Web对象的引用,如request、application、session等),也可以手动保存自己的数据对象,同时也可以随时随地将对象从值栈取出或操作(通过OGNL表达式)
最核心的作用:存值
值栈(ValueStack)的接口是ValueStack类,实现类是OgnlValueStack类,该对象是Struts2利用OGNL的基础。只要我们用到ognl,那么就需要依赖值栈。
Struts2框架将ValueStack对象保存在名为“struts.valueStack”的请求(request)
属性中,即值栈是request中的一个对象,一个请求对应一个Action实例和一个值栈对象。
值栈的获取方式:
request.getAttribute(“struts.valueStack”)- ActionContext.getContext().getValueStack()
该两种方式获取的值栈其实是同一个。
2.2 值栈的数据存储结构的分析
【示例1】
目标:值栈对象的获取,并证明两种方式获取的对象是同一个对象:
1)request.getAttribute(“struts.valueStack”)
2)ActionContext.getContext().getValueStack()
步骤一:编写action
创建包和action
public class DemoAction extends ActionSupport{
@Override
public String demo(){
//获取值栈的第一种方式
ValueStack valueStack1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
//获取值栈的第二种方式
ValueStack valueStack2 = ActionContext.getContext().getValueStack();
System.out.println(valueStack1);
System.out.println(valueStack2);
return SUCCESS;
}
}
<struts>
<!-- 配置开发者模式 -->
<constant name="struts.devMode" value="true"></constant>
<package name="p1" extends="struts-default">
<action name="demo" class="cn.itcast.valueStack.DemoAction" method="demo">
<result name="success">/demo.jsp</result>
</action>
</package>
</struts>
【值栈小结】:
值栈包括两部分:root栈和map栈
1、root栈:继承了List接口,又称之为对象栈
2、map栈:实现了Map接口,又可以称之为上下文栈(context)
3.值栈的操作(重点)
3.1 Root栈的存值和取值
Root栈的存值:
它按照先进后出
的原则存储数据,即先进入的数据被压入栈底
,最后进入的数据在栈顶
,需要读取数据的时候,从栈顶开始弹出数据(即最后一个数据被第一个读出来)。
栈也被成为先进后出表,可进行插入和删除操作,插入称之为进栈(压栈)(push)
,删除称之为退栈(pop)
。
【示例】
值栈数据的保存:向root栈存入数据,响应页面根据不同的方式(通过ognl表达式)分别将值获取出来。
1.Push方式压入栈顶
push方法:将数据压入root栈的栈顶。
public class DemoAction extends ActionSupport{
@Override
public String demo(){
//获取值栈的第一种方式
ValueStack valueStack1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
//获取值栈的第二种方式
ValueStack valueStack2 = ActionContext.getContext().getValueStack();
System.out.println(valueStack1);
System.out.println(valueStack2);
//将数据存入root栈,以push方法压入栈顶
valueStack2.push("zhangsan");
//继续将一个数据压入栈顶
valueStack2.push("lisi");
return SUCCESS;
}
}
Root栈分析:
步骤二:设置响应页面利用ognl表达式从值栈取值
导入struts标签:
<%@taglib uri="/struts-tags" prefix="s" %>
demo.jsp页面内容:
<!--
通过s:property标签获取数据
value:是一个ognl表达式,通过valu值可以到值栈中去查询对应的数据
[0].top:表示获取root栈栈顶的数据
-->
获取栈顶数据1:<s:property value="[0].top"/><br>
获取栈顶数据2:<s:property/><br>
<s:debug></s:debug>
执行成功!
步骤三:测试
2.Set方式压入栈顶
set方法:
将数据压入栈顶:会先创建一个map集合,让后将数据存入到集合中,最后将map集合压入栈顶。
步骤一:修改DemoAction :
public class DemoAction extends ActionSupport{
public String demo(){
//第一种获取值栈的方式
ValueStack valueStack1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
System.out.println(valueStack1);
//第二种获取值栈的方式
ValueStack valueStack2 = ActionContext.getContext().getValueStack();
System.out.println(valueStack2);
//将数据存入root栈栈顶
valueStack2.push("zhangsan");
valueStack2.push("lisi");
//set:创建一个map集合,将数据存入该集合中,最终将map集合压入root栈栈顶
valueStack2.set("name","wangwu");
return SUCCESS;//相当于"none",表示不响应任何结果集视图
}
}
<%--
如果栈顶是map集合:通过集合的key可以直接获取value值
通过name到值栈中从上往下进行查找,找到的第一个进行返回
--%>
获取栈顶map中的数据:<s:property value="name"/><br>
3.通过action的成员变量将数据存入值栈
页面的<s :debug>标签会通过getName()方法从值栈中获取数据并显示在root栈中
步骤一:修改ValueStackAction
在action中添加成员变量,并设置成员变量的的getter和setter方法
3.2 Map栈的存值和取值
1.通过put方法将数据存入map栈
put方法:
将数据存入map栈,map栈是一个map,因此需要通过key来获取对应的value.
如果需要直接从map栈获取数据,建议在ognl表达式中添加一个#,表示直接从map栈查找,而不是从root栈栈顶开始从上往下查找。
如:<s:property value="#name"/>
修改DemoAction
public class DemoAction extends ActionSupport{
private String name = "maliu";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String demo(){
//第一种获取值栈的方式
ValueStack valueStack1 = (ValueStack) ServletActionContext.getRequest().getAttribute("struts.valueStack");
System.out.println(valueStack1);
//第二种获取值栈的方式
ValueStack valueStack2 = ActionContext.getContext().getValueStack();
System.out.println(valueStack2);
//将数据存入root栈栈顶
valueStack2.push("zhangsan");
valueStack2.push("lisi");
//set:创建一个map集合,将数据存入该集合中,最终将map集合压入root栈栈顶
valueStack2.set("name","wangwu");
//将数据存入map栈
//由于map栈本质就是一个集合,因此存储方式和map一样
ActionContext.getContext().put("name", "tianqi");
return SUCCESS;//相当于"none",表示不响应任何结果集视图
}
}
页面获取map栈数据
<%--
从map栈获取数据:在ognl表达式中使用#表示直接从map栈查找数据
--%>
获取map栈中的数据:<s:property value="#name"/>
3.3 值栈的存取小结
值栈的主要作用就是数据的保存和获取
使用值栈的时候要思考:用什么代码存进去,然后再用什么代码取出来
1.如何向值栈保存数据
- ValueStack.push(obj) :
保存数据到Root栈顶
-压入栈顶 - ValueStack.set(key,value):
将数据保存到Root栈顶(数据被封装到map中
,然后将map压入栈顶)–有key(不推荐)
- 提供Action成员变量,提供setter和getter方法(Action就在root栈中,Action属性可以被搜索)
- ActionContext.getContext().put(key,value) :
保存数据到Map栈中
—有key
2.如何向值栈保存数据
-
JSP页面获取
- 通过 [index].top 指定访问root栈某个对象 ,例如<s:property value=”[0].top”/> ,获取栈顶对象的简写方式:<s:property/>
- <s :property value= “name”/> 先搜索root栈的key,再搜索map的key ,将查找到的第一个数据进行返回
(不推荐)
- <s:property value=”#name” /> 搜索map的key ,获取map栈中的数据
-
Action代码获取
ValueStack.findValue(ognl表达式) ; 获取值栈数据
//代码怎么获取值栈数据(页面的ognl表达式获取数据的底层代码)
String str1=(String)ActionContext.getContext().getValueStack().findValue("username");
System.out.println(str1);
String str2=(String)ActionContext.getContext().getValueStack().findValue("#username");
System.out.println(str2);
值栈的主要目的:就是存进去,取出来。
3.4 值栈的创建过程源代码分析(了解)
值栈(和保存对象)的创建过程详解:
1.访问action的时候,走过滤器,StrutsPrepareAndExecuteFilter中创建了值栈(ValueStack<–OgnlValueStack),并将常用web对象保存到OgnlContext的Map中。
注意:此时root栈为空。
2.当访问Action的时候,过滤器会通过ActionProxy代理对象来执行。通过ActionProxyFactory的createActionProxy方法来得到代理对象。在获取代理对象的时候,需要创建一个ActionInvocation接口的默认实现类DefaultActionInvocation的对象,该默认对象在创建的时候会自动调用init方法来初始化,在初始化方法中会自动调用stack.push(action)方法,将当前Action对象,压入Root栈的顶部。
注意:再通过debug调试发现,默认情况下ValueStack的root栈存在2个对象 ,ValueStackAction(Action在栈顶)和DefaultTextProvider(用来初始化资源信息)
原理目的:
- 知道值栈的初始化时机,访问action的时候,才创建(这个过程中会创建actionProxy对象,并且同时创建值栈,并且,将一些对象放入值栈。)
- 值栈初始化之后,里面主要默认有:root栈(action类对象、text。。、model(user)),map栈(servlet相关对象、action对象一个引用)
- 要了解:哪些对象在栈顶!因为后面我们从值栈取值,都从栈顶往下取。所以,一定要知道哪个对象在栈上面。
3.5 值栈的生命周期
【回顾分析】:
- 贯穿整个Action的生命周期,每个Action类的对象实例都拥有一个ValueStack对象。
- Struts2框架将ValueStack对象保存在名为“struts.valueStack”的请求(request)属性中,即值栈是request中的一个对象,一个请求对应一个Action实例和一个值栈对象。
值栈能够线程安全的为每个请求提供公共的数据存取服务。当有请求到达的时候,Struts2会为每个请求创建一个新的值栈,也就是说,值栈和请求是一一对应的,不同的请求,值栈也不一样,而值栈封装了一次请求所有需要操作的相关的数据。
因此:值栈的生命周期,就是request生命周期。
问题:如果结果集使用的是重定向,值栈中的数据存在吗?
答案:值栈存在于request中,request在一次请求结束后生命周期也随之结束,因此值栈的生命周期也就结束了,所以重定向之后的值栈是第二次请求发送时创建的一个新的值栈,找不到之前的数据了。
4.Struts2使用表达式语言来操作值栈
4.1 Struts2支持的表达式语言
Struts 2支持以下几种表达式语言:
- OGNL(Object-Graph Navigation Language),可以方便地操作对象属性的开源表达式语言;
- EL(Expression Language),可以方便的操作JSP页面四个域范围数据 (page、 request、 session、 application );
- JSTL(JSP Standard Tag Library),JSP 2.0集成的标准的表达式语言;
- Groovy,基于Java平台的动态语言,它具有时下比较流行的动态语言(如Python、Ruby和Smarttalk等)的一些起特性;
- Velocity,严格来说不是表达式语言,它是一种基于Java的模板匹配引擎,具说其性能要比JSP好。
Struts 2默认的表达式语言是OGNL,原因是它相对其它表达式语言更简单、强大,最重要的是可以直接操作值栈。
4.2 OGNL对Struts2的值栈的操作方式
Struts2 存取数据,通过值栈完成的, Ognl是访问值栈数据的表示式语言
在Struts2中,使用OGNL表达式获取数据有三种用法 :
- # 号用法:获取map栈数据
- % 号用法:强制解析或者不解析
- $ 号用法:xml中获取值栈数据
1.#号的使用
在使用#获取map栈数据之前,我们对map栈的结构可以在进行一次深入的理解,map栈除了我们可以手动存储数据之外,还保存了其他的一些数据,主要如下:
Map的key(类型是String) | Map的Value (类型是Object) | 说明信息 |
---|---|---|
application | Java.util.Map<String,Object> | 封装的应用域中的所有数据 |
session | Java.util.Map<String,Object> | 封装的会话域中的所有数据 |
request | Java.util.Map<String,Object> | 封装的请求域中的所有数据 |
parameters | Java.util.Map<String,String[]> | 封装的是请求参数 |
attr | Java.util.Map<String,Object> | 封装的是四大域的组合数据,从最小的域开始搜索 |
那么如何来获取map栈中的各大域的数据呢?
用法一: 访问Map栈中常用对象,包括web对象, 添加#进行访问
提示:#attr 按照 page — request — session — application的顺序依次进行搜索
编写action
public class Demo2Action extends ActionSupport{
public String demo2(){
//将数据存入request
ServletActionContext.getRequest().setAttribute("name", "request_name");
//将数据存入session
ServletActionContext.getRequest().getSession().setAttribute("name", "session_name");
//将数据存入application
ServletActionContext.getServletContext().setAttribute("name", "application_name");
return SUCCESS;
}
}
Struts.xml
<action name="demo2" class="cn.itcast.expr.Demo2Action" method="demo2">
<result name="success">/demo2.jsp</result>
</action>
创建页面
<body>
内容:
参数范围:<s:property value="#parameters.name"/>|${param.name }<br>
request范围:<s:property value="#request.name"/>|${requestScope.name }<br>
session范围:<s:property value="#session.name"/>|${sessionScope.name }<br>
application范围:<s:property value="#application.name"/>|${applicationScope.name }<br>
page范围:<s:property value="#attr.name"/>|${name }<br>
</body>
显示如下:
注意:attr依次从page --- request --- session --- application的顺序依次进行搜索。
2.%号的使用
%主要作用是控制解析或者不解析
Struts标签分为普通标签和表单标签两种:
普通标签是通过属性value的ognl表达式获取值栈数据的,比如:<s:property value=””>
表单标签是通过name属性的ognl表达式获取值栈数据的,value属性只能存入字符串,比如:<s:textfield value=“username”>
%”符号的用途是在标签的属性值被理解为字符串类型时,告诉执行环境%{}里的是OGNL表达式,就比如:<s:textfield value=”%{username}”>。 也可在OGNL表达式中,添加%{‘ ’}或者’’,来让其变成普通字符串而不解析,比如:<s:property value=”%{‘username}’”>
简单的说:%表达式的作用是,可以让字符串变成支持ognl表达式的解析,也可以让解析的表达式变成字符串,
public class Demo3Action extends ActionSupport{
public String demo3(){
ActionContext.getContext().put("name", "zhangsan");
return SUCCESS;
}
}
<action name="demo3" class="cn.itcast.expr.Demo3Action" method="demo3">
<result name="success">/demo3.jsp</result>
</action>
demo3.jsp页面
创建一个demo3.jsp页面:
<body>
<!-- 获取map栈数据 -->
<s:property value="#name"/><br>
<!--
强制解析
s:textfield:该标签的value属性并不支持ognl表达式,为了能够获取数据就需要强制解析成一个ognl表达式
-->
<s:textfield value="%{#name}"></s:textfield><br>
<!--
不解析
-->
<s:property value="%{'name'}"/><br>
</body>
3.$号的使用
$主要作用是在相关配置文件中引入OGNL表达式,让其在配置文件中也能解析OGNL表达式。(换句话说:$用于在配置文件中获取值栈的值用的。)
需求:通过结果集类型重定向到另外一个页面的时候,在第二次发送的请求地址后面拼接一个从值栈中获取到的数据。
步骤一:编写action
public class Demo4Action extends ActionSupport{
public String demo4() throws UnsupportedEncodingException{
ActionContext.getContext().put("name", "lisi");
return SUCCESS;
}
}
步骤二:struts.xml:
<action name="demo4" class="cn.itcast.expr.Demo4Action" method="demo4">
<result name="success" type="redirect">/demo4.jsp?name=${#name}</result>
</action>
jsp页面
新建一个demo4.jsp页面
<body>
<h1>执行成功!</h1>
</body>