上次课回顾
Struts2的Servlet的API的访问
- 完全解耦合的方式:ActionContext对象(基于值栈)
- 使用原生的方式:ServletActionContext对象
- 接口注入的方式:实现ServletRequestAware,ServletContext Aware,ServletResponseAware
Struts2的结果页面配置
- 结果页面类型
- 全局结果页面
- 局部结果页面
- 结果页面配置
- name属性
- type属性
- dispatcher :默认值,转发
- redirect :重定向
- chain :转发到Action
- redirectAction:重定向到Action
Struts2的数据的封装
- 属性驱动
- 一种:提供set方法的方式
- 二种:页面提供OGNL表达式的方式
- 模型驱动
- 三种:模型驱动的方式
Struts2的复杂类型的数据封装
- 封装到List集合中
- 封装到Map集合中
2、OGNL表达式
2.1 OGNL概述
对象导航图语言(Object Graph Navigation Language),简称OGNL,是应用于Java中的一个开源的表达式语言(Expression Language),它被集成在Struts2等框架中,作用是对数据进行访问,它拥有类型转换、访问对象方法、操作集合对象等功能。
- OGNL:对象图导航语言,比EL表达式强大很多倍的语言
- EL:从域对象中获取数据,从EL的11个对象中获取数据
- OGNL:调用对象的方法,获取Struts2的值栈的数据。
2.2 OGNL使用的要素
- 表达式
- 根对象
- Context对象
2.3 OGNL的java环境下的入门(了解)
- 访问对象的方法
//OGNL调用对象的额方法
@Test
public void demo1() throws OgnlException {
//获得context
OgnlContext context = new OgnlContext();
//获得根对象
Object root = context.getRoot();
//执行表达式
Object obj = Ognl.getValue("'helloWorld'.length()", context, root);
System.out.println(obj);
}
- 访问对象的静态方法
@Test
public void demo2() throws OgnlException {
//获得context对象
OgnlContext context = new OgnlContext();
//获得根对象
Object root = context.getRoot();
//执行表达式:@类名@方法名
Object obj = Ognl.getValue("@java.lang.Math@random()",context, root);
System.out.println(obj);
}
- 获得Root中的数据
//访问Root中的数据,不需要加#
@Test
public void demo3() throws OgnlException {
//获得context对象
OgnlContext context = new OgnlContext();
context.setRoot(new User("hamza","666"));
//获得根对象
Object root = context.getRoot();
//执行表达式
Object username = Ognl.getValue("name", context, root);
Object pwd = Ognl.getValue("password", context, root);
System.out.println(username+"-->"+pwd);
}
- 获得OGNLContext中的数据
//访问context中的数据,需要加#
@Test
public void demo4() throws OgnlException {
//获得context对象
OgnlContext context = new OgnlContext();
//获得根对象
Object root = context.getRoot();
//向context中存入数据
context.put("name", "张三");
//执行表达式
Object name = Ognl.getValue("#name", context, root);
System.out.println(name);
}
2.4 OGNL 的Struts2环境入门
- 访问对象的方法
<h3>调用对象的方法</h3>
<s:property value="'struts'.length()"/>
- 访问对象的静态方法
<h3>调用对象的静态方法</h3>
<!-- 静态方法访问在struts2中是默认关闭的,要使用就得开启一个常量 -->
<s:property value="@java.lang.Math@random()"/>
<!-- 开启静态方法访问 -->
<constant name="struts.ognl.allowStaticMethodAccess" value="true"></constant>
3、值栈
3.1 值栈的概述
3.1.1 什么是值栈
Struts2将XWork对Ognl的扩展这一套机制封装起来,这个对象叫ValueStack。
ValueStack实际上就是一个容器。它由Struts框架创建,当前端页面如jsp发送一个请求时,Struts的默认拦截器会将请求中的数据进行封装,并入ValueStack的栈顶。
- ValueStack类似于一个数据中转站(Struts2的框架当中的数据就都保存到了ValueStack中)
- ValueStack接口,实现类OgnlValueStack对象
- ValueStack贯穿整个Action的生命周期。(Action一旦创建你了,框架就会创建一个ValueStack对象)
3.1.2 值栈的内部结构
- ValueStack中有两个主要的区域:
- root区域 :其实就是一个ArrayList。里面一般放置对象。获取root的数据不需要加#
- context区域 :其实就是一个Map。里面放置的是web开发的常用的对象数据的引用。获取context数据需要加#
- request
- session
- application
- parameter
- attr
- 所说的操作值栈,通常指的是操作ValueStack中的root区域。
3.1.3 值栈与ActionContext的关系
- ServletContext :Servlet的上下文
- ActionContext :Action的上下文
- 通过源代码查看到:当请求过来的时候,执行过滤器中doFilter方法,在这个方法中创建ActionContext,在创建ActionContext过程中,创建ValueStack对象,将ValueStack对象传递给ActionContext对象。所以可通过ActionContext获取值栈对象。
- ActionContext对象之所以能够访问Servlet的API(访问是域对象的数据),因为在其内部有值栈的引用。
3.2 获得值栈
- 通过ActionContext对象获得值栈
//一种:通过ActionContext获得
ValueStack valueStack = ActionContext.getContext().getValueStack();
- 在Struts2的内部,将值栈存入request中一份
//二种:通过request对象获得
ValueStack valueStack2 = (ValueStack) ServletActionContext.getRequest()
.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
//一个Action的实例,只会创建一个ValueStack的对象
System.out.println(valueStack == valueStack2);
//结果为true
3.3 操作值栈—向值栈中存入数据
- 一种:在Action中提供属性的get方法方式
public class ValueStackDemo3 extends ActionSupport{
private User user;
public User getUser() {
return user;
}
@Override
public String execute() throws Exception {
//向ValueStack中存入数据
user = new User("张三","123");
return SUCCESS;
}
}
在页面中用OGNL表达式获取
<s:property value="user.name"/>
- 二种:ValueStack中本身的方法方式
@Override
public String execute() throws Exception {
//向ValueStack中存入数据
//获得值栈对象
ValueStack valueStack = ActionContext.getContext().getValueStack();
//使用push(Object obj); set(String key,Object obj)
User user = new User("张三","123456");
valueStack.push(user);
valueStack.set("laqin", "laqin"); //创建了一个Map,将这个Map压入到栈顶
return SUCCESS;
}
<!-- 方式二的获取:调用ValueStack本身的方法 -->
<s:property value="username"/>
<s:property value="pwd"/>
<s:property value="laqin"/>
3.4 获取值栈的数据
- 获取值栈中的数据就是在页面中使用OGNL表达式即可
- 获取root的数据:不需要加#
<!-- 获取一个对象 -->
<s:property value="name"/>
<s:property value="pwd"/>
<!-- 获取集合中的对象-->
<s:property value="list[0].name"/><br/>
<s:property value="list[0].pwd"/><br/>
<s:property value="list[1].name"/><br/>
<s:property value="list[1].pwd"/><br/>
<s:property value="list[2].name"/><br/>
<s:property value="list[2].pwd"/><br/>
<s:property value="list[3].name"/><br/>
<s:property value="list[3].pwd"/><br/>
获取context的数据
<!-- 获取context中的数据 -->
<s:property value="#request.name"/>
<s:property value="#session.name"/>
<s:property value="#application.name"/>
<s:property value="#attr.name"/>
3.4.1 EL为何能访问值栈的数据
- 因为Struts2的框架底层对request.getAttribute(String name)进行了增强
4、OGNL中特殊字符
4.1 #号
- 获取context的数据
<%
request.setAttribute("name", "张三");
%>
<s:property value="#request.name"/>
- 可以使用#来构建Map集合
<h3>构建Map集合</h3>
<!-- 遍历值栈中的数据 -->
<s:iterator value="list">
<s:property value="name"/>
</s:iterator>
<s:iterator var="i" value="{'aa','bb','cc','dd'}">
<s:property value="i"/> -- <s:property value="#"/><br/>
</s:iterator>
<!-- Map集合-->
<s:iterator var="entry" value="#{'name':'张三'},'age':'23' }">
<s:property value="key"/> ----><s:property value="value"/><br/>
<s:property value="#entry.key"/> ----><s:property value="#entry.value"/><br/>
</s:iterator>
4.2 %号
- 强制解析OGNL
<h2>%号的用法</h2>
<%
request.setAttribute("name", "牛魔王");
%>
姓名:<s:textfield name="name" value="%{#request.name}"/>
- 强制不解析OGNL
4.3 %号
- 在配置文件中使用OGNL
- 属性文件
- 国家化地方
- message_zh_CN.properties
- user.login=登录
- userwelcome=欢迎,${#session.username}
- message_en_US.properties
- userlogin=Login
- userwelcome=欢迎,${#session.username}
- message_zh_CN.properties
- 国家化地方
- xml文件
- 文件下载
- 配置
- 文件下载
- 属性文件
<action name="download" class="xxx.DownloadAction">
<result type="stream">
<param name="Content-Type">文件类型</param>
<param name="Content-Disposition">attachment:filename=${文件名}</param>
</result>
</action>