1. OGNL表达式
1.1 OGNL表达式概述
1.1.1 什么是OGNL表达式
OGNL的全称是对象图导航语言(Object-Graph Navigation Language),它是一种功能强大的开源表达式语言,使用这种表达式语言,可以通过某种表达式语法,存取Java对象的任意属性,调用Java对象的方法,同时能够自动实现必要的类型转换。如果把表达式看作是一个带有语义的字符串,那么OGNL无疑成为了这个语义字符串与Java对象之间沟通的桥梁。
1.1.2 OGNL表达式的由来
它原本是xwork2中的默认表达式语言,当年OpenSymphony和apache在合作开发struts2框架时,把这个表达式也引进来了,所以就变成了struts2的默认表达式语言。
1.1.3 OGNL表达式的使用要求
要想使用ognl表达式,一般情况下都得需要使用struts2的标签库。
<%@ taglib uri="/struts-tags" prefix="s" %>
1.1.4 它的特点
它不仅可以用于取值,显示。还可以赋值。取值是我们程序员使用框架做的事情。赋值是框架为我们做的。
1.2 OGNL表达式的基本用法
1.2.1 s:property标签输出内容到浏览器
1.2.1.1 s:property的用法
<%--要想使用OGNL表达式获取数据,此时需要借助struts2的标签库
s:property标签实现把数据输出到浏览器上
<s:property value=""/>
value属性的取值是一个OGNL表达式。
标签会把value属性取值所对应的内容输出到浏览器上
如果没有任何对应内容,则什么都不显示
--%>
OGNL的最基本用法:<s:property value="OGNLExpression"/>
1.2.1.2 OGNL表达式和字符串的转换
<%--OGNL表达式和字符串的转换
表达式转成字符串
%{''} | %{""}
可以把%{}去掉
--%>
OGNL转成一个普通的字符串:<s:property value="%{'OGNLExpression1'}"/><br/>
OGNL转成一个普通的字符串:<s:property value='%{"OGNLExpression2"}'/><br/>
OGNL转成一个普通的字符串:<s:property value='"OGNLExpression3"'/><br/>
OGNL转成一个普通的字符串:<s:property value="'OGNLExpression4'"/><br/>
<!--
字符串转成表达式
%{}把字符串套起来
-->
<!-- user.name 看上去是字符串,当它执行user对象的getName方法时,表示用OGNL表达式解释。 -->
字符串转成一个OGNL表达式:<s:textfield name="username" value="%{user.name}"/>
1.2.2 OGNL表达式访问对象的方法
<%--OGNL表达式访问对象的方法 --%>
调用字符串的长度方法:<s:property value="'OGNLExpression1'.length()"/><br/>
调用字符串的转大写方法:<s:property value="'OGNLExpression1'.toUpperCase()"/><br/>
调用字符串的分隔方法:<s:property value="'OGNLExpression1'.split('E')"/><br/>
1.2.3 OGNL表达式访问类的静态属性和静态方法
<%--OGNL表达式访问类的静态成员(静态属性)
访问静态属性需要按照固定的书写规范来写。
规范是:
@包名.包名...类名@静态属性名称
--%>
OGNL表达式访问静态属性:<s:property value="@java.lang.Integer@MAX_VALUE"/>
<%--OGNL表达式访问类的静态方法
访问静态方法需要按照固定的书写规范来写。
规范是:
@包名.包名...类名@静态方法名称
--%>
OGNL表达式访问静态方法:<s:property value="@java.lang.Math@random()"/>
1.2.4 OGNL表达式操作集合
1.2.4.1 list集合
<%--操作List集合
s:radio标签的list取值就是一个OGNL表达式。
{}就表示创建了一个List集合
{'男','女'}=== List list = new ArrayList(); list.add("男"); list.add("女");
--%>
Struts2的单选按钮:<br/>
<s:radio list="{'男','女'}" name="gender2" label="性别"></s:radio>
HTML的单选按钮:<br/>
性别:<input type="radio" name="gender1" value="男">男
<input type="radio" name="gender1" value="女">女
1.2.4.2 map集合
<%--操作Map集合
#{}就表示创建了一个Map集合。
#{key:value,key:value}
#{'male':'男','female':'女'}=== Map map = new HashMap(); map.put("male","男"); map.put("female","女");
--%>
Struts2的单选按钮:<br/>
<s:radio list="#{'male':'男','female':'女'}" name="gender4" label="性别"></s:radio>
HTML的单选按钮:<br/>
性别:<input type="radio" name="gender3" value="male">男
<input type="radio" name="gender3" value="female">女
2. OGNL上下文
2.1 ContextMap
2.1.1 ContextMap概述
它是OGNL上下文对象,是struts2中封装数据最大的对象。我们一次请求中所有用到的信息都可以在它里面找到。它是一个Map结构的对象,其中key是字符串,value是一个Object。
2.1.2 ContextMap中封装的数据
我们把这些内容拿出来逐个分析一下,得到下面的表格:
Map的key(类型是String) | Map的Value (类型是Object) | 说明信息 |
---|---|---|
application | Java.util.Map<String,Object> | 封装的应用域中的所有数据 |
session | Java.util.Map<String,Object> | 封装的会话域中的所有数据 |
request | Java.util.Map<String,Object> | 封装的请求域中的所有数据 |
valueStack(特殊) | com.opensymphony.xwork2.ognl.OgnlValueStack | 它是List结构 |
parameters | Java.util.Map<String,String[]> | 封装的是请求参数 |
attr | Java.util.Map<String,Object> | 封装的是四大域的组合数据,从最小的域开始搜索 |
action | com.opensymphony.xwork2.ActionSupport | 当前执行的动作类对象 |
2.2 ActionContext
2.2.1 ActionContext对象概述
它是一个工具类,是struts2框架提供给我们的,可以让我们调用其中的方法,快速的操作ContextMap。用它操作OGNL上下文对象,比直接操作ContextMap要方便很多。
2.2.2 ActionContext对象与ContextMap的关系
ActionContext就相当于对ContextMap进行了一次再封装。
2.2.3 ActionContext何时创建
由于ActionContext是操作的ContextMap,而ContextMap中封了我们一次请求的所有数据,所以它的创建应该是每次请求访问Action时,即核心控制器(StrutsPrepareAndExecuteFilter)的doFilter方法执行时,下图是代码截取:
2.2.4 ActionContext的线程安全
我们都知道,java的web工程是多线程的,那么每个线程在访问Action时,都会创建自己的ActionContext,那么是如何保证在获取ActionContext时,每个线程都能获取到自己的那个呢?
答案就是,每次创建ActionContext时,把对象绑定到当前线程上。下图是代码截取:
2.2.5 ActionContext的获取
使用ActionContext类中的静态方法getContext()从当前线程上获取
2.2.6 获取ContextMap中的数据
2.2.6.1 s:debug标签的使用
<%-- 引入标签库 --%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<%--1、struts2的debug标签
它是一个用于开发阶段的标签,查看我们OGNL上下文中内容的标签 --%>
<s:debug/>
2.2.6.2 使用OGNL表达式获取Map中的数据
动作类存数据:
/**
* ActionContext的数据存取
*
* @author wgy
*/
public class Demo1Action extends ActionSupport {
/**
* 通过ActionContext往ContextMap中存入数据
* contextMap hello context map
*
* 往应用域中存入数据:用两种方式实现
* applicationMap hello application map
* applicationAttr hello application attr
*
* 往会话域中存入数据:同上用两种方式
* @return
*/
public String demo1(){
//1.获取ActionContext
//从当前线程上获取
ActionContext context = ActionContext.getContext();
//2.存入数据
context.put("contextMap", "hello context map");
//3.往应用域中存入数据
//第一种方式:使用原始ServletAPI对象ServletContext
ServletContext applicationAttr = ServletActionContext.getServletContext();
applicationAttr.setAttribute("applicationAttr", "hello application attr");
//第二种方式:根据key从ActionContext中获取应用域的map,往map中存入数据
Map<String,Object> applicationMap = context.getApplication();
applicationMap.put("applicationMap","hello application map");
//4.往会话域中存入数据
//第一种:使用ServletAPI的HttpSession
HttpSession session = ServletActionContext.getRequest().getSession();
session.setAttribute("sessionAttr", "hello session attr");
//第二种:获取key为session的map
Map<String,Object> sessionMap = context.getSession();
sessionMap.put("sessionMap","hello session map");
return SUCCESS;
}
}
在页面中使用OGNL表达式获取:
<%--借助struts2的s:property标签和OGNL表达式获取ActionContext存入的数据
我们现在获取的数据,都是在map中。
获取Map中的数据,OGNL表达式的写法:
#key
如果还想继续向下获取,使用.key的方式
--%>
<s:property value="#contextMap"/><br/>
<s:property value="#application.applicationMap"/><br/>
<s:property value="#session.sessionAttr"/>
<s:property value="#session.sessionMap"/>
2.3 ValueStack对象
2.3.1 ValueStack对象概述
ValueStack是Struts的一个接口,字面意义为值栈,OgnlValueStack是ValueStack的实现类,客户端发起一个请求struts2架构会创建一个action实例同时创建一个OgnlValueStack值栈实例,OgnlValueStack贯穿整个 Action 的生命周期。
它是ContextMap中的一部分,里面的结构是一个List,是我们可以快速访问数据一个容器。它的封装是由struts2框架完成的。
通常情况下我们是从页面上获取数据。它实现了栈的特性(先进后出)。
2.3.2 ValueStack的内部结构
在 OnglValueStack 中包含了一个CompoundRoot的对象,该对象继承了ArrayList,并且提供了只能操作集合第一个元素的方法,所以我们说它实现了栈的特性。同时,它里面定义了一个ContextMap的引用,也就是说,我们有值栈对象,也可以通过值栈来获取ContextMap。
2.3.3 获取ValueStack中的数据
2.3.3.1 值栈中都有什么
首先我们要明确,值栈中存的都是对象。因为它本质就是一个List,List中只能存对象。
值栈中包含了我们通过调用push方法压栈的对象,当前执行的动作了和一个名称为DefaultTextProvider的类。值栈中的内容如下图:
2.3.3.2 在动作类中往值栈中存入数据
/**
* ValueStack的数据存取
*
* @author wgy
*/
public class Demo2Action extends ActionSupport {
//把私有成员放入值栈中
private String name = "泰斯特";
public String getName() {
return name;
}
/**
* 获取ValueStack,并且压栈操作
* @return
*/
public String demo2(){
//1.获取ActionContext
//从当前线程上获取
ActionContext context = ActionContext.getContext();
//2.获取ValueStack对象
ValueStack vs = context.getValueStack();
//3.压栈操作
Student s = new Student("张三",18,"male");
vs.push(s);
return SUCCESS;
}
}
2.3.3.3 我们可以获取值栈中的什么
一般情况下,我们都是根据debug标签中显示的Property Name来获取Property Value。
当然我们也可以获取栈顶对象。
2.3.3.5 在页面上使用OGNL表达式获取数据
<%--获取值栈的数据也需要借助于struts2的标签库
使用s:property获取
获取值栈的数据,是直接写属性名称,得到的就是属性的值。
OGNL表达式的找法,是从栈顶逐个属性名称开始查找,只要找到之后,就不再继续查找,而是返回结果。
--%>
姓名:<s:property value="name"/><br/>
年龄:<s:property value="age"/><br/>
性别:<s:property value="gender"/><br/>
<%--获取指定位置的属性 --%>
获取第一个name:<s:property value="[0].name"/><br/>
获取第二个name:<s:property value="[1].name"/>
<%--如果使用s:property标签,没有写value属性,取的是栈顶对象 --%>
<s:property/>
2.3.3.6 OGNL表达式执行时调用的方法
<%--s:property在通过OGNL表达式获取数据时,所调用的方法:ValueStack中的findValue(String expr); --%>
<%
ActionContext context = ActionContext.getContext();
ValueStack vs = context.getValueStack();
Object o1 = vs.findValue("[0].name");
out.println(o1);
out.println("<br/>");
Object o2 = vs.findValue("[1].name");
out.print(o2);
out.println("<br/>");
Object o3 = vs.findValue("#application.applicationMap");
out.print(o3);
%>
3. Struts2中使用EL表达式
3.1 EL表达式回顾
EL表达式的写法:${表达式}。
它是从四大域中,由小到大逐个域搜索,根据名称获取值。只要找到了,就不再继续搜索。
它的原理:使用的是PageContext类中的findValue方法。
3.2 Struts2对EL表达式的改变
Struts2框架中对EL表达式做了如下改变:
EL表达式原来的搜素顺序:
page Scope——>request Scope——>session Scope——>application Scope
EL表达式改变后的搜索顺序:
page Scope—>request Scope—>valueStack—>contextMap—>session Scope—>application Scope
struts2框架对request对象进行了包装,并且对getAttribute方法进行了增强,代码如下:
4. OGNL表达式中的各种符号总结
4.1 %
1、把OGNL表达式转成普通字符串 %{""}
2、把字符串转成OGNL表达式%{}
4.2 #
1、获取ContextMap中的数据。#key
2、在页面中可以创建Map集合。 #{}
4.3 $
1、EL表达式使用
2、可以在struts2的配置中使用OGNL表达式(配置可以是xml文件,也可以是注解)${}
5. 案例-优化客户列表的展示
5.1 改造Action
我们把之前查询所有客户的动作方法改造一下,之前我们是把查询结果存入请求域中了,而此时我们只需要在Action中定义一个集合,并且提供get/set方法,它就会出现在值栈中。就可以在页面中使用OGNL表达式获取。
/**
* 查询所有客户
* @return
*/
private List<Customer> customers;
public String findAllCustomer(){
//1.调用service查询客户
List<Customer> customers = customerService.findAllCustomer();
//2.返回
return "findAllCustomer";
}
public List<Customer> getCustomers() {
return customers;
}
public void setCustomers(List<Customer> customers) {
this.customers = customers;
}
5.2 改造jsp
在显示客户列表时,我们之前采用的是jstl标签库的c:forEach标签,今天我们将使用struts2提供的迭代标签s:iterator。
<%--
<c:forEach items="${customers}" var="customer">
<TR>
<TD>${customer.custName }</TD>
<TD>${customer.custLevel }</TD>
<TD>${customer.custSource }</TD>
<TD>${customer.custIndustry }</TD>
<TD>${customer.custAddress }</TD>
<TD>${customer.custPhone }</TD>
</TR>
</c:forEach>
--%>
<%--
struts2中的迭代标签:
属性:
value:它的取值是一个OGNL表达式
var:写了该属性:它会把var的值作为key,把当前遍历的对象作为value,存入contextMap中
没写该属性:它会把每次遍历的对象压入栈顶
--%>
<%--
<s:iterator value="customers" var="cust">
<TR>
<TD><s:property value="#cust.custName"/></TD>
<TD><s:property value="#cust.custLevel"/></TD>
<TD><s:property value="#cust.custSource"/></TD>
<TD><s:property value="#cust.custIndustry"/></TD>
<TD><s:property value="#cust.custAddress"/></TD>
<TD><s:property value="#cust.custPhone"/></TD>
</TR>
</s:iterator>
--%>
<s:iterator value="customers">
<TR>
<TD><s:property value="custName"/></TD>
<TD><s:property value="custLevel"/></TD>
<TD><s:property value="custSource"/></TD>
<TD><s:property value="custIndustry"/></TD>
<TD><s:property value="custAddress"/></TD>
<TD><s:property value="custPhone"/></TD>
</TR>
</s:iterator>