1.今日任务
·OGNL表达式
·OGNL与Struts2的结合
·使用Struts2完成对客户查询的优化操作
2.相关知识
2.1 OGNL
2.1.1 OGNL的概述
OGNL表达式
OGNL,全称为Object-GraphNavigation Language,它是一个功能强大的表达式语言,用来获取和设置Java对象的属性,它旨在提供一个更高的更抽象的层次来对Java对象图进行导航。
OGNL表达式的基本单位是"导航链",一般导航链由如下几个部分组成:
属性名称(property)
方法调用(method invoke)
数组元素
OGNL不仅仅可以视图导航.支持比EL表达式更加丰富的功能.
2.1.2 OGNL的作用
1、支持对象方法调用,如xxx.doSomeSpecial();
2、支持类静态的方法调用和值访问,表达式的格式:
@[类全名(包括包路径)]@[方法名 | 值名],例如:
@java.lang.String@format('foo%s', 'bar')
或@tutorial.MyConstant@APP_NAME;
设置struts.ognl.allowStaticMethodAccess=true
3、访问OGNL上下文(OGNLcontext)和ActionContext;访问值栈
4、支持赋值操作和表达式串联,如price=100,discount=0.8,
calculatePrice(),这个表达式会返回80;
5、操作集合对象。
2.1.3 OGNL的入门
1.导包
Struts的jar包已经包含了OGNL的包
2.测试代码
public class TestOGNL {
@Test
public void f1() throws OgnlException{
//准备root
User rootUser = new User("tom",25);
//准备context
Map<String, User> context = new HashMap<>();
context.put("user1", new User("jack",18));
context.put("user2", new User("rose",16));
OgnlContext oc = new OgnlContext(context);
//将rootUser作为root部分
oc.setRoot(rootUser);
//测试获取root的name属性
String rootName = (String) Ognl.getValue("name", oc, oc.getRoot());
System.out.println(rootName);
//测试获取context user1的name属性
String user1Name = (String) Ognl.getValue("#user1.name", oc, oc.getRoot());
System.out.println(user1Name);
//测试获取context user1的age属性
Integer user1Age = (Integer) Ognl.getValue("#user1.age", oc, oc.getRoot());
System.out.println(user1Age);
}
}
2.1.4 OGNL语法
public class TestOGNL {
/**
* 基本语法
*
* @throws OgnlException
*/
@Test
public void f1() throws OgnlException {
// 准备root
User rootUser = new User("tom", 25);
// 准备context
Map<String, User> context = new HashMap<>();
context.put("user1", new User("jack", 18));
context.put("user2", new User("rose", 16));
OgnlContext oc = new OgnlContext(context);
// 将rootUser作为root部分
oc.setRoot(rootUser);
// 测试获取root的name属性
String rootName = (String) Ognl.getValue("name", oc, oc.getRoot());
System.out.println(rootName);
// 测试获取context user1的name属性
String user1Name = (String) Ognl.getValue("#user1.name", oc, oc.getRoot());
System.out.println(user1Name);
// 测试获取context user1的age属性
Integer user1Age = (Integer) Ognl.getValue("#user1.age", oc, oc.getRoot());
System.out.println(user1Age);
}
/**
* 赋值
*
* @throws OgnlException
*/
@Test
public void f2() throws OgnlException {
// 准备root
User rootUser = new User("tom", 25);
// 准备context
Map<String, User> context = new HashMap<>();
context.put("user1", new User("jack", 18));
context.put("user2", new User("rose", 16));
OgnlContext oc = new OgnlContext(context);
// 将rootUser作为root部分
oc.setRoot(rootUser);
// 测试为context user1的name属性赋值
String user1Name = (String) Ognl.getValue("#user1.name='小明'", oc, oc.getRoot());
System.out.println(user1Name);
}
/**
* 调用方法
*
* @throws OgnlException
*/
@Test
public void f3() throws OgnlException {
// 准备root
User rootUser = new User("tom", 25);
// 准备context
Map<String, User> context = new HashMap<>();
context.put("user1", new User("jack", 18));
context.put("user2", new User("rose", 16));
OgnlContext oc = new OgnlContext(context);
// 将rootUser作为root部分
oc.setRoot(rootUser);
// 测试调用user2方法
String user2Name = (String) Ognl.getValue("#user2.setName('小红'),#user2.getName()", oc, oc.getRoot());
System.out.println(user2Name);
// 调用静态方法 Math.random()
Double number = (Double) Ognl.getValue("@java.lang.Math@random()", oc, oc.getRoot());
System.out.println(number);
}
}
2.2 值栈的概述
2.2.1 什么是值栈?
ValueStack是Struts的一个接口,字面意义为值栈,OgnlValueStack是ValueStack的实现类,客户端发起一个请求struts2架构会创建一个action实例同时创建一个OgnlValueStack值栈实例,OgnlValueStack贯穿整个 Action 的生命周期,struts2中使用OGNL将请求Action的参数封装为对象存储到值栈中,并通过OGNL表达式读取值栈中的对象属性值。
2.2.2 值栈的内部结构
在 OnglValueStack 中包括两部分,值栈和map(即ognl上下文)
·Context: 即OgnlContext上下文,它是一个map结构,上下文中存储了一些引用,parameters、request、session、application等,上下文的Root为CompoundRoot。
OgnlContext中的一些引用:
parameters:该 Map 中包含当前请求的请求参数
request:该 Map 中包含当前 request 对象中的所有属性
session:该 Map 中包含当前 session 对象中的所有属性
application:该 Map 中包含当前application 对象中的所有属性
attr:该 Map 按如下顺序来检索某个属性: request, session, application
·CompoundRoot:存储了action实例,它作为OgnlContext 的Root对象。
CompoundRoot继承ArrayList实现压栈和出栈功能,拥有栈的特点,先进后出,后进先出,最后压进栈的数据在栈顶。我们把它称为对象栈。
struts2对原OGNL作出的改进就是Root使用CompoundRoot(自定义栈),使用OnglValueStack的findValue方法可以在CompoundRoot中从栈顶向栈底找查找的对象的属性值。
CompoundRoot作为OgnlContext的Root对象,并且在CompoundRoot中action实例位于栈顶,当读取action的属性值时会先从栈顶对象中找对应的属性,如果找不到则继续找栈中的其它对象,如果找到则停止查找。
2.2.3 ActionContext和ValueStack的关系
通过源码查询:
public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
ActionContext ctx;
Integer counter = 1;
Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
if (oldCounter != null) {
counter = oldCounter + 1;
}
ActionContext oldContext = ActionContext.getContext();
if (oldContext != null) {
// detected existing context, so we are probably in a forward
ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
} else {
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
ctx = new ActionContext(stack.getContext());
}
request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
ActionContext.setContext(ctx);
return ctx;
}
* 在创建ActionContext的时候 创建ValueStack的对象,将ValueStack对象给ActionContext.
* ActionContext中有一个ValueStack的引用. ValueStack中也有一个ActionContext的引用.
* ActionContext获取ServletAPI的时候,依赖值栈了.
2.2.4 获取值栈对象
【通过ActionContext对象获取值栈.】
ValueStack stack1=ActionContext.getContext().getValueStack();
【通过request域获取值栈.】
ValueStackstack2=(ValueStack)ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
2.2.5 操作值栈
【对Action中的属性提供get方法的方式】
因为Action本身在值栈中,Action中的属性也就默认在值栈中了,所以我们可以通过对Action的属性提供get方法的方式来操作值栈。
【手动操作值栈】
调用值栈的push和set方法对值栈进行操作
2.3 EL的特殊字符的使用
2.3.1 #号的使用
【获取context的数据】
<s:propertyvalue=”#request.name”/>
【用于构建一个Map集合】:使用struts的UI标签的时候.
<s:iteratorvalue="#{'aaa':'111','bbb':'222','ccc':'333' }"var="entry">
<s:propertyvalue="key"/>---<s:propertyvalue="value"/><br/>
<s:propertyvalue="#entry.key"/>---<s:propertyvalue="#entry.value"/><br/>
</s:iterator>
<s:radiolist="#{'1':'男','2':'女' }"name="sex"></s:radio>
2.3.2 %号的使用
【%强制解析OGNL表达式】
<s:textfield name="name"value="%{#request.name}"/>
【%强制不解析OGNL表达式】
<s:propertyvalue="%{'#request.name'}"/>
2.3.3 $号的使用
【在配置文件中使用OGNL表达式】
在struts的配置文件中使用.XML文件 或者 是属性文件.
3.crm案例
使用Struts2完成对客户查询的优化操作
Action代码:
public class CustomerAction extends ActionSupport implements ModelDriven<Customer>{
private Customer customer = new Customer();
private int currentPage = 1;
private CustomerService customerService = new CustomerServiceImpl();
/**
* 获取客户列表
* @return
* @throws Exception
*/
public String list() throws Exception{
DetachedCriteria dc = DetachedCriteria.forClass(Customer.class);
//获取筛选客户名称
String cust_name = customer.getCust_name();
if (cust_name!=null) {
//如果筛选客户名称不为空添加模糊查询条件
dc.add(Restrictions.like("cust_name", "%"+cust_name+"%"));
}
//设置每一页显示几条记录
int pageSize = 10;
//调用业务层获取客户列表
PageBean<Customer> pb = customerService.getCustomerByPage(dc,currentPage,pageSize);
//将pagebean放入值栈中
ValueStack valueStack = ActionContext.getContext().getValueStack();
valueStack.getContext().put("pb", pb);
return "list";
}
/**
* 添加客户
* @return
* @throws Exception
*/
public String save() throws Exception{
customerService.save(customer);
return "toList";
}
@Override
public Customer getModel() {
// TODO Auto-generated method stub
return customer;
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
}
jsp页面:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ 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>
<TITLE>客户列表</TITLE>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<LINK href="${pageContext.request.contextPath }/css/Style.css" type=text/css rel=stylesheet>
<LINK href="${pageContext.request.contextPath }/css/Manage.css" type=text/css
rel=stylesheet>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>
<SCRIPT language=javascript>
function to_page(currentPage){
if(currentPage){
$("#currentPage").val(currentPage);
}
document.customerForm.submit();
}
</SCRIPT>
<META content="MSHTML 6.00.2900.3492" name=GENERATOR>
</HEAD>
<BODY>
<FORM id="customerForm" name="customerForm" action="${pageContext.request.contextPath }/CustomerAction_list" method="post">
<TABLE cellSpacing=0 cellPadding=0 width="98%" border=0>
<TBODY>
<TR>
<TD width=15><IMG src="${pageContext.request.contextPath }/images/new_019.jpg"
border=0></TD>
<TD width="100%" background="${pageContext.request.contextPath }/images/new_020.jpg"
height=20></TD>
<TD width=15><IMG src="${pageContext.request.contextPath }/images/new_021.jpg"
border=0></TD>
</TR>
</TBODY>
</TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="98%" border=0>
<TBODY>
<TR>
<TD width=15 background=${pageContext.request.contextPath }/images/new_022.jpg><IMG
src="${pageContext.request.contextPath }/images/new_022.jpg" border=0></TD>
<TD vAlign=top width="100%" bgColor=#ffffff>
<TABLE cellSpacing=0 cellPadding=5 width="100%" border=0>
<TR>
<TD class=manageHead>当前位置:客户管理 > 客户列表</TD>
</TR>
<TR>
<TD height=2></TD>
</TR>
</TABLE>
<TABLE borderColor=#cccccc cellSpacing=0 cellPadding=0
width="100%" align=center border=0>
<TBODY>
<TR>
<TD height=25>
<TABLE cellSpacing=0 cellPadding=2 border=0>
<TBODY>
<TR>
<TD>客户名称:</TD>
<TD><INPUT class=textbox id=sChannel2
style="WIDTH: 80px" maxLength=50 name="cust_name" value="${param.cust_name }"></TD>
<TD><INPUT class="button" id="sButton2" type="submit" name="sButton2"></TD>
</TR>
</TBODY>
</TABLE>
</TD>
</TR>
<TR>
<TD>
<TABLE id=grid
style="BORDER-TOP-WIDTH: 0px; FONT-WEIGHT: normal; BORDER-LEFT-WIDTH: 0px; BORDER-LEFT-COLOR: #cccccc; BORDER-BOTTOM-WIDTH: 0px; BORDER-BOTTOM-COLOR: #cccccc; WIDTH: 100%; BORDER-TOP-COLOR: #cccccc; FONT-STYLE: normal; BACKGROUND-COLOR: #cccccc; BORDER-RIGHT-WIDTH: 0px; TEXT-DECORATION: none; BORDER-RIGHT-COLOR: #cccccc"
cellSpacing=1 cellPadding=2 rules=all border=0>
<TBODY>
<TR
style="FONT-WEIGHT: bold; FONT-STYLE: normal; BACKGROUND-COLOR: #eeeeee; TEXT-DECORATION: none">
<TD>客户名称</TD>
<TD>客户级别</TD>
<TD>客户来源</TD>
<TD>联系人</TD>
<TD>电话</TD>
<TD>手机</TD>
<TD>操作</TD>
</TR>
<s:iterator value="#pb.list">
<TR
style="FONT-WEIGHT: normal; FONT-STYLE: normal; BACKGROUND-COLOR: white; TEXT-DECORATION: none">
<TD>${cust_name }</TD>
<TD>${cust_level }</TD>
<TD>${cust_source }</TD>
<TD>${cust_linkman }</TD>
<TD>${cust_phone }</TD>
<TD>${cust_mobile }</TD>
<TD>
<a href="${pageContext.request.contextPath }/customer?method=edit&custId=${cust_id}">修改</a>
<a href="${pageContext.request.contextPath }/customer?method=delete&custId=${cust_id}">删除</a>
</TD>
</TR>
</s:iterator>
</TBODY>
</TABLE>
</TD>
</TR>
<TR>
<TD><SPAN id=pagelink>
<DIV
style="LINE-HEIGHT: 20px; HEIGHT: 20px; TEXT-ALIGN: right">
共[<B>${pb.totalCount}</B>]条记录,[<B>${pb.totalPage}</B>]页
,每页显示
<select name="pageSize">
<option value="15" <c:if test="${pb.pageSize==1 }">selected</c:if>>1</option>
<option value="30" <c:if test="${pb.pageSize==30 }">selected</c:if>>30</option>
</select>
条
[<A href="javascript:to_page(${pb.currentPage-1})">前一页</A>]
<B>${pb.currentPage}</B>
[<A href="javascript:to_page(${pb.currentPage+1})">后一页</A>]
到
<input type="text" size="3" id="currentPage" name="currentPage" />
页
<input type="button" value="Go" οnclick="to_page()"/>
</DIV>
</SPAN></TD>
</TR>
</TBODY>
</TABLE>
</TD>
<TD width=15 background="${pageContext.request.contextPath }/images/new_023.jpg"><IMG
src="${pageContext.request.contextPath }/images/new_023.jpg" border=0></TD>
</TR>
</TBODY>
</TABLE>
<TABLE cellSpacing=0 cellPadding=0 width="98%" border=0>
<TBODY>
<TR>
<TD width=15><IMG src="${pageContext.request.contextPath }/images/new_024.jpg"
border=0></TD>
<TD align=middle width="100%"
background="${pageContext.request.contextPath }/images/new_025.jpg" height=15></TD>
<TD width=15><IMG src="${pageContext.request.contextPath }/images/new_026.jpg"
border=0></TD>
</TR>
</TBODY>
</TABLE>
</FORM>
</BODY>
</HTML>