OGNL表达式
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,是一个使用简单、功能强大的、开源的表达式语言,可以方便地操作任何的对象属性、方法等。
Struts2框架使用OGNL作为默认的表达式语言,主要用于页面的取值。它类似于EL表达式语言,但比EL语法强大很多。
OGNL表达式的用法
OGNL表达式是类似于el表达式,用于页面上取后台的值,但是OGNL表达式需要结合s标签来使用。
OGNL最简单的四种用法(先入门)
1、直接显示一个值
2、调用对象的方法或属性
3、可以直接调用类里的静态方法,把调用静态方法的开关打开
4、计算某个表达式的值
注意:在演示ognl调用静态方法时,需要把静态方法调用的开关打开
在struts.xml中开启OGNL调用静态方法的开关
<!-- 打开通过OGNL表达式调用静态方法的开关 -->
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
案例:
测试ognl几种简单的用法
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ 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>
<h1>演示ognl的几种用法</h1>
直接在页面上显示一个值:<s:property value="1"/><br>
调用对象里的方式:<s:property value="'hello'.toUpperCase()"/><br>
直接调用类里静态方法:<s:property value="@java.lang.Math@max(10,11)"/><br>
运算:<s:property value="1+2"/>
</body>
</html>
值栈(重点)
值栈是struts2框架的数据中转站,里面自动保存了当前Action对象和web对象引用(page,request,session,application,attr,paramertes),也可以手动保存自己的对象,同时也可以随时将这些对象从值栈中取出来。
值栈在哪?
每个请求都会产生一个值栈,会放在request域中,名字struts.valueStack。
获取值栈的两种方式:
1、从request域中获取
2、通过ActionContext中的getValueStack方法获取
案例:
1.新建ValueStackAction类
public class ValueStackAction extends ActionSupport{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
public String execute() throws Exception {
Map<String,Object> req = (Map<String, Object>) ActionContext.getContext().get("request");
ValueStack valueStack = (ValueStack) req.get("struts.valueStack");
// System.out.println(valueStack);
ValueStack valueStack1 = ActionContext.getContext().getValueStack();
System.out.println(valueStack == valueStack1);
return SUCCESS;
}
}
2.新建valueStack.jsp页面
3.在struts.xml配置ValueStackAction
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="true" />
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
<constant name="struts.custom.i18n.resources" value="messageResource"></constant>
<package name="default" namespace="/" extends="struts-default">
<action name="valueStack" class="cn.icbc.action.ValueStackAction">
<result>
/valueStack.jsp
</result>
</action>
</package>
</struts>
结果:请求valueStack这个Action,观察打印结果,发现结果为true,证明两种方式获取到的值栈是同一个值栈.
值栈的数据存储结构分析
存储结构图:
观察值栈的内部结构
1、debug来调试:在ValueStackAction的第21行代码加一个断点,debug启动服务器,访问valueStack这个Action,进入断点,观察值栈的内部结构
OgnlContext
打开OgnlContext的源码,我们发现OgnlContext实现了Map接口,所以OgnlContext其实就是一个Map集合,里面存放键值对。
2、在valueStack.jsp页面中添加<s:debug>标签,来观察值栈结构
访问valueStack这个action,跳转到valueStack.jsp页面
点击debug超链接,出现debug页面
小结:
1、值栈包含两个逻辑部分:对象栈+上下文栈
2、对象栈(List)存放:Action对象+其它对象
3、上下文栈(Map)存放:Action对象,web对象的引用,其它对象。
值栈的操作(重点)
一、值栈的存放数据
struts2框架会自动将当前Action的对象存放到对象栈中
向对象栈手动存放数据
对象栈是栈的结构,先进后出,所以后放的元素在上边。
匿名存放
有名字的存放,实质上存放的是一个Map对象
案例:
1.新建ValueStack1Action类
public class ValueStack1Action extends ActionSupport{
private static final long serialVersionUID = 1L;
@Override
public String execute() throws Exception {
ValueStack valueStack = ActionContext.getContext().getValueStack();
//向对象栈存放数据(匿名存放)
valueStack.push("汪峰");
//向对象栈存放数据(有名字的存放)
valueStack.set("username", "那英");
return SUCCESS;
}
}
2.新建valueStack1.jsp页面
页面中添加<s:debug>标签,来观察值栈结构中的值。
3.在struts.xml的ValueStack1Action类
<action name="valueStack1" class="cn.itcast.action.ValueStack1Action">
<result>
/valueStack1.jsp
</result>
</action>
4.在浏览器中请求valueStack1跳转到页面,通过<s:debug>标签查看值栈
向上下栈里手动存放数据
在浏览器中请求valueStack1跳转到页面,通过<s:debug>标签观察值栈
二、值栈的获取数据
注意:获取值栈中数据需要通过OGNL表达式结合s标签来获取
1.获取对象栈里的数据 规律:不需要加#
a.根据属性名取值:<s:property value=“username”/>
b.根据序号取值
[0].top表示取对象栈里的第一个元素,因为栈顶是一个map,我们需要取出map里username这个key的值,所以这么写[0].top.username
<body>
取对象栈里的数据:<s:property value="username"/><br>
取对象栈里匿名的数据:<s:property value="[1].top"/><br>
取对象栈栈顶的元素:<s:property value="[0].top.username"/><br>
<s:debug></s:debug>
2.取上下文栈里的数据 规律:加#号来取上下文栈中的数据
值栈的搜索顺序
1、如果不加#号,先搜索对象栈,再搜索上下文栈,如果都都搜索不到,就不显示;如果中途搜索到,立即停止搜索;
2、如果加#号,直接搜索上下文栈,如果搜索不到,就不显示;
在实际项目开发中,我们把数据放到对象栈中有两种方法
1、可以手动获取值栈,手动把值存放进去
2、因为当前Action对象会自动保存到对象栈中,那么直接在Action类中定义属性并提供getter方法,也能把数据存放到对象栈中。
值栈的创建过程
1、请求被过滤器过滤,创建值栈(参考过滤器第89行,参考PrepareOperation类第80行),把web对象的引用放入上下文栈中;(参考PrepareOperation类第81行)此时对象栈为空;
StrutsPrepareAndExecuteFilter类
PrepareOperations类
Dispatcher类
2、当访问Action时,会创建ActionProxy来增强Action(参考Dispatcher第553行代码);
在创建ActionProxy时,会创建ActionInvocation(参考efaultActionProxyFactory的第56行代码);
接着会调用ActionProxy的prepare方法做增强的准备工作(参考DefaultActionProxyFactory的第70行代码);
在prepare方法中,会初始化ActionInvocation(参考DefaultActionProxy的第194行代码),在init方法中,把当前Action对象压入栈顶(参考DefaultActionInvocation的第398行代码)此时对象栈的栈顶为当前Action对象
3、然后,把值栈对象放入request中(参考dispatcher第557行代码)
Dispatcher类
4、在初始化ActionProxy之后,会调用层层的拦截器,在执行ModelDriven拦截器时,会先判断该Action是否实现了ModelDriven接口(参考ModelDrivenInterceptor的第94行代码),如果实现了ModelDriven接口,则把model压入栈顶,此时,位于栈顶的是model对象。
5、然后,调用params拦截器,参考第303行代码,把参数值按照名字设置到对象的属性里,因为model对象在栈顶,所以先给model对象的属性赋值,Action中的属性就不会赋值。
创建过程的简单表述:
1.请求被过滤器StrutsPrepareAndExecuteFilter过滤,创建一个空值栈。
2.过滤器调用PrepareOperations类的方法,把web对象的引用放入context中。
3.当访问Action时调用了ActionInvocation.init(),把当前Action对象压入栈顶。
4.然后,调用Dispatcher类中的方法,把值栈对象放入request中。
5.初始化ActionProxy后,会调用层层的拦截器,判断该Action是否实现了ModelDriven接口,若实现则把model压入栈顶。
6.调用params拦截器,把参数值按照名字设置到对象的属性里。
值栈的生命周期
1、每一个请求都会产生一个值栈对象,放在request的域中,名字叫struts.valueStack
2、值栈的生命周期同request的生命周期,如果请求的是action,值栈的生命周期同action的生命周期
3、值栈贯穿整个Action的生命周期
三、OGNL表达式对值栈的操作方法
1.Struts2支持的表达式语言
2.#号的用法(重点)
- 作用:从值栈的上下文栈中取值。如果不加#,就是优先从对象栈里取;
- 可以取pageContext,request,session,application,attr,parameters;
- attr:依次寻找pageContext---->request---->session----->application
- parameters:取请求中的参数值,取出的是一个数组,再通过[0]取出具体的值
在测试时,访问ValueStack2Action要带上一个参数,这样才能取到参数值
#还可以用来构造Map集合和List集合
%号的用法
$号的用法
作用:用于在配置文件(struts.xml )里取值栈里的值。
<action name="valueStack3" class="cn.icbc.action.ValueStack3Action">
<result type="redirect">
/valueStack3.jsp?username=${username}
</result>
</action>