关于属性文件的读法

Size: px
Start display at page:

Download "关于属性文件的读法"

Transcription

1 轻松入门之 Struts2 V3.0 前言 Struts2 其实并不是一个陌生的 Web 框架,Struts2 是以 Webwork 的设计思想为核心, 吸收了 Struts1 的优点, 因此, 可以认为 Struts2 是 Struts1 和 Webwork 结合的产物 Struts2 的使用人群逐渐增多, 它在逐步吸引开发者和用户的目光, 毫无疑问, 大家最终都会选择于它, 困为它确实是一个很优秀的框架 Struts2 方面的书籍很多, 如果你是想精通 strtus2, 那么将很遗憾地告知你, 本书内容不适合你 ; 如果你想花最少的时间来获取对 struts2 的最大了解程度, 则它将是你最好的选择 本着学习共享的精神, 将总结的资料与所有的朋友共享, 希望各位朋友多多指教 感谢周大庆和聂静宇等朋友的参与, 本教程又添加了相应的内容, 并对以前的细节做了调整 由于时间关系, 未能全部完善, 希望更多的朋友参与进来, 集众人所长, 所快乐分享 由于精力有限, 有些内容未细细推敲, 有些内容攫取于网络, 如果无意之间侵犯了您的版权, 请您来信告知, 我将尽快删除 如果知识点有误, 感谢告知 从 2.0 发布至今已有三个多月, 在这三个多月中发生了很多事情, 尤其是姑姑的去世让我悲痛万分 希望身边的朋友多关注健康, 重视健康 这段时间也一直忙于基于 struts2 的项目开发, 直到项目临近尾声之时才抽出空儿来对本文档进行更新 希望更多的朋友参与进来, 把最实用的知识展现出来, 让更多的初学者朋友少走弯路, 快速上手 欢迎免费索取本资料, 欢迎相互交流 本资料仍在不断完善之中, 可以通过加入群 发邮件或关注 blog 来获取最新资料 如果你的 JavaWeb 基础不够夯实, 请参看 JavaWeb 书籍, 如果想对 struts2 有深入了解, 请与 JSF 比照学习, 以下书籍向大家推荐, 书籍中的案例均具有可操作性 JSF 编程 清华出版社出版 : 详情请查询 : Java Web 整合 人民邮电出版社出版 : 详情请查询 : 精通 JSF 人民邮电出版社出版: 详情请查询 : 技术交流 QQ: 文档学习 QQ 群 : 作者 qhxx@tom.com 交流 blog: QQ: QQ 群 : Page 1 of 162

2 大漠孤烟 目录 第 1 章 STRUTS2 入门... 3 第 2 章 STRUTS2 晋级... 9 第 3 章 STRUTS2 核心概念 struts2 的体系结构 struts2 配置文件 Action 配置 第 4 章表单验证 手动完成输入校验 struts2 框架实现数据校验 第 5 章国际化实现 页面的国际化 Action 的国际化 验证信息的国际化 第 6 章拦截器浅析 拦截器基础 使用拦截器 自定义拦截器 综合示例 第 7 章探讨 IOC 模式 第 8 章 STRUTS2 标签 第 9 章表达式 OGNL OGNL 概述 OGNL 基础 struts2 中 OGNL OGNL 使用示例 第 10 章上传下载 第 11 章视图浅析 第 12 章集成 AJAX JSON 概述 JSON-RPC 概述 JSON 示例 struts2 与 JSON 示例 第 13 章集成 HIBERNATE 系统总体设计图 系统用例图 数据库 系统效果图展示 代码清单 代码树形图 第 14 章集成 SPRING QQ: QQ 群 : Page 2 of 162

3 第 15 章集成 IBATIS 第 16 章集成 JQUERY 什么是 Jquery Jquery 操作 CSS Jquery 操作 DOM Jquery 处理 text Jquery 处理 xml Jquery 处理 json 第 17 章投票管理系统 第 18 章无纸化办公管理系统 第 19 章某数据采集系统 第 1 章 Struts2 入门 Struts2 的学习持续有一段时间了, 也经历个几个小项目 身边也有一些朋友在逐步使用 struts2, 为了减轻他们的学习曲线, 我把自己的学习过程用文字记录下来, 供他们参考 现在也决定把它共享出来, 请大家批评指正 Struts2 虽然简单, 但要一一描述清楚, 可能会形成厚厚地一本书 所以我这里仅仅记录与工作相关的内容 有些部分请读者朋友查看相应的帮助文档或专业书籍 一 环境说明 1. Struts 版本 :2.1.2 此处下载的是 struts all QQ: QQ 群 : Page 3 of 162

4 2. JDK 版本 :JDK Tomat 版本 :6 4. MySql 版本 : MyEclipse 版本 :6 除无特别说明, 后面的程序都基于以上环境 有些内容是基于 struts 所提供的案例 当提交一个 HTML 表单到这个框架中的时候, 输入并不是被发送到服务页, 而是被发送到你提供的 Java 类, 这些类被称为 Action 在这些 Action 执行完后, 选择某一个资源来呈现返回结果, 这个资源一般是页面, 但也可以是 PDF 文件, 或者是 Excel 文件, 亦或是 Java applet 窗口 假设你想要创建一个 Hello World 的例子来呈现欢迎信息, 在你准备好开发环境后, 为了创建一个 Hello World 的例子, 你需要做如下三件事情 : 1 创建一个 jsp 页面来呈现欢迎信息 ; 2 创建一个 Action 类来创建信息 ; 3 在配置文件中配置 action 和页面的映射关系 注意 : 为了创建这个组件, 我们将工作流分成几乎无人不晓的三部分 : 视图 模型和控制器 分离这三部分的原因是当系统变得越来月复杂的时候, 我们能够更好的管理 一. 准备工作建立 web 工程, 其中工程名为 tutorial, 在 WebRoot 下引入 struts2 的 lib 下的如下 4 个包 : QQ: QQ 群 : Page 4 of 162

5 在 web.xml 文件中增加 struts2 的 FilterDispatcher, 修改后的 web.xml 如下 : <?xml version="1.0" encoding="utf-8"?> <web-app id="webapp_9" version="2.4" xmlns= xmlns:xsi=" xsi:schemalocation=" <display-name>tutorial</display-name> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.filterdispatcher</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app> 注意 :struts2 配置了一个过滤器来执行分发功能 二. 代码 1 我们需要一个 jsp 页面来呈现信息,HelloWorld.jsp 页面代码如下所示 : <%@ page contenttype=" text/html; charset=utf-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>hello World!</title> </head> <body> QQ: QQ 群 : Page 5 of 162

6 <h2> <s:property value="message" /> </h2> </body> </html> Struts2 的标签引用也简单多了, 只需要一句就可以 2 我们需要一个 Action 类来创建信息, 代码如下 : package org.xmh.demo; import java.util.date; import java.text.dateformat; import com.opensymphony.xwork2.actionsupport; public class HelloWorld extends ActionSupport { private String message; public String getmessage() { return message; public String execute() { message = "Hello World, Now is " + DateFormat.getInstance().format(new Date()); return SUCCESS; 3 我们需要在配置文件中进行相应配置来将两者联系起来 让我们编辑 src 下的 struts.xml 文件, 该文件内容如下 : <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="tutorial" extends="struts-default"> <action name="helloworld" class="tutorial.helloworld"> <result>/helloworld.jsp</result> </action> <!-- Add your actions here --> </package> </struts> 4 发布与测试 部署该应用程序并在 IE 地址栏中输入 可以看到标题栏为 :Hello World, 并且显示 Struts is up and running! 的页面呈现在我们面前 三 代码是如何工作的? QQ: QQ 群 : Page 6 of 162

7 上述 Hello World 的示例用处理流程如下所示 : 用文字来描述这个处理过程如下 : 1 浏览器请求 发送到 web 应用服务器 2 容器接收到了 Web 服务器对资源 HelloWorld.action 的请求, 根据 web.xml 中的配置, 服务器将包含有.action 后缀的请求转到 org.apache.struts2.dispatcher.filterdispatcher 类进行处理 调用这个 FilterDispatcher, 进入 struts2 的流程中 3 框架在 struts.xml 配置文件中查找名为 HelloWorld 的 action 对应的类 框架初始化该 Action 并且执行该 Action 类的 execute 方法 ; 4execute 方法将信息放入 message 变量中, 并返回成功 5 框架检查配置以查看当返回成功时对应的页面 框架告诉容器来获得请求返回的结果页面 HelloWorld.jsp; 6<s:property value="message" /> 标签调用 HelloWorld 的 Action 类中的 getmessage 方法来获得 message 的值 7 标签显示 message 并以 HTML 格式呈现给用户 ; 四 测试 Action 测试 Action 很简单, 以下是上述 HelloWorld 的 Action 类的测试类的代码 : QQ: QQ 群 : Page 7 of 162

8 package org.xmh.demo; import junit.framework.testcase; import com.opensymphony.xwork2.actionsupport; public class TestHello extends TestCase { public void testhelloworld() throws Exception { HelloWorld hello_world = new HelloWorld(); String result = hello_world.execute(); asserttrue(actionsupport.success.equals(result)); 五 需要记住的东西本框架利用 Action 来处理 HTML 的表单以及其余请求 Action 返回一个结果的名字字符串, 例如 SUCCESS ERROR 以及 INPUT 等, 从 struts.xml 中获取映射信息 一个给定的结果字符串将选择一个页面或其他资源 ( 图片或 PDF) 来返回给用户 当一个 jsp 被载入的时候, 通常有一些动态变化的元素需要 Action 来载入 为了更加容易的显示动态数据, 本框架提供了一些可以跟 HTML 配合使用的标签 六 问题汇总如果你的程序无法正常运行, 并且显示如下的错误 : 严重 : Exception starting filter struts2 Unable to load configuration. - bean jar:file:/f:/struts2/struts2/webroot/web-inf/lib/struts2-core jar!/struts-defau lt.xml:46:178 at com.opensymphony.xwork2.config.configurationmanager.getconfiguration(configurationman ager.java:58) at org.apache.struts2.dispatcher.dispatcher.init_preloadconfiguration(dispatcher.java:37 1) at org.apache.struts2.dispatcher.dispatcher.init(dispatcher.java:424) 原因是由于有些文件的版本不匹配造成的, 请将 lib 包中相关的 commons 组件包拷至项目之中并覆盖之即可 QQ: QQ 群 : Page 8 of 162

9 如果你的程序出现如下的错误信息严重 : Could not find action or result There is no Action mapped for namespace / and action name login. - [unknown location] at com.opensymphony.xwork2.defaultactionproxy.prepare(defaultactionproxy.java:186) at org.apache.struts2.impl.strutsactionproxyfactory.createactionproxy(strutsactionproxyfactory.java:41) at org.apache.struts2.dispatcher.dispatcher.serviceaction(dispatcher.java:494) at java.lang.thread.run(unknown Source) 请确保你的 struts2.xml 放在 classes 下, 亦即你的 struts2.xml 是否位于 src 目录之下 第 2 章 Struts2 晋级 Struts2 实际上是在 Webwork 基础上构建起来的 MVC 框架 从 Struts2 的源代码中可以看到, 有很多都是直接使用的 xwork(webwork 的核心技术 ) 的包 既然从技术上来说 Struts2 是全新的框架, 那么就让我们来学习一下这个新的框架的使用方法 如果大家使用过 Struts1.x, 应该对建立基于 Struts1.x 的 Web 程序的基本步骤非常清楚 让我们先来回顾一下建立基于 Struts1.x 的 Web 程序的基本步骤 1 安装 Struts 由于 Struts 的入口点是 ActionServlet, 所以得在 web.xml 中配置一下这个 Servlet 2 编写 Action 类 ( 一般从 org.apache.struts.action.action 类继承 ) 3 编写 ActionForm 类 ( 一般从 org.apache.struts.action.actionform 类继承 ), 这一步不是必须的, 如果要接收客户端提交的数据, 需要执行这一步 4 在 struts-config.xml 文件中配置 Action 和 ActionForm 5 如果要采集用户录入的数据, 一般需要编写若干 JSP 页面, 并通过这些 JSP 页面中的 form 将数据提交给 Action 下面按编写 struts1.x 程序的这五步和 struts2.x 程序的编写过程一一对应, 看看它们的异同 编写一个基于 Struts2 的 Web 程序 这个程序的功能是让用户录入两个整数, 并提交给一个 Struts Action, 并计算这两个数的代数和, 如果代码和为非负数, 则跳转到 positive.jsp 页面, 否则跳转到 negative.jsp 页面 第 1 步 安装 Struts2 这一步对于 Struts1.x 和 Struts2 都是必须的, 只是安装的方法不同 Struts1 的入口点是一个 Servlet, 而 Struts2 的入口点是一个过滤器 (Filter) 因此,Struts2 要按过滤器的方式配置 下面是在 web.xml 中配置 Struts2 的代码 : <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.filterdispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 第 2 步 编写 Action 类这一步和 Struts1.x 也必须进行 只是 Struts1.x 中的动作类必须从 Action 类中继承, 而 Struts2.x 的动作类需要从 com.opensymphony.xwork2.actionsupport 类继承 下面是计算两个整数代码和的 QQ: QQ 群 : Page 9 of 162

10 Action 类, 代码如下 : package action; import com.opensymphony.xwork2.actionsupport; public class FirstAction extends ActionSupport { private int operand1; private int operand2; public String execute() throws Exception { if (getsum() >= 0) // 如果代码数和是非负整数, 跳到 positive.jsp 页面 { return "positive"; else // 如果代码数和是负整数, 跳到 negative.jsp 页面 { return "negative"; public int getoperand1() { return operand1; public void setoperand1(int operand1) { System.out.println(operand1); this.operand1 = operand1; public int getoperand2() { return operand2; public void setoperand2(int operand2) { System.out.println(operand2); this.operand2 = operand2; public int getsum() { return operand1 + operand2; // 计算两个整数的代码数和 从上面的代码可以看出, 动作类的一个特征就是要覆盖 execute 方法, 只是 Struts2 的 execute 方法没有参数了, 而 Struts1.x 的 execute 方法有四个参数 而且 execute 方法的返回值也不同的 Struts2 只返回一个 String, 用于表述执行结果 ( 就是一个标志 ) 上面代码的其他部分将在下面讲解 第 3 步 编写 ActionForm 类在本例中当然需要使用 ActionForm 了 在 Struts1.x 中, 必须要单独建立一个 ActionForm 类 ( 或是定义一个动作 Form), 而在 Struts2 中 ActionForm 和 Action 已经二合一了 从第二步的代码可以看出, 后面的部分就是应该写在 ActionForm 类中的内容 所以在第 2 步, 本例的 ActionForm 类已经编写 QQ: QQ 群 : Page 10 of 162

11 完成 ( 就是 Action 类的后半部分 ) 第 4 步 配置 Action 类这一步 struts1.x 和 struts2.x 都是必须的, 只是在 struts1.x 中的配置文件一般叫 struts-config.xml( 当然也可以是其他的文件名 ), 而且一般放到 WEB-INF 目录中 而在 struts2.x 中的配置文件一般为 struts.xml, 放到 WEB-INF/classes 目录中 下面是在 struts.xml 中配置动作类的代码 : <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="struts2" namespace="/mystruts" extends="struts-default"> <action name="sum" class="action.firstaction"> <result name="positive">/positive.jsp</result> <result name="negative">/negative.jsp</result> </action> </package> </struts> 在 <struts> 标签中可以有多个 <package>, 第一个 <package> 可以指定一个 Servlet 访问路径 ( 不包括动作名 ), 如 /mystruts extends 属性继承一个默认的配置文件 struts-default, 一般都继承于它, 大家可以先不去管它 <action> 标签中的 name 属性表示动作名,class 表示动作类名 <result> 标签的 name 实际上就是 execute 方法返回的字符串, 如果返回的是 positive, 就跳转到 positive.jsp 页面, 如果是 negative, 就跳转到 negative.jsp 页面 在 <struts> 中可以有多个 <package>, 在 <package> 中可以有多个 <action> 我们可以用如下的 URL 来访问这个动作 : 注 :Struts1.x 的动作一般都以.do 结尾, 而 Struts2 是以.action 结尾 第 5 步 编写用户录入接口 (JSP 页面 ) 1 主界面(sum.jsp) 在 Web 根目录建立一个 sum.jsp, 代码如下 : <%@ page language="java" import="java.util.*" pageencoding="gbk" %> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title> 输入操作数 </title> </head> <body> 求代数和 <br/> <s:form action="mystruts/sum.action" > <s:textfield name="operand1" label=" 操作数 1"/> <s:textfield name="operand2" label=" 操作数 2" /> <s:submit value=" 代数和 " /> </s:form> </body> </html> 在 sum.jsp 中使用了 Struts2 带的 tag 在 Struts2 中已经将 Struts1.x 的好几个标签库都统一了, 在 Struts2 中只有一个标签库 /struts-tags 这里面包含了所有的 Struts2 标签 但使用 Struts2 的标 QQ: QQ 群 : Page 11 of 162

12 签大家要注意一下 在 <s:form> 中最好都使用 Struts2 标签, 尽量不要用 HTML 或普通文本, 大家可以 将 sum.jsp 的代码改为如下的形式, 看看会出现什么效果 : 求代数和 <br/> <s:form action="mystruts/sum.action" > 操作数 1:<s:textfield name="operand1" /><br/> 操作数 2:<s:textfield name="operand1" /><br/> <s:submit value=" 代数和 " /> </s:form> 提示一下, 在 <s:form> 中 Struts2 使用 <table> 定位 2 positive.jsp <%@ page language="java" import="java.util.*" pageencoding="gbk"%> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title> 显示代数和 </title> </head> <body> 代数和为非负整数 <h1><s:property value="sum" /></h1> </body> </html> 3 negative.jsp <%@ page language="java" import="java.util.*" pageencoding="gbk"%> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title> 显示代数和 </title> </head> <body> 代数和为负整数 <h1><s:property value="sum" /></h1> </body> </html> 这两个 jsp 页面的实现代码基本一样, 只使用了一个 <s:property> 标签来显示 Action 类中的 sum 属性值 <s:property> 标签是从 request 对象中获得了一个对象中得到的 sum 属性, 如我们可以使用如下的代码来代替 <s:property value= sum />: <% com.opensymphony.xwork2.util.ognlvaluestack ovs = (com.opensymphony.xwork2.util.ognlvaluestack)request.getattribute("struts.valuestack" ); out.println(ovs.findstring("sum")); %> 启动 Tomcat 后, 在 IE 中输入如下的 URL 来测试这个例子 : QQ: QQ 群 : Page 12 of 162

13 处理一个 form 多个 submit 在很多 Web 应用中, 为了完成不同的工作, 一个 HTML form 标签中可能有两个或多个 submit 按钮, 如下面的效果图所示 : 当输入 xmh s demo 单击保存按钮时 : 当输入 xmh s demo 单击打印按钮时 : 由于在 <form> 中的多个提交按钮都向一个 action 提交, 使用 Struts2 Action 的 execute 方法就无法判断用户点击了哪一个提交按钮 如果大家使用过 Struts1.x 就会知道在 Struts1.2.9 之前的版本需要使用一个 LookupDispatchAction 动作来处理含有多个 submit 的 form 但使用 LookupDispatchAction 动作需要访问属性文件, 还需要映射, 比较麻烦 从 Struts1.2.9 开始, 加入了一个 EventDispatchAction 动作 这个类可以通过 java 反射来调用通过 request 参数指定的动作 ( 实际上只是判断某个请求参数是不存在, 如果存在, 就调用在 action 类中和这个参数同名的方法 ) 使用 EventDispatchAction 必须将 submit 的 name 属性指定不同的值以区分每个 submit 而在 Struts2 中将更容易实现这个功能 当然, 也可以模拟 EventDispatchAction 的方法通过 request 获得和处理参数信息 但这样比较麻烦 在 Struts2 中提供了另外一种方法, 使得无需要配置可以在同一个 action 类中执行不同的方法 ( 默认执行的是 execute 方法 ) 使用这种方式也需要通过请求参来来指定要执行的动作 请求参数名的格式为 action!method.action 注 : 由于 Struts2 只需要参数名, 因此, 参数值是什么都可以 QQ: QQ 群 : Page 13 of 162

14 下面我就给出一个实例程序来演示如何处理有多个 submit 的 form: 第 1 步 实现主页面 <%@ page language="java" import="java.util.*" pageencoding="gbk"%> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>my JSP 'hello.jsp' starting page</title> </head> <body> <s:form action="submit.action" > <s:textfield name="msg" label=" 输入内容 "/> <s:submit name="save" value=" 保存 " align="left" method="save"/> <s:submit name="print" value=" 打印 " align="left" method="print" /> </s:form> </body> </html> 第 2 步 实现 Action 类 package org.xmh.demo; import javax.servlet.http.httpservletrequest; import org.apache.struts2.interceptor.servletrequestaware; import com.opensymphony.xwork2.actionsupport; public class MoreSubmitAction extends ActionSupport implements ServletRequestAware { private String msg; private javax.servlet.http.httpservletrequest request; // 获得 HttpServletRequest 对象 public void setservletrequest(httpservletrequest request) { this.request = request; // 处理 save submit 按钮的动作 public String save() throws Exception { request.setattribute("result", " 成功保存 [" + msg + "]"); return "save"; // 处理 print submit 按钮的动作 public String print() throws Exception { request.setattribute("result", " 成功打印 [" + msg + "]"); return "print"; public String getmsg() { return msg; public void setmsg(string msg) { QQ: QQ 群 : Page 14 of 162

15 this.msg = msg; 上面的代码需要注意如下两点 : save 和 print 方法必须存在, 否则会抛出 java.lang.nosuchmethodexception 异常 Struts2 Action 动作中的方法和 Struts1.x Action 的 execute 不同, 只使用 Struts2 Action 动作的 execute 方法无法访问 request 对象, 因此,Struts2 Action 类需要实现一个 Struts2 自带的拦截器来获得 request 对象, 拦截器如下 : org.apache.struts2.interceptor. ServletRequestAware 第 3 步 配置 Struts2 的配置文件 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="demo" extends="struts-default"> <action name="submit" class="org.xmh.demo.moresubmitaction"> <result name="save">/result.jsp</result> <result name="print">/result.jsp</result> </action> </package> </struts> 第 4 步 编写结果页 <%@ page pageencoding="gbk"%> <html> <head> <title> 提交结果 </title> </head> <body> <h1>${result</h1> </body> </html> 在 result.jsp 中将在 save 和 print 方法中写到 request 属性中的执行结果信息取出来, 并输出到客户端 启动 Tomcat 后, 在 IE 中执行如下的 URL 来测试程序 : 大家也可以直接使用如下的 URL 来调用 save 和 print 方法 : 调用 save 方法 : 调用 print 方法 : 第 3 章 Struts2 核心概念 本章节将深入探讨 struts2 的核心概念, 主要通过示例来说明它的使用, 最后着重介绍它的三个组成部分 Action Result Interceptor 的原理和使用方法 QQ: QQ 群 : Page 15 of 162

16 3.1 struts2 的体系结构 Struts2 的工作机制从图可以看出, 一个请求在 Struts2 框架中的处理大概分为以下几个步骤 : 1 客户端初始化一个指向 Servlet 容器 ( 例如 Tomcat) 的请求 ; 2 这个请求经过一系列的过滤器(Filter)( 这些过滤器中有一个叫做 ActionContextCleanUp 的可选过滤器, 这个过滤器对于 Struts2 和其他框架的集成很有帮助, 例如 :SiteMesh Plugin); 3 接着 FilterDispatcher 被调用,FilterDispatcher 询问 ActionMapper 来决定这个请求是否需要调用某个 Action; 4 如果 ActionMapper 决定需要调用某个 Action, FilterDispatcher 把请求的处理交给 ActionProxy; 5 ActionProxy 通过 Configuration Manager 询问框架的配置文件, 找到需要调用的 Action 类 ; 6 ActionProxy 创建一个 ActionInvocation 的实例 7 ActionInvocation 实例使用命名模式来调用, 在调用 Action 的过程前后, 涉及到相关拦截器 (Intercepter) 的调用 8 一旦 Action 执行完毕,ActionInvocation 负责根据 struts.xml 中的配置找到对应的返回结果 返回结果通常是 ( 但不总是, 也可能是另外的一个 Action 链 ) 一个需要被表示的 JSP 或者 FreeMarker 的模版 在表示的过程中可以使用 Struts2 框架中继承的标签 在这个过程中需要涉及到 ActionMapper QQ: QQ 群 : Page 16 of 162

17 3.2 struts2 配置文件 Struts2 相关的配置文件有 web.xml,struts.xml,struts.properties, struts-default.xml,velocity.properties,struts-default.vm 其中 web.xml,struts.xml 是必须的, 其它的配置文件可选择 它们在 web 应用中的功能如下 : web.xml: 包含所有必须的框架组件的 web 部署描述符 Struts.xml: 配置包含 result/view 类型 action 映射 拦截器等的 Struts2 的主要配置文件 Struts.properties: 配置 struts2 的框架属性 Struts-default.xml: 在文件在 struts-action-x.x.jar 中, 该文件是应该被包含在 struts.xml 中的缺省配置 Welocity.properties: 重写了 velocity 的配置文件 Struts-default.vm: 相对于 velocity 的缺省配置 struts.properties 配置文件 struts.properties 文件是一个标准的 Properties 文件, 该文件包含了系列的 key-value 对象, 每个 key 就是一个 Struts 2 属性, 该 key 对应的 value 就是一个 Struts 2 属性值. struts.properties 文件通常放在 Web 应用的 WEB-INF/classes 路径下. 实际上, 只要将该文件放在 Web 应用的 CLASSPATH 路径下, Struts 2 框架就可以加载该文件. struts.properties 配置文件提供了一种改变框架默认行为的机制. 一般来讲我们没必要修改这个文件, 除非你想拥有一个更加友好的开发调试环境 struts.properties 文件中所包含的所有属性都可以在 web.xml 配置文件中使用 "init-param" 标签进行配置, 或者在 struts.xml 文件中使用 constant 标签进行配置. 可以被修改的属性允许改变 Freemarker 选项 -- 改变 action-mapping 类 决定是否允许 XML 配置文件重载及确定默认用户接口主题等行为 一个名为 default.properties 的属性文件包含在 Struts2-Core JAR 文件中 你可以在你项目源文件路径的根目录下创建一个名为 struts.properties 的文件, 来对某个属性进行修改 这样你就可以增加你想要修改的属性 新的属性值将会覆盖默认值 以下为 struts.properties 中定义的 Struts 2 属性 : struts.configuration 该属性指定加载 Struts 2 配置文件的配置文件管理器. 该属性的默认值是 org.apache.struts2.config.defaultconfiguration, 这是 Struts 2 默认的配置文件管理器. 如果需要实现自己的配置管理器, 开发者则可以实现一个实现 Configuration 接口的类, 该类可以自己加载 Struts 2 配置文件. struts.locale 指定 Web 应用的默认 Locale. struts.i18n.encoding 指定 Web 应用的默认编码集. 该属性对于处理中文请求参数非常有用, 对于获取中文请求参数值, 应该将该属性值设置为 GBK 或者 GB2312; 当设置该参数为 GBK 时, 相当于调用 HttpServletRequest 的 setcharacterencoding 方法. struts.objectfactory 指定 Struts 2 默认的 ObjectFactory Bean, 该属性默认值是 spring. struts.objectfactory.spring.autowire 指定 Spring 框架的自动装配模式, 该属性的默认值是 name, 即默认根据 Bean 的 name 属性自动装配. struts.objectfactory.spring.useclasscache 该属性指定整合 Spring 框架时, 是否缓存 Bean 实例, 该属性只允许使用 true 和 false 两个属性值, QQ: QQ 群 : Page 17 of 162

18 它的默认值是 true. 通常不建议修改该属性值. struts.objecttypedeterminer 该属性指定 Struts 2 的类型检测机制, 支持 tiger 和 notiger 两个属性值. struts.multipart.parser 该属性指定处理 multipart/form-data 的 MIME 类型 ( 文件上传 ) 请求的框架, 该属性支持 cos,pell 和 jakarta 等属性值, 即分别对应使用 cos 的文件上传框架,pell 上传及 common-fileupload 文件上传框架. 该属性的默认值为 jakarta. 注意 : 如果需要使用 cos 或者 pell 的文件上传方式, 则应该将对应的 JAR 文件复制到 Web 应用中. 例如, 使用 cos 上传方式, 则需要自己下载 cos 框架的 JAR 文件, 并将该文件放在 WEB-INF/lib 路径下. struts.multipart.savedir 该属性指定上传文件的临时保存路径, 该属性的默认值是 javax.servlet.context.tempdir. struts.multipart.maxsize 该属性指定 Struts 2 文件上传中整个请求内容允许的最大字节数. struts.custom.properties 该属性指定 Struts 2 应用加载用户自定义的属性文件, 该自定义属性文件指定的属性不会覆盖 struts.properties 文件中指定的属性. 如果需要加载多个自定义属性文件, 多个自定义属性文件的文件名以英文逗号 (,) 隔开. struts.mapper.class 指定将 HTTP 请求映射到指定 Action 的映射器,Struts 2 提供了默认的映射器 : org.apache.struts2.dispatcher.mapper.defaultactionmapper. 默认映射器根据请求的前缀与 Action 的 name 属性完成映射. struts.action.extension 该属性指定需要 Struts 2 处理的请求后缀, 该属性的默认值是 action, 即所有匹配 *.action 的请求都由 Struts 2 处理. 如果用户需要指定多个请求后缀, 则多个后缀之间以英文逗号 (,) 隔开. struts.serve.static 该属性设置是否通过 JAR 文件提供静态内容服务, 该属性只支持 true 和 false 属性值, 该属性的默认属性值是 true. struts.serve.static.browsercache 该属性设置浏览器是否缓存静态内容. 当应用处于开发阶段时, 我们希望每次请求都获得服务器的最新响应, 则可设置该属性为 false. struts.enable.dynamicmethodinvocation 该属性设置 Struts 2 是否支持动态方法调用, 该属性的默认值是 true. 如果需要关闭动态方法调用, 则可设置该属性为 false. struts.enable.slashesinactionnames 该属性设置 Struts 2 是否允许在 Action 名中使用斜线, 该属性的默认值是 false. 如果开发者希望允许在 Action 名中使用斜线, 则可设置该属性为 true. struts.tag.altsyntax 该属性指定是否允许在 Struts 2 标签中使用表达式语法, 因为通常都需要在标签中使用表达式语法, 故此属性应该设置为 true, 该属性的默认值是 true. struts.devmode 该属性设置 Struts 2 应用是否使用开发模式. 如果设置该属性为 true, 则可以在应用出错时显示更多 更友好的出错提示. 该属性只接受 true 和 flase 两个值, 该属性的默认值是 false. 通常, 应用在开发阶段, 将该属性设置为 true, 当进入产品发布阶段后, 则该属性设置为 false. struts.i18n.reload 该属性设置是否每次 HTTP 请求到达时, 系统都重新加载资源文件 ( 允许国际化文件重载 ). QQ: QQ 群 : Page 18 of 162

19 认值是 false. 在开发阶段将该属性设置为 true 会更有利于开发, 但在产品发布阶段应将该属性设置为 false. 提示 : 开发阶段将该属性设置了 true, 将可以在每次请求时都重新加载国际化资源文件, 从而可以让开发者看到实时开发效果 ; 产品发布阶段应该将该属性设置为 false, 是为了提供响应性能, 每次请求都需要重新加载资源文件会大大降低应用的性能. struts.ui.theme 该属性指定视图标签默认的视图主题, 该属性的默认值是 xhtml. struts.ui.templatedir 该属性指定视图主题所需要模板文件的位置, 该属性的默认值是 template, 即默认加载 template 路径下的模板文件. struts.ui.templatesuffix 该属性指定模板文件的后缀, 该属性的默认属性值是 ftl. 该属性还允许使用 ftl vm 或 jsp, 分别对应 FreeMarker Velocity 和 JSP 模板. struts.configuration.xml.reload 该属性设置当 struts.xml 文件改变后, 系统是否自动重新加载该文件. 该属性的默认值是 false. struts.velocity.configfile 该属性指定 Velocity 框架所需的 velocity.properties 文件的位置. 该属性的默认值为 velocity.properties. struts.velocity.contexts 该属性指定 Velocity 框架的 Context 位置, 如果该框架有多个 Context, 则多个 Context 之间以英文逗号 (,) 隔开. struts.velocity.toolboxlocation 该属性指定 Velocity 框架的 toolbox 的位置. struts.url.http.port 该属性指定 Web 应用所在的监听端口. 该属性通常没有太大的用户, 只是当 Struts 2 需要生成 URL 时 ( 例如 Url 标签 ), 该属性才提供 Web 应用的默认端口. struts.url.https.port 该属性类似于 struts.url.http.port 属性的作用, 区别是该属性指定的是 Web 应用的加密服务端口. struts.url.includeparams 该属性指定 Struts 2 生成 URL 时是否包含请求参数. 该属性接受 none get 和 all 三个属性值, 分别对应于不包含 仅包含 GET 类型请求参数和包含全部请求参数. struts.custom.i18n.resources 该属性指定 Struts 2 应用所需要的国际化资源文件, 如果有多份国际化资源文件, 则多个资源文件的文件名以英文逗号 (,) 隔开. struts.dispatcher.parametersworkaround 对于某些 Java EE 服务器, 不支持 HttpServlet Request 调用 getparametermap() 方法, 此时可以设置该属性值为 true 来解决该问题. 该属性的默认值是 false. 对于 WebLogic Orion 和 OC4J 服务器, 通常应该设置该属性为 true. struts.freemarker.manager.classname 该属性指定 Struts 2 使用的 FreeMarker 管理器. 该属性的默认值是 org.apache.struts2.views.freemarker.freemarkermanager, 这是 Struts 2 内建的 FreeMarker 管理器. struts.freemarker.wrapper.altmap 该属性只支持 true 和 false 两个属性值, 默认值是 true. 通常无需修改该属性值. struts.xslt.nocache 该属性指定 XSLT Result 是否使用样式表缓存. 当应用处于开发阶段时, 该属性通常被设置为 true; 当应用处于产品使用阶段时, 该属性通常被设置为 false. QQ: QQ 群 : Page 19 of 162

20 struts.configuration.files 该属性指定 Struts 2 框架默认加载的配置文件, 如果需要指定默认加载多个配置文件, 则多个配置文件的文件名之间以英文逗号 (,) 隔开. 该属性的默认值为 struts-default.xml,struts-plugin.xml,struts.xml, 看到该属性值, 所以应该明白为什么 Struts 2 框架默认加载 struts.xml 文件了. struts.xml 常用配置解析 struts.xml 文件主要负责管理应用中的 Action 映射, 以及该 Action 包含的 Result 定义等 struts.xml 内容主要包括 :Action Interceptor Packages 和 Namespace 等, 下面将会详细介绍如何配置这些元素 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="demo" extends="struts-default"> <action name="submit" class="org.xmh.demo.moresubmitaction"> <result name="save">/result.jsp</result> <result name="print">/result.jsp</result> </action> </package> </struts> 1 使用 <include> 标签重用配置文件在 Struts2 中提供了一个默认的 struts.xml 文件, 但如果 package action interceptors 等配置比较多时, 都放到一个 struts.xml 文件不太容易维护 因此, 就需要将 struts.xml 文件分成多个配置文件, 然后在 struts.xml 文件中使用 <include> 标签引用这些配置文件 这样做的优点如下 : 结构更清晰, 更容易维护配置信息 配置文件可以复用 如果在多个 Web 程序中都使用类似或相同的配置文件, 那么可以使用 <include> 标签来引用这些配置文件, 这样可以减少工作量 假设有一个配置文件, 文件名为 newstruts.xml, 代码如下 : <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="demo" extends="struts-default" > <action name="submit" class="action.moresubmitaction"> <result name="save" > /result.jsp </result> <result name="print"> /result.jsp </result> </action> </package> </struts> 则 struts.xml 引用 newstruts.xml 文件的代码如下 : <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC QQ: QQ 群 : Page 20 of 162

21 "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <include file="newstruts.xml"/> <package name="test" extends="struts-default">... </package> </struts> 注意 : 1 用 <include> 引用的 xml 文件也必须是完成的 struts2 的配置 实际上 <include> 在引用时是单独解析 xml 文件, 而不是将被引用的文件插入到 struts.xml 文件中 2struts-default.xml 这个文件被包含在 struts2-core.jar 中, 文件名已经可以看出这个文件的作用是 struts.xml 的默认配置, 它将自动被加载后导入到 struts.xml 中去 2 action 配置在默认情况下,Struts2 会调用动作类的 execute 方法 但有些时候, 需要在一个动作类中处理不同的动作 也就是用户请求不同的动作时, 执行动作类中的不同的方法 为了达到这个目的, 可以在 <action> 标签中通过 method 方法指定要执行的动作类的方法名, 并且需要为不同的动作起不同的名子 ( 也称为别名 ) 如下面代码所示: <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="demo" extends="struts-default" > <action name="test" class="action.myaction">... </action> <action name="my" class="action. MyAction" method="my">... </action> </package> </struts> 上面代码的两个动作的 class 属性都指向同一个类,name 为这个类起了两个动作别名 :test 和 my 在动作 my 中, 使用了 method 属性指定要要运行的方法名为 my 在 MyAction 类中必须要有 my 方法, 代码如下 : package action; import com.opensymphony.xwork2.actionsupport; public class MyAction extends ActionSupport{... public String execute() throws Exception { // 处理 test 动作的代码 public String my() throws Exception { // 处理 my 动作的代码... 除了在 struts.xml 中配置别名, 还可以通过请求参数来描述指定动作 ( 并不需要在 struts.xml 中配置 ) 请求参数的格式如下: QQ: QQ 群 : Page 21 of 162

22 关于通过请求指定动作的详细内容, 请参阅前面的章节中有关处理一个 form 多个 submit 的部分 在 struts2 中还可以为 action 指定一个或多个参数 大家还记着 struts1.x 是如何设置的 action 参数不? 在 struts1.x 中可以使用 <action> 标签的 parameter 属性为其指定一个 action 参数, 如果要指定多个, 就只能通过逗号 (,) 或其他的分隔符将不同的参数隔开 而在 struts2 中可以通过 <param> 标签指定任意多个参数 代码如下 : <action name="submit" class="action.myaction"> <param name="param1">value1</param> <param name="param2">value2</param> <result name="save" > /result.jsp</result>... </action> 当然, 在 action 中读这些参数也非常简单, 只需要象获取请求参数一样在 action 类中定义相应的 setter 方法即可 ( 一般不用定义 getter 方法 ) 如下面的代码将读取 param1 和 param2 参数的值 : package action; import com.opensymphony.xwork2.actionsupport; public class MyAction extends ActionSupport { private String param1; private String param2; public String execute() throws Exception { System.out.println(param1 + param2); public void setparam1(string param1) { this.param1 = param1; public void setparam2(string param2) { this.param2 = param2;... 当 struts2 在调用 execute 之前,param1 和 param2 的值就已经是相应参数的值了, 因此, 在 execute 方法中可以直接使用 param1 和 param2 3 result 配置在默认时,<result> 标签的 type 属性值是 dispatcher ( 实际上就是转发,forward) 开发人员可以根据自己的需要指定不同的类型, 如 redirect stream 等 如下面代码所示 : <result name="save" type="redirect"> /result.jsp</result> 如果第一个 result 的属性省略了,struts2 默认会把当作 success 这此 result-type 可以在 struts2-core jar 包或 struts2 源代码中的 struts-default.xml 文件中找到, 在这个文件中找到 <result-types> 标签, 所有的 result-type 都在里面定义了 代码如下 : <result-types> <result-type name="chain" class="com.opensymphony.xwork2.actionchainresult"/> <result-type name="dispatcher" class="org.apache.struts2.dispatcher.servletdispatcherresult" default="true"/> <result-type name="freemarker" class="org.apache.struts2.views.freemarker.freemarkerresult"/> <result-type name="httpheader" QQ: QQ 群 : Page 22 of 162

23 class="org.apache.struts2.dispatcher.httpheaderresult"/> <result-type name="redirect" class="org.apache.struts2.dispatcher.servletredirectresult"/> <result-type name="redirectaction" class="org.apache.struts2.dispatcher.servletactionredirectresult"/> <result-type name="stream" class="org.apache.struts2.dispatcher.streamresult"/> <result-type name="velocity" class="org.apache.struts2.dispatcher.velocityresult"/> <result-type name="xslt" class="org.apache.struts2.views.xslt.xsltresult"/> <result-type name="plaintext" class="org.apache.struts2.dispatcher.plaintextresult" /> <!-- Deprecated name form scheduled for removal in Struts The camelcase versions are preferred. See ww > <result-type name="redirect-action" class="org.apache.struts2.dispatcher.servletactionredirectresult"/> <result-type name="plaintext" class="org.apache.struts2.dispatcher.plaintextresult" /> </result-types> 有很多时候一个 <result> 被很多 <action> 使用, 这时可以使用 <global-results> 标签来定义全局的 <result>, 代码如下 : <struts> <package name="demo" extends="struts-default"> <global-results> <result name="print">/result.jsp</result> </global-results> <action name="submit" class="action.moresubmitaction">... </action> <action name="my" class="action.moresubmitaction" method="my">... </action> </package> </struts> 如果 <action> 中没有相应的 <result>,struts2 就会使用全局的 <result> 4 拦截器配置 Struts2 的拦截器和 Servlet 过滤器类似 在执行 Action 的 execute 方法之前,Struts2 会首先执行在 struts.xml 中引用的拦截器, 在执行完所有引用的拦截器的 intercept 方法后, 会执行 Action 的 execute 方法 在使用拦截器的时候, 在 Action 里面必须最后一定要引用 struts2 自带的拦截器缺省堆栈 defaultstack, 如下 ( 这里我是引用了 struts2 自带的 checkbox 拦截器 ): <interceptor-ref name="checkbox"> <param name="uncheckedvalue">0</param> </interceptor-ref> <interceptor-ref name="defaultstack"/> ( 必须加, 否则出错 ) 也可以改为对全局 Action 设置自己需要的拦截器, 如下 : 在 struts.xml 里面定义全局的配置设置 <package name="struts-shop" extends="struts-default"> <interceptors> <interceptor-stack name="mystack"> QQ: QQ 群 : Page 23 of 162

24 <interceptor-ref name="checkbox"> <param name="uncheckedvalue">0</param> </interceptor-ref> <interceptor-ref name="defaultstack"/> </interceptor-stack> </interceptors> <default-interceptor-ref name="mystack"/>( 这句是设置所有 Action 自动调用的拦截器堆栈 ) </package> struts-action.xml 里面配置 Action 如下 : <package name="logonadmin" extends="struts-shop">( 这里扩展 struts.xml 里面定义的配置就可以了 ) <action name="logon" class="logonaction"> <result>/jsp/smeishop/admin/index.jsp</result> <result name="error">/jsp/smeishop/admin/logon.jsp</result> <result name="input">/jsp/smeishop/admin/logon.jsp</result> </action> <action name="logout" class="logoutaction"> <result>/jsp/smeishop/admin/logon.jsp</result> </action> </package> 引用名既可以是拦截器名也可以是栈名 5 包配置使用 package 可以将逻辑上相关的一组 Action,Result,Interceptor 等组件分为一组,Package 有些像对象, 可以继承其他的 Package, 也可以被其他 package 继承, 甚至可以定义抽象的 Package Package 的可以使用的属性 : 属性是否必须说明 name 是 Package 的表示, 为了让其他的 package 引用 extends 否从哪个 package 继承行为 namespace 否参考 Namespace 配置说明 abstract 否定义这个 package 为抽象的, 这个 package 中不需要定义 action 由于 struts.xml 文件是自上而下解析的, 所以被继承的 package 要放在继承 package 的前边 Namespace 将 action 分成逻辑上的不同模块, 每一个模块有自己独立的前缀 使用 namespace 可以有效的避免 action 重名的冲突, 例如每一个 package 都可以有自己独立的 Menu 和 Help action, 但是事项方式各有不同 Struts2 标签带有 namespace 选项, 可以根据 namespace 的不同向服务器提交不同的 package 的 action 的请求 / 表示根 namespace, 所有直接在应用程序上下文环境下的请求 (Context) 都在这个 package 中查找 表示默认 namespace, 当所有的 namespace 中都找不到的时候就在这个 namespace 中寻找 例如, 有如下配置 : CODE: <package name="default"> <action name="foo" class="mypackage.simpleaction> <result name="success" type="dispatcher"> greeting.jsp </result> </action> <action name="bar" class="mypackage.simpleaction"> QQ: QQ 群 : Page 24 of 162

25 <result name="success" type="dispatcher">bar1.jsp</result> </action> </package> <package name="mypackage1" namespace="/"> <action name="moo" class="mypackage.simpleaction"> <result name="success" type="dispatcher">moo.jsp</result> </action> </package> <package name="mypackage2" namespace="/barspace"> <action name="bar" class="mypackage.simpleaction"> <result name="success" type="dispatcher">bar2.jsp</result> </action> </package> 1 如果请求为 /barspace/bar.action 查找 namespace:/barspace, 如果找到 bar 则执行对应的 action, 否则将会查找默认的 namespace, 在上面的例子中, 在 barspace 中存在名字为 bar 的 action, 所以这个 action 将会被执行, 如果返回结果为 success, 则画面将定为到 bar2.jsp 2 如果请求为 /moo.action, 则 namespace('/') 被查找, 如果 moo.action 存在则执行, 否则查询默认的 namespace, 上面的例子中, 根 namespace 中存在 moo action, 所以该 action 被调用, 返回 success 的情况下画面将定位到 moo.jsp 3.3 Action 配置 在 struts2 框架中每一个 Action 是一个工作单元 Action 负责将一个请求对应到一个 Action 处理上去, 每当一个 Action 类匹配一个请求的时候, 这个 Action 类就会被 Struts2 框架调用 一个 Action 配置示例 : <action name="logon" class="tutorial.logon"> <result type="redirect-action">menu</result> <result name="input">/tutorial/logon.jsp</result> </action> 每一个 Action 可以配置多个 result, 多个 ExceptionHandler, 多个 Intercepter, 但是只能有一个 name, 这个 name 和 package 的 namespace 来唯一区别一个 Action 每当 struts2 框架接受到一个请求的时候, 它会去掉 Host,Application 和后缀等信息, 得到 Action 的名字, 如下的请求将得到 Welcome 这个 Action /Welcome.action 在一个 Struts2 应用程序中, 一个指向 Action 的链接通常有 Struts Tag 产生, 这个 Tag 只需要指定 Action 的名字,Struts 框架会自动添加诸如后缀等的扩展, 例如 : <s:form action="hello"> <s:textfield label="please enter your name" name="name"/> <s:submit/> </s:form 将产生一个如下的链接的请求 : 在定义 Action 的名字的时候不要使用. 和 /, 最好使用英文字母和下划线 Action 的默认入口方法由 xwork2 的 Action 接口来定义, 代码清单为 : public interface Action { public String execute() throws Exception; QQ: QQ 群 : Page 25 of 162

26 有些时候我们想指定一个 Action 的多个方法, 我们可以做如下两步 : 步骤 1 建立一些 execute 签名相同的方法, 例如 : Public String forward() throws Exception 步骤 2 在 Action 配置的时候使用 method 属性, 例如 : <action name="delete" class="example.crudaction" method="delete"> 默认情况下, 当请求 HelloWorld.action 发生时,Struts 运行时 (Runtime) 根据 struts.xml 里的 Action 映射集 (Mapping), 实例化 tutoiral.helloworld 类, 并调用其 execute 方法 当然, 可以通过以下两种方法改变这种默认调用 这个功能 (Feature) 有点类似 Struts 1.x 中的 LookupDispathAction 1 在 classes/sturts.xml 中新建 Action, 并指明其调用的方法 ; 2 访问 Action 时, 在 Action 名后加上!xxx (xxx 为方法名 ) 实现方法请参考例 2: 在 classes/ struts2demo/helloworld.java 中加入以下方法 : public String aliasaction() { message =" 自定义 Action 调用方法 "; return SUCCESS; 在 classes/sturts.xml 中加入下面代码 : <action name="aliashelloworld" class="org.xmh.demo.helloworld" method="aliasaction"> <result>/helloworld.jsp</result> </action> 使用 地址来访问 HelloWorld Action 在浏览器地址栏中键入 或 struts2demo /HelloWorld!aliasAction.action, 可以看到如图 2 所示页面 通过上面的个例子, 细心的朋友应该可能会发现 classes/struts2demo/helloworld.java 中 Action 方法 (execute 和 aliasaction) 返回都是 SUCCESS 这个属性变量并没有定义, 所以大家应该会猜到它在 ActionSupport 或其父类中定义 没错,SUCCESS 在接口 com.opensymphony.xwork2.action 中定义, 另外同时定义的还有 ERROR, INPUT, LOGIN, NONE 此外, 在配置 Action 时都没有为 result 定义名字 (name), 所以它们默认都为 success 值得一提的是 Struts 2.0 中的 result 不仅仅是 Struts 1.x 中 forward 的别名, 它可以实现除 forward 外的很激动人心的功能, 如将 Action 输出到 FreeMaker 模板 Velocity 模板 JasperReports 和使用 XSL 转换等 这些都过 result 里的 type( 类型 ) 属性 (Attribute) 定义的 另外, 您还可以自定义 result 类型 下面让我们来做一个 Velocity 模板输出的例子, 首先在 classes/struts.xml 中新建一个 Action 映射 (Mapping), 将其 result 类型设为 velocity, 如以下代码所示 : <action name="vmhelloworld" class="org.xmh.demo.helloworld"> QQ: QQ 群 : Page 26 of 162

27 <result type="velocity">/helloworld.vm</result> </action> 新建 HelloWorld.vm, 内容如下所示 : <html> <head> <title>velocity</title> <meta http-equiv="content-type" content="text/html; charset=utf-8"> </head> <body> <h2>message rendered in Velocity: $message</h2> </body> </html> 注意在使用 Velocity 模板的时候需要导入相关的包, 可以在其官方网站 下载 也可以直接打开它自带 showcase 案例, 在其 lib 中拷贝相关的 jar 文件 在 IE 地址栏中键入 页面输出如下图所示 如果想对 Velocity 做深入了解, 请参见我发布的相关文档 Action 中的方法通配符有些时候对 Action 中方法的调用满足一定的规律, 例如 edit Action 对应 edit 方法,delete Action 对应 delete 方法, 这个时候我们可以使用方法通配符, 例如 : QQ: QQ 群 : Page 27 of 162

28 <action name="*crud" class="example.crud" method="{1"> 这时,editCrudAction 的引用将调 用 edit 方法, 同理,deleteCrudAction 的引用将调用 delete 方法 另外一种比较常用的方式是使用下划线分割, 例如 : <action name="crud_*" class="example.crud" method="{1"> 这样当遇到如下调用的时候可以找到对应的方法 "action=crud_input" => input 方法 "action=crud_delete" => delete 方法通配符和普通的配置具有相同的地位, 可以结合使用框架的所有其他功能 默认的 Action 当我们没有指定 Action 的 class 属性的时候, 默认使用 com.opensymphony.xwork.actionsupport ActionSupport 有两个方法 input 和 execute, 每个方法都是简单的返回 SUCCESS 通常情况下, 请求的 Action 不存在的情况下,Struts2 框架会返回一个 Error 画面 : Page not found, 有些时候或许我们不想出现一个控制之外的错误画面, 我们可以指定一个默认的 Action, 在请求的 Action 不存在的情况下, 调用默认的 Action, 通过如下配置可以达到要求 : <package name="hello" extends="action-default"> <default-action-ref name="underconstruction"> <action name="underconstruction"> <result>/underconstruction.jsp</result> </action> <action name="*" > <result>/{1.jsp</result> </action> 每个 Action 将会被映射到以自己名字命名的 JSP 上 第 4 章表单验证 前面, 花了不少的时间讨论 Action 的输出 其实 web 程序搞来搞去, 有何新意? 程序无非就是输入 操作和输出 因此, 接下来讨论一下输入 表单输入 使用 Struts 2.0, 表单数据的输入将变得非常方便, 和普通的 POJO 一样在 Action 编写 Getter 和 Setter, 然后在 JSP 的 UI 标志的 name 与其对应, 在提交表单到 Action 时, 就可以取得其值 让我们看一个例子, 新建 Login Action, 它通过 Login.jsp 的表单获得用户名和密码, 验查用户名是否为 xmh, 密码是否则为 xmh 如果, 两者都符合, 就在 HelloWorld 中显示 Welcome, xmh, 否则显示 Invalid user or Password package org.xmh.demo; import com.opensymphony.xwork2.actionsupport; public class Login extends ActionSupport { private String name; private String password; private String message; 省略 set/get 方法 public String getmessage() { return message; QQ: QQ 群 : Page 28 of 162

29 @Override public String execute() { if ("xmh".equals(name) && "xmh".equals(password)) { message = "Welcome, " + name; else { message = "Invalid user or password"; return SUCCESS; <%@ page contenttype="text/html; charset=utf-8" %> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>login</title> </head> <body> <s:form action="login" method="post"> <s:textfield name="name" label=" 用户名 "/> <s:password name="password" label=" 密码 "/> <s:submit value=" 登录 "/> </s:form> </body> </html> 出现如图 4 所示页面 分别在 User name 中输入 xmh 和 xmh, 点击 登录 按钮, 出现如图所示页面 图 5 Login 成功页面在浏览器地址栏中键入 分别在 User name 中输入 123 和 123, 点击 登录 按钮, 出现如图所示页面 QQ: QQ 群 : Page 29 of 162

30 Struts 2.0 支持更高级的 POJO 访问, 如 user.getpassword() 可以用另一写法实现 首先, 将 name 和 password 从 Login 类中分离出来, 到新建类 User 中 这样对开发多层系统尤其有用 它可以使系统结构更清晰 在上例的基础上调整如下 : <%@ page contenttype="text/html; charset=utf-8" %> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>login</title> </head> <body> <s:form action="login" method="post"> <s:textfield name="user.name" label=" 用户名 "/> <s:password name="user.password" label=" 密码 "/> <s:submit value=" 登录 "/> </s:form> </body> </html> package org.xmh.demo; public class User { private String name; private String password; 省略相应的 set/get 方法 package org.xmh.demo; import com.opensymphony.xwork2.actionsupport; public class Login extends ActionSupport { private User user; private String message; public String getmessage() { return public String execute() { if ("xmh".equals(user.getname()) && "xmh".equals(user.getpassword())) { message = "Welcome, " + user.getname(); QQ: QQ 群 : Page 30 of 162

31 else { message = "Invalid user or password"; return SUCCESS; public User getuser() { return user; public void setuser(user user) { this.user = user; 4.1 手动完成输入校验 1. 重写 validate() 方法也是把校验条件写在 validate() 方法里面. 这样可以减少 execute() 或是自定义的 action 方法的代码.struts2 在调用 execte() 方法时, 会先执行 validate() 方法. 如果没通过 validate 则会抛出 fielderror, 并返回 name="input" 中定义的视图. 否则继续执行 execute() 方法. <%@ page language="java" import="java.util.*" pageencoding="gbk"%> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title> 验证数据 </title> </head> <body> <s:actionerror /> <s:actionmessage /> <s:form action="validate.action" theme="simple"> 输入内容 :<s:textfield name="msg" /> <s:fielderror> <s:param>msg.hello</s:param> </s:fielderror> <br /> <s:submit value=" 登录 " /> <s:reset value=" 重填 " /> </s:form> QQ: QQ 群 : Page 31 of 162

32 </body> </html> package org.xmh.demo; import com.opensymphony.xwork2.actionsupport; public class ValidateAction extends ActionSupport { private String msg; public String execute() { System.out.println(SUCCESS); return SUCCESS; public void validate() { if (!msg.equalsignorecase("hello")) { System.out.println(INPUT); this.addfielderror("msg.hello", " 必须输入 hello!"); this.addactionerror(" 处理动作失败!"); else { this.addactionmessage(" 提交成功 "); public String getmsg() { return msg; public void setmsg(string msg) { this.msg = msg; <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC QQ: QQ 群 : Page 32 of 162

33 "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="demo" extends="struts-default"> <action name="validate" class="org.xmh.demo.validateaction"> <result name="success">/validator.jsp</result> <result name="input">/validator.jsp</result> </action> </package> </struts> 2. 重写 validatexxx() 方法如果输入校验只想校验某个处理逻辑, 也就是仅仅校验某个处理方法, 则重写 validate 方法显然不够,validate() 方法无法知道需要校验哪个处理逻辑 实际上, 如果重写了 Action 的 validate 方法, 则该方法会校验所有的处理逻辑 为了实现校验指定处理逻辑的功能,struts2 的 Action 提供了一个 validatexxx 方法 XXX 即是 Action 对应的处理逻辑方法 例 : public void validatesaveorder(){ System.out.println("\n test "); if(ordermain.getordertypeid() == 0){ addfielderror("test","test"); package org.xmh.demo; import java.util.regex.pattern; import com.opensymphony.xwork2.actionsupport; public class NewValidateAction extends ActionSupport { private String msg; // 必须输入 private Integer age; // 在 13 和 20 之间 省略相应的 set/get 方法 public void validateregist(){ System.out.println("\\d 匹配数字 "+"8".matches("\\d")); if(msg==null "".equals(msg)){ addfielderror("msg","validatexxx 方法 : 用户名为必填项 "); QQ: QQ 群 : Page 33 of 162

34 if(age==null age<13 age>20){ addfielderror("age","validatexxx 方法 : 年龄为必填项并且为数字, 范围在 间 "); public String regist(){ return SUCCESS; <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="demo" extends="struts-default"> <action name="new_validate" class="org.xmh.demo.newvalidateaction" method="regist"> <result name="input">/validate_form.jsp</result> <result name="success">/validate_form.jsp</result> </action> </package> </struts> 基本输入校验使用 validate 方法来验证客户端提交的数据, 但如果使用 validate 方法就会将验证代码和正常的逻辑代码混在一起, 但这样做不利于代码维护, 而且也很难将过些代码用于其他程序的验证 在 Struts2 中为我们提供了一个 Validation 框架, 这个框架和 Struts1.x 提供的 Validation 框架类似, 也是通过 XML 文件进行配置 4.2 struts2 框架实现数据校验 服务端验证下面将给出一个例子来演示如何使用 Struts2 的 validation 框架来进行服务端验证 我们可以按着如下四步来编写这个程序 : 第 1 步 建立 Action 类创建 action 类, 重写 execute() 方法. 例如 : ValidationAction.java import com.opensymphony.xwork2.actionsupport; public class ValidationAction extends ActionSupport { private String requiredstring; public String getrequiredstring() { return requiredstring; QQ: QQ 群 : Page 34 of 162

35 public void setrequiredstring(string requiredstring) { this.requiredstring = public String execute() { return SUCCESS; 下面我们来验证 msg 和 age 属性 第 2 步 编写验证规则配置文件这是一个基于 XML 的配置文件, 和 struts1.x 中的 validator 框架的验证规则配置文件类似 但一般放到和要验证的.class 文件在同一目录下, 而且配置文件名要使用如下两个规则中的一个来命名 : <ActionClassName>-validation.xml <ActionClassName>-<ActionAliasName>-validation.xml 其中 <ActionAliasName> 就是 struts.xml 中 <ation> 的 name 属性值 在本例中我们使用第一种命名规则, 所以文件名是 NewValidateAction-validation.xml 文件的内容如下: 在 action 的包下创建 action-validation.xml 文件. 例 :ValidationAction-validation.xml( 黑色部分代表相应的 action 类 ) <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" " > <validators> <field name ="requiredstring" > <field-validator type ="requiredstring"> <message> This string is required 必填字段 </message> </field-validator> </field> </validators> 这个文件使用了两个规则 :requiredstring( 必须输入 ) 和 int( 确定整型范围 ) 关于其他更详细的验证规则, 请读者访问 docs/validation.html 来查看 第 3 步 配置 Action 类,struts.xml 的代码如下 : 在 struts.xml 定义 action 类 <action name ="ValidationAction" class ="com.xishi.action.validationaction" > <result>output.jsp</result > <result name ="input">input.jsp</result> </action> 注 :input 参数代表校验失败应返回的页面. 在 Input.jsp 页面增加 <s:fielderror/>, 这个是必需的, 否则不会提示. 注 :<s:fielderror/> 的用法 : <s:fielderror/> 表示显示全部校验信息 <s:fielderror><s:param> 字段 1</s:param></s:fielderror> 表示只显示字段 1 的校验信息至于 Input.jsp Output.jsp 就是普通 struts2 页面. 第 4 步 编写数据录入 JSP 页 在 Web 根目录中建立一个 validate_form.jsp 文件, 代码如下 : QQ: QQ 群 : Page 35 of 162

36 在上面的程序中还使用了一个 styles.css 来定制错误信息的风格 具体代码参见 styles.css 文件 使用如下的 URL 来测试这个程序 : package org.xmh.demo; import com.opensymphony.xwork2.actionsupport; public class NewValidateAction extends ActionSupport { private String msg; // 必须输入 private int age; // 在 13 和 20 之间 省略相应的 set/get 方法 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="demo" extends="struts-default"> <action name="new_validate" class="org.xmh.demo.newvalidateaction"> <result name="input">/validate_form.jsp</result> <result name="success">/validate_form.jsp</result> </action> </package> </struts> QQ: QQ 群 : Page 36 of 162

37 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" " <validators> <field name="msg"> <field-validator type="requiredstring"> <message> 请输入信息 </message> </field-validator> </field> <field name="age"> <field-validator type="int"> <param name="min">13</param> <param name="max">20</param> <message> 必须在 13 至 20 之间 </message> </field-validator> </field> </validators> page language="java" import="java.util.*" pageencoding="gbk"%> taglib prefix="s" uri="/struts-tags"%> <link rel="stylesheet" type="text/css" href="<s:url value="/styles.css"/>"> <html> <head> <title> 验证数据 </title> </head> <body> <s:form action="new_validate"> <s:textfield name="msg" label=" 姓名 " /> <s:textfield name="age" label=" 年龄 " /> <s:submit /> </s:form> </body> </html> 总结 :struts2 数据校验是在 web server 上的实现, 因此有可能增加 web server 的负荷 所以纯粹 的空值校验, 可以用 JavaScript 实现的 当然如果客户端禁用 Javascript 或是不支持 JavaScript, 那就是另外一回事了 另外使用 struts 作为数据校验, 应尽可能地使用框架进行校验, 可以实现 action 与 validation 分离, 减少代码量 只有在校验框架满足不了需求时, 才要重写 validate() 方法. 最后的选择是重写 validatexxx() 方法 第 5 章国际化实现 有关国际化问题, 在 jsp 和 struts 的学习中都为之做了详细的讲解 此处不再赘述 因为程序国际化是一个企业应用必须实现的功能, 所以还是希望读者朋友要重视本章节的学习 Struts 2 提供了很好的程序国际化支持 QQ: QQ 群 : Page 37 of 162

38 程序国际化的设计的主要思想是 : 程序界面中需要输出国际化信息的地方, 不要在页面中直接输出信息, 而是输出一个 key 值, 该 key 值在不同语言环境下对应不同的字符串 当程序需要显示时, 程序将根据不同的语言环境, 加载该 key 对应该语言环境下的字符串 这样就可以完成程序的国际化 5.1 页面的国际化 Struts2 的国际化实现更加方便, 下面通过修改 login 示例来说明它的相关用法 步骤 1 准备资源属性文件 在 src 目录下新建一个 local 包, 把所有的资源文件放置其中, 这里只创建两种语言的资源文件 : 英文和中文的资源文件 如下图所示 message_en_us.properties 的内容如下所示 : login.name=name login.pass=pass login.submit=submit login.succmessage=welcome login.erromessage=invalid user or password 步骤 2: 页面上使用国际化功能 struts2 支持在 JSP 页面中临时加载资源文件, 也支持通过全局属性来加载资源文件, 全局属性加载资源文件就是通过 struts.properties 文件来完成 1 在页面中临时加载 <%@ page contenttype="text/html; charset=utf-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>login</title> </head> <body> <s:i18n name="local.message"> <s:form action="login" method="post"> <s:textfield name="user.name" label="%{gettext('login.name')" /> <s:password name="user.password" label="%{gettext('login.pass')" /> <s:submit value="%{gettext('login.submit')" /> QQ: QQ 群 : Page 38 of 162

39 </s:form> </s:i18n> </body> </html> 2 全局加载在 classes 文件夹下新建 Struts.properties 文件, 设置全局属性加载资源的路径, 如下图所示 或者打开 struts.xml, 在 <struts> 的下面加如下一行配置 : <constant name="struts.custom.i18n.resources" value="message" /> 页面部分就毋须再使用 <s:i18n/> 标签, 上面的页面部分就修改为 : <%@ page contenttype="text/html; charset=utf-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>login</title> </head> <body> <s:form action="login" method="post"> <s:textfield name="user.name" label="%{gettext('login.name')" /> <s:password name="user.password" label="%{gettext('login.pass')" /> <s:submit value="%{gettext('login.submit')" /> </s:form> </body> </html> 发布应用程序, 在 IE 中输入访问地址, 调整 IE 所用的语言, 当首选语言分别设置为 中文 和 英文 时, 显示的效果图如下所示 注意 : 如果在 struts 的属性文件中设置 struts.locale=zh_cn 或 struts.local=en_us 时, 不需要再调整 IE 所用的语言也可以获得如上的效果 5.2 Action 的国际化 在 struts2 中, 当由一个 Action 跳转到一个页面中时, 就可以在该页面获取这个 Action 的资源包中信息字符串 比如在登录这个示例中, 如果用户登录成功应该返回欢迎的提示信息, 否则就提示用户名或密码错误的提示信息, 前面是直接将字符串硬编码在程序中, 现在要用到国际化, QQ: QQ 群 : Page 39 of 162

40 置于资源文件中, 然后在 Action 中取出所对应的字符串值存储于变量中即可 package org.xmh.demo; import com.opensymphony.xwork2.actionsupport; public class Login extends ActionSupport { private User user; private String message; public String getmessage() { return public String execute() { if ("xmh".equals(user.getname()) && "xmh".equals(user.getpassword())) { message = gettext("login.succmessage")+", " + user.getname(); else { message = gettext("login.erromessage"); return SUCCESS; public User getuser() { return user; public void setuser(user user) { this.user = user; 发布应用程序, 在 IE 中输入访问地址, 调整 IE 所用的语言, 登录成功时中文和英文显示的效果如下图所示 登录失败时中文和英文显示的效果如下图所示 QQ: QQ 群 : Page 40 of 162

41 5.3 验证信息的国际化 在前一章节学习了验证, 怎么实现验证信息的国际化呢? 修改资源文件, 内容如下 : login.name=name login.pass=pass login.submit=submit login.succmessage=welcome login.erromessage=invalid user or password error.username=user's name should not be null error.userpass=password should not be null 修改配置文件 : <struts> <package name="tutorial" extends="struts-default"> <action name="login" class="org.xmh.demo.login"> <result name="success">/helloworld.jsp</result> <result name ="input">login.jsp</result> </action> </package> </struts> 在 Action 的同级目录添加 Login-validation.xml 文件 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" " > <validators> <field name ="user.name" > <field-validator type ="requiredstring"> <message key="error.username">not null</message> </field-validator> </field> <field name ="user.password" > <field-validator type ="requiredstring"> <message key="error.userpass">not null</message> </field-validator> </field> </validators> 发布项目, 运行 tomcat, 输入访问 URL, 然后直接用鼠标单击登录按钮, 然后再调整 IE 首选语言, 效果如下图所示 : QQ: QQ 群 : Page 41 of 162

42 注意 : 国际化文件的查找顺序假设我们在某个 ChildAction 中调用了 gettext("user.title"),struts 2.0 的将会执行以下的操作 : 1 查找 ChildAction_xx_XX.properties 文件或 ChildAction.properties; 2 查找 ChildAction 实现的接口, 查找与接口同名的资源文件 MyInterface.properties; 3 查找 ChildAction 的父类 ParentAction 的 properties 文件, 文件名为 ParentAction.properties; 4 判断当前 ChildAction 是否实现接口 ModelDriven 如果是, 调用 getmodel() 获得对象, 查找与其同名的资源文件 ; 5 查找当前包下的 package.properties 文件 ; 6 查找当前包的父包, 直到最顶层包 ; 7 在值栈(Value Stack) 中, 查找名为 user 的属性, 转到 user 类型同名的资源文件, 查找键为 title 的资源 ; 8 查找在 struts.properties 配置的默认的资源文件, 参考例 1; 9 输出 user.title 第 6 章拦截器浅析 6.3 拦截器基础 Struts2 的拦截器和 Servlet 过滤器类似 在执行 Action 的 execute 方法之前,Struts2 会首先执行在 struts.xml 中引用的拦截器, 在执行完所有引用的拦截器的 intercept 方法后, 会执行 Action 的 execute 方法 Struts2 拦截器类必须从 com.opensymphony.xwork2.interceptor.interceptor 接口继承, 在 Intercepter 接口中有如下三个方法需要实现 : void destroy(); void init(); String intercept(actioninvocation invocation) throws Exception; 其中 intercept 方法是拦截器的核心方法, 所有安装的拦截器都会调用之个方法 在 Struts2 中已经在 struts-default.xml 中预定义了一些自带的拦截器, 如 timer params 等 如果在 <package> 标签中继承 struts-default, 则当前 package 就会自动拥有 struts-default.xml 中的所有配置 代码如下 : <package name="demo" extends="struts-default" >... </package> 在 struts-default.xml 中有一个默认的引用, 在默认情况下 ( 也就是 <action> 中未引用拦截器时 ) 会自动引用一些拦截器 这个默认的拦截器引用如下 : QQ: QQ 群 : Page 42 of 162

43 <default-interceptor-ref name="defaultstack"/> <interceptor-stack name="defaultstack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servletconfig"/> <interceptor-ref name="prepare"/> <interceptor-ref name="i18n"/> <interceptor-ref name="chain"/> <interceptor-ref name="debugging"/> <interceptor-ref name="profiling"/> <interceptor-ref name="scopedmodeldriven"/> <interceptor-ref name="modeldriven"/> <interceptor-ref name="fileupload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="staticparams"/> <interceptor-ref name="params"> <param name="excludeparams">dojo\..*</param> </interceptor-ref> <interceptor-ref name="conversionerror"/> <interceptor-ref name="validation"> <param name="excludemethods">input,back,cancel,browse</param> </interceptor-ref> <interceptor-ref name="workflow"> <param name="excludemethods">input,back,cancel,browse</param> </interceptor-ref> </interceptor-stack> 上面在 defaultstack 中引用的拦截器都可以在 <action> 中不经过引用就可以使用 ( 如果在 <action> 中引用了任何拦截器后, 要使用在 defaultstack 中定义的拦截器, 也需要在 <action> 中重新引用, 在后面将详细讲解 ) 6.2 使用拦截器 Struts2 中自带的默认拦截器栈已经能够满足大多数应用的需要了, 大多数的应用都不必再增加自己的拦截器或者改变已有的拦截器栈 在 Struts2 中, 许多 action 通常都有一些共同的需要关心的问题, 比如有一些 action 它们都需要对页面上的输入进行验证, 有一些 action 需要对上传的文件进行一下预处理, 还有一些 action 可能需要防止重复提交 (double submit), 还有很多 action 需要在页面显示之前将下拉列表和者其它的一些控件事先装好值 通过使用 拦截器 策略,Struts2 框架使得共享这些问题的解决方案变得十分容易 当请求一个 action 的时候, 框架会调用这个 action 对象, 但是, 在 action 执行之前, 这个调用可能会被另外一个对象所拦截, 在 action 执行之后, 这个调用可能被一个对象再次拦截, 如果读者朋友对 AOP 有所了解, 则一切都会变得简单明了 拦截器能在 action 被调用之前和被调用之后执行一些 代码 Struts2 框架的大部分核心功能都是通过拦截器来实现的, 如防止重复提交 类型转换 对象封装 校验 文件上传 页面预装载等等, 都是在拦截器的帮助下实现的 每一个拦截器都是独立装载的 (pluggable), 我们可以根据实际的需要为每一个 action 配置它所需要的拦截器, 例如, 一个 action 需要用来类型装换 文件上传, 那么我们可以给它设置相应的两个拦截器 QQ: QQ 群 : Page 43 of 162

44 在有些情况下, 拦截器可能阻止一个 action 执行, 例如重复提交的情况下或者校验没有通过的情况下, 除此之外, 拦截器还能在一个 action 执行之前改变它的状态 拦截器被定义在栈中, 这个栈指定了拦截器的执行顺序, 在有些情况下, 拦截器在栈中的顺序是非常重要的 下面是一个简单的拦截器配置的例子 : 要实现一个日志的功能, 在某个 Action 执行的前后分别打印出相关的日志信息 在 struts2 中已经提供了一个日志的拦截器, 如下图所示 这里仍然以登录的程序 login 为例, 来实同此功能 修改 Action 文件 : public String execute() { try { System.out.println("======= 这是正常输出内容 ======="); catch (Exception e) { // TODO Auto-generated catch block e.printstacktrace(); // 延迟 1 秒 return SUCCESS; 修改配置文件 : <struts> <package name="tutorial" extends="struts-default"> <action name="login" class="org.xmh.demo.login"> <interceptor-ref name="logger" /> <result>/helloworld.jsp</result> </action> </package> </struts> QQ: QQ 群 : Page 44 of 162

45 运行程序, 在 IE 中输入 控制台显示的效果如下图所示 : 图中用红线标注的地方就是日志信息, 下面通过源代码来分析一下相应的功能 public class LoggingInterceptor extends AbstractInterceptor { private static final Log log = LogFactory.getLog(LoggingInterceptor.class); private static final String FINISH_MESSAGE = "Finishing execution stack for action "; private static final String START_MESSAGE = "Starting execution stack for action "; public String intercept(actioninvocation invocation) throws Exception { logmessage(invocation, FINISH_MESSAGE); String result = invocation.invoke(); logmessage(invocation, START_MESSAGE); return result; private void logmessage(actioninvocation invocation, String basemessage) { if (log.isinfoenabled()) { StringBuffer message = new StringBuffer(baseMessage); String namespace = invocation.getproxy().getnamespace(); if ((namespace!= null) && (namespace.trim().length() > 0)) { message.append(namespace).append("/"); message.append(invocation.getproxy().getactionname()); log.info(message.tostring()); 6.3 自定义拦截器 上例学习了 struts 提供的拦截器, 本小节将学习如何自定义一个拦截器 在 JSP 的学习过程中, 曾处理过如何防止非法用户登录的示例 下面将使用拦截器来改写这个示例 当用户访问 login.jsp 页面, 并输入正确的用户名和密码时, 可以到注册成功页, 并且可以访问 content.jsp 页面内容 如果用户未登录直接访问 loginaction 或 TestInterceptor.action 则返回到 login.jsp 页面 QQ: QQ 群 : Page 45 of 162

46 自定义拦截器的步骤如下 : 步骤 1 编写拦截器代码 package org.xmh.demo; import java.util.map; import org.apache.struts2.interceptor.sessionaware; import com.opensymphony.xwork2.actionsupport; public class LoginAction extends ActionSupport implements SessionAware { private static final long serialversionuid = 1L; private UserBean userbean; private Map<String, String> session; public UserBean getuserbean() { return userbean; public void setuserbean(userbean userbean) { this.userbean = public String execute() { if (userbean == null) { // 这里的 LOGIN 对应 Action.java 中的 //public static final String LOGIN = "login"; return LOGIN; String name = userbean.getname(); String pwd = userbean.getpassword(); if (name.equals("xmh") && pwd.equals("xmh")) { session.put("login", name); return SUCCESS; else { return ERROR; public void setsession(map session) { this.session = session; QQ: QQ 群 : Page 46 of 162

47 UserBean 的代码比较简单, 清单如下所示 : package org.xmh.demo; public class UserBean { private String name; private String password; public String getname() { return name; public void setname(string name) { this.name = name; public String getpassword() { return password; public void setpassword(string password) { this.password = password; LoginAction 的代码清单如下所示 : package org.xmh.demo; import java.util.map; import org.apache.struts2.interceptor.sessionaware; import com.opensymphony.xwork2.actionsupport; public class LoginAction extends ActionSupport implements SessionAware { private static final long serialversionuid = 1L; private UserBean userbean; private Map<String, String> session; public UserBean getuserbean() { return userbean; public void setuserbean(userbean userbean) { this.userbean = public String execute() { if (userbean == null) { return LOGIN; String name = userbean.getname(); String pwd = userbean.getpassword(); if (name.equals("xmh") && pwd.equals("xmh")) { session.put("login", name); QQ: QQ 群 : Page 47 of 162

48 return SUCCESS; else { return ERROR; public void setsession(map session) { this.session = session; TestInterceptorAction 的代码清单如下所示 : public class TestInterceptorAction extends ActionSupport { private static final long serialversionuid = public String execute() { return SUCCESS; 步骤 2 配置拦截器 : <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <!-- 加载默认的 struts2 配置文件 --> <include file="struts-default.xml" /> <!-- 继承默认的 struts2 配置文件 --> <package name="default" extends="struts-default"> <!-- 定义一个名为 checklogin 的拦截器 --> <interceptors> <!-- 定义权限检查拦截器 --> <interceptor name="checklogin" class="org.xmh.demo.checklogininterceptor" /> <!-- 定义一个包含权限检查的拦截器栈 --> <interceptor-stack name="mydefaultstack"> <!-- 定义拦截器栈包含 checklogin 拦截器 --> <interceptor-ref name="checklogin"></interceptor-ref> <interceptor-ref name="defaultstack"></interceptor-ref> </interceptor-stack> </interceptors> <!-- 设置全局全局默认的拦截器栈 --> <default-interceptor-ref name="mydefaultstack"></default-interceptor-ref> <!-- 定义全局 Result --> <global-results> <!-- 当返回 login 视图名时, 转入 /login.jsp 页面 --> <result name="login">/login.jsp</result> </global-results> QQ: QQ 群 : Page 48 of 162

49 <!-- action 标签里 name 属性代表我们要处理的.action 的前面部分 action 标签里 class 属性代表我们需要哪个类来处理 result 标签的 name 属性代表 action 类的执行方法的返回值, action 类的默认执行方法是 public String execute() --> <action name="login" class="org.xmh.demo.loginaction"> <result name="success">success.jsp</result> <result name="error">error.jsp</result> <result name="login">login.jsp</result> <!-- 拦截器一般配置在 result 元素之后 --> </action> <action name="testinterceptor" class="org.xmh.demo.testinterceptoraction"> <result name="success">content.jsp</result> <result name="login">login.jsp</result> </action> </package> </struts> XML 中的 <default-interceptor-ref name="defaultstack"/> 就是对 interceptor 或者是 interceptor 栈的引用 从 tag 的命名 default-interceptor-ref 可以知道这个 interceptor(interceptor 栈 ) 是默认的设置, 也就是所有的 action 在没有定义自己特有的 interceptor 引用的时候, 都会使用这个默认的 interceptor 引用 ; 其它页面的代码请参看清代码部分 步骤 3 运行示例, 查看效果在 IE 中输入访问地址, 输入正确的帐号和密码, 则可以获得如下图所示的效果 6.4 综合示例 第 7 章探讨 Ioc 模式 很多时候我的同事会问我 : 如果我要取得 Servlet API 中的一些对象, 如 request response 或 session 等, 应该怎么做? 这里的 execute 不像 Struts 1.x 的那样在参数中引入 开发 Web 应用程序 QQ: QQ 群 : Page 49 of 162

50 当然免不了跟这些对象打交道 在 Strutx 2.0 你可以有两种方式获得这些对象 : 非 IoC( 控制反转 Inversion of Control) 方式和 IoC 方式 非 IoC 方式要获得上述对象, 关键 Struts 2.0 中 com.opensymphony.xwork2.actioncontext 类 我们可以通过它的静态方法 getcontext() 获取当前 Action 的上下文对象 另外, org.apache.struts2.servletactioncontext 作为辅助类 (Helper Class), 可以帮助您快捷地获得这几个对象 HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); HttpSession session = request.getsession(); 如果你只是想访问 session 的属性 ( Attribute ), 你也可以通过 ActionContext.getContext().getSession() 获取或添加 session 范围 (Scoped) 的对象 IoC 方式要使用 IoC 方式, 我们首先要告诉 IoC 容器 (Container) 想取得某个对象的意愿, 通过实现相应的接口做到这点 package org.xmh.demo; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; import javax.servlet.http.httpsession; import org.apache.struts2.servletactioncontext; import com.opensymphony.xwork2.actioncontext; import com.opensymphony.xwork2.actionsupport; public class NonIoCServlet extends ActionSupport { private String message; public String getmessage() { return public String execute() { ActionContext.getContext().getSession().put("msg", "Hello World from Session!"); HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); HttpSession session = request.getsession(); StringBuffer sb =new StringBuffer("Message from request: "); sb.append(request.getparameter("msg")); sb.append("<br>response Buffer Size: "); sb.append(response.getbuffersize()); sb.append("<br>session ID: "); sb.append(session.getid()); message = sb.tostring(); QQ: QQ 群 : Page 50 of 162

51 return SUCCESS; package org.xmh.demo; import java.util.map; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; import javax.servlet.http.httpsession; import org.apache.struts2.interceptor.servletrequestaware; import org.apache.struts2.interceptor.servletresponseaware; import org.apache.struts2.interceptor.sessionaware; import com.opensymphony.xwork2.actioncontext; import com.opensymphony.xwork2.actionsupport; public class IoCServlet extends ActionSupport implements SessionAware, ServletRequestAware, ServletResponseAware { private String message; private Map att; private HttpServletRequest request; private HttpServletResponse response; public String getmessage() { return message; public void setsession(map att) { this.att = att; public void setservletrequest(httpservletrequest request) { this.request = request; public void setservletresponse(httpservletresponse response) { this.response = public String execute() { att.put("msg", "Hello World from Session!"); HttpSession session = request.getsession(); StringBuffer sb =new StringBuffer("Message from request: "); sb.append(request.getparameter("msg")); sb.append("<br>response Buffer Size: "); sb.append(response.getbuffersize()); QQ: QQ 群 : Page 51 of 162

52 sb.append("<br>session ID: "); sb.append(session.getid()); message = sb.tostring(); return SUCCESS; <%@ page contenttype="text/html; charset=utf-8" %> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>hello World!</title> </head> <body> <h2> <s:property value="message" escape="false"/> <br>message from session: <s:property value="#session.msg"/> </h2> </body> </html> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="xmh" extends="struts-default"> <action name="noniocservlet" class="org.xmh.demo.noniocservlet"> <result>/servlet.jsp</result> </action> <action name="iocservlet" class="org.xmh.demo.iocservlet"> <result>/servlet.jsp</result> </action> </package> </struts> 在 struts1.x Action 类的 execute 方法中, 有四个参数, 其中两个就是 response 和 request 而在 Struts2 中, 并没有任何参数, 因此, 就不能简单地从 execute 方法获得 HttpServletResponse 或 HttpServletRequest 对象了 但在 Struts2 Action 类中仍然有很多方法可以获得这些对象 下面就列出四种获得这些对象的方法 方法 1 使用 Struts2 Aware QQ: QQ 群 : Page 52 of 162

53 这种方法需要 Action 类实现相应的拦截器接口 如我们要获得 HttpServletResponse 对象, 需要 实现 org.apache.struts2.interceptor.servletresponseaware 接口, 代码如下 : package action; import com.opensymphony.xwork2.actionsupport; import javax.servlet.http.*; import org.apache.struts2.interceptor.*; public class MyAction extends ActionSupport implements ServletResponseAware { private javax.servlet.http.httpservletresponse response; // 获得 HttpServletResponse 对象 public void setservletresponse(httpservletresponse response) { this.response = response; public String execute() throws Exception { response.getwriter().write(" 实现 ServletResponseAware 接口 "); 在上面的代码中,MyAction 实现了一个 ServletResponseAware 接口, 并且实现了 setservletresponse 方法 如果一个动作类实现了 ServletResponseAware 接口,Struts2 在调用 execute 方法之前, 就会先调用 setservletresponse 方法, 并将 response 参数传入这个方法 如果想获得 HttpServletRequest HttpSession 和 Cookie 等对象, 动作类可以分别实现 ServletRequestAware SessionAware 和 CookiesAware 等接口 这些接口都在 org.apache.struts2.interceptor 包中 如果要获得请求参数, 动作类可以实现 org.apache.struts2.interceptor. ParameterAware 接口, 但如果只想判断某个参数是否存在, 也可以实现 com.opensymphony.xwork2.interceptor. ParameterNameAware 接口 这个接口有一个 acceptableparametername 方法, 当 Struts2 获得一个请求参数时, 就会调用一次 读者可以在这个方法中将所有的请求参数记录下来, 以便以后使用 这个方法的定义如下 : boolean acceptableparametername(string parametername); 方法 2 使用 RequestAware 拦截器这种方法和第 1 种方法类似 动作类需要实现一个 org.apache.struts2.interceptor.requestaware 接口 所不同的是 RequestAware 将获得一个 com.opensymphony.xwork2.util.ognlvaluestack 对象, 这个对象可以获得 response request 及其他的一些信息 代码如下所示 : package action; import java.util.map; import org.apache.struts2.*; import com.opensymphony.xwork2.actionsupport; import javax.servlet.http.*; import com.opensymphony.xwork2.util.*; import org.apache.struts2.interceptor.*; public class FirstAction extends ActionSupport implements RequestAware { private Map request; private HttpServletResponse response; QQ: QQ 群 : Page 53 of 162

54 public void setrequest(map request) { this.request = request; public String execute() throws Exception { java.util.set<string> keys = request.keyset(); // 枚举所有的 key 值 实际上只有一个 key:struts.valuestack for(string key: keys) System.out.println(key); // 获得 OgnlValueStack 对象 OgnlValueStack stack = (OgnlValueStack)request.get("struts.valueStack"); // 获得 HttpServletResponse 对象 response = (HttpServletResponse)stack.getContext().get(StrutsStatics.HTTP_RESPONSE); response.getwriter().write(" 实现 RequestAware 接口 "); 我们也可以使用 StrutsStatics.HTTP_REQUEST StrutsStatics.PAGE_CONTEXT 来获得 HttpServletRequest 和 PageContext 对象 这种方法有些麻烦, 一般很少用, 读者可以作为一个参考 方法 3 使用 ActionContext 类这种方法比较简单, 我们可以通过 org.apache.struts2.actioncontext 类的 get 方法获得相应的对象 代码如下 : HttpServletResponse response(httpservletresponse) = ActionContext.getContext().get(org.apache.struts2.StrutsStatics.HTTP_RESPONSE); HttpServletRequest request(httpservletrequest) = ActionContext.getContext().get(org.apache.struts2.StrutsStatics.HTTP_REQUEST); 方法 4 使用 ServletActionContext 类 Struts2 为我们提供了一种最简单的方法获得 HttpServletResponse 及其他对象 这就是 org.apache.struts2.servletactioncontext 类 我们可以直接使用 ServletActionContext 类的 getrequest getresponse 方法来获得 HttpServletRequest HttpServletResponse 对象 代码如下 : HttpServletResponse response = ServletActionContext.getResponse() response.getwriter().write("hello world"); 从这四种方法来看, 最后一种是最简单的, 读者可以根据自己的需要和要求来选择使用哪一种方法来获得这些对象 第 8 章 Struts2 标签 为了使从一个页面中链接一个动态数据变得简单,Struts2 框架提供了一系列的标签 Struts2 标签的一种用法是创建链接到其他 Web 资源, 特别是针对那些在本地应用中的资源 1. 普通链接 Web 程序中最普通的应用是链接到其他页面, 下面看 Welcome.jsp <%@ page contenttype="text/html; charset=utf-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> QQ: QQ 群 : Page 54 of 162

55 <title>welcome</title> <link href="<s:url value="/css/tutorial.css"/>" rel="stylesheet" type="text/css"/> </head> <body> <h3>commands</h3> <ul> <li><a href="<s:url action="login_input"/>">sign On</a></li> <li><a href="<s:url action="register"/>">register</a></li> </ul> </body> </html> 注册 action, 在 struts.xml 中注册一个 action 来显示 welcome.jsp <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="testemo" extends="struts-default"> <action name="welcome"> <result>/welcome.jsp</result> </action> </package> </struts> 在地址栏中敲入 是 project 名 ), 会导向到 Welcome.jsp 效果页是 : 因为此页面其它的连接并无配置相关的 action, 所以单击其它连接, 将出现错误页面导航的提示 此页面解析后的代码如下图所示 : QQ: QQ 群 : Page 55 of 162

56 页面代码分析如下 : 1.<%@ taglib prefix="s" uri="/struts-tags" %> 此句表示导入 struts 标签, 并以 s 为前缀 即以 s 为前缀的标签均来自 struts 标签库 2.<link href="<s:url value="/css/tutorial.css"/>" rel="stylesheet" type="text/css"/> 此句表示利用 url 标签导入一个路径, 链接到一个文件, 注意此路径为项目下的绝对路径 3.<a href="<s:url action="login_input"/>">sign On</a> 此句表示利用 url 标签链接到一个 action 2. 使用通配符对于上面的 action 注册, 我们也可以用下面的语句代替 <action name="*"> <result>/example/{1.jsp</result> </action> 此句的意思是, 如果在没有找到匹配的 action 名称的情况下, 默认调用 action 名称.jsp 第一句中星号指任意, 而第二句中 {1 指代第一句中星号指代的内容 如果在地址栏中敲入 则系统查找 struts.xml, 如果找不到 name 为 Welcome 的 action, 则调用 name 为星号的这个 action, 根据此 action, 将输出 / Welcome.jsp 如果在地址栏中敲入 则系统查找 struts.xml, 如果找不到 name 为 Test 的 action, 则调用 name 为星号的这个 action, 根据此 action, 将输出 / Test.jsp, 因为没有这个页面, 所以系统将会报错找不相应的页面 到 Login_input.jsp 和 Register.jsp 因为这两个 action 还没有注册, 也没有相应的 jsp 文件 QQ: QQ 群 : Page 56 of 162

57 3. 带参数的链接超链接后面带有参数大家不会陌生, 诸如 这个链接后面带有一个 language 参数, 其值为 ch 你可以通过 request.getparameter( language ) 找到参数值 下面演示在 struts2 中如何设置带参数的链接 看 HelloWorld.jsp <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>hello World!</title> </head> <body> <h2> <s:property value="message" /> </h2> <h3> Languages </h3> <ul> <li> <s:url id="url" action="helloworld"> <s:param name="request_locale">en</s:param> </s:url> <s:a href="%{url">english</s:a> </li> <li> <s:url id="url" action="helloworld"> <s:param name="request_locale">gb2312</s:param> </s:url> <s:a href="%{url">chinese</s:a> </li> </ul> </body> </html> 运行效果如下图所示 : QQ: QQ 群 : Page 57 of 162

58 查看源代码如下所示 : 说明 <s:url id="url" action="helloworld"><s:param name="request_locale">en</s:param></s:url> 此段表示设置一个 url 标签指向名为 HelloWorld 的 action, 此标签带一个 id 取名为 url, 后面会用到 带一个参数 request_locale, 其值为 en <s:a href="%{url">english</s:a> 此句用到了 struts2 的超链接标签, 连接的地址即为 1 中 url, 点击 English, 发出的信息为 : struts2demo /HelloWorld.action?request_locale=en 注册 action 到 struts.xml <package name="example" extends="struts-default"> <action name="helloworld" > <result>/ HelloWorld.jsp</result> </action> 在 Struts 2 中实现 CRUD CRUD 是 Create( 创建 ) Read( 读取 ) Update( 更新 ) 和 Delete( 删除 ) 的缩写, 它是普通应用程序的缩影 如果您掌握了某框架的 CRUD 编写, 那么意味可以使用该框架创建普通应用程序了, 所以大家使用新框架开发 OLTP(Online Transaction Processing) 应用程序时, 首先会研究一下如何编写 CRUD 这类似于大家在学习新编程语言时喜欢编写 Hello World 本文旨在讲述 Struts 2 上的 CRUD 开发, 所以为了例子的简单易懂, 我不会花时间在数据库的操作上 取而代之的是一个模拟数据库的哈希表 (Hash Map) 这是一个列表页 : QQ: QQ 群 : Page 58 of 162

59 当单击添加时, 显示添加页面 单击提交按钮, 新添加的数据将在列表页显示出来 QQ: QQ 群 : Page 59 of 162

60 执行删除命令, 将删除新添加的这一条数据 然后单击 Black Art 所对应的 Edit 按钮 将显示数据更新页 程序的布局如下图所示 : QQ: QQ 群 : Page 60 of 162

61 首先, 看看数据模型 Book 类 Book 类有三个属性 isbn, title 和 price 分别代表书籍的编号 名称和价格, 其中编号用于唯一标识书籍 ( 相当数据库中的主键 ) package org.xmh.demo; public class Book { private String isbn; private String title; private double price; public Book() { public Book(String isbn, String title, double price) { this.isbn = isbn; this.title = title; this.price = price; public String getisbn() { return isbn; public void setisbn(string isbn) { this.isbn = isbn; public double getprice() { return price; public void setprice(double price) { this.price = price; QQ: QQ 群 : Page 61 of 162

62 public String gettitle() { return title; public void settitle(string title) { this.title = title; Book 对象, 这主要是为了方便检索和保存 Book 对象 ; 另外, 我还接下来看看的 DAO(Data Access Object, 数据访问对象 ), 使用 ConcurrentMap 数据结构存储, 将 data 变量设为静态唯一来模拟应用程序的数据库 代码如下 : package org.xmh.demo; import java.util.collection; import java.util.concurrent.concurrenthashmap; import java.util.concurrent.concurrentmap; public class BookDao { private static final BookDao instance; private static final ConcurrentMap<String, Book> data; static { instance = new BookDao(); data = new ConcurrentHashMap<String, Book>(); data.put(" ", new Book(" ", "Code Web site", 32.99)); data.put(" ", new Book(" ", "EJB", 35.96)); data.put(" ", new Book(" ", "Design Patterns", 43.19)); data.put(" ", new Book(" ", "Information Architectures", 25.19)); data.put(" ", new Book(" ", "Black Art", 25.19)); private BookDao() { public static BookDao getinstance() { return instance; public Collection<Book> getbooks() { return data.values(); public Book getbook(string isbn) { return data.get(isbn); public void storebook(book book) { data.put(book.getisbn(), book); QQ: QQ 群 : Page 62 of 162

63 public void removebook(string isbn) { data.remove(isbn); public void removebooks(string[] isbns) { for(string isbn : isbns) { data.remove(isbn); 然后, 我们再来看看 Action 类的代码 : package org.xmh.demo; import java.util.collection; import com.opensymphony.xwork2.actionsupport; public class BookAction extends ActionSupport { private static final long serialversionuid = L; private String isbn; private String[] isbns; private Book book; private Collection<Book> books; private BookDao dao = BookDao.getInstance(); public Book getbook() { return book; public void setbook(book book) { this.book = book; public String getisbn() { return isbn; public void setisbn(string isbn) { this.isbn = isbn; public String[] getisbns() { return isbns; public void setisbns(string[] isbns) { this.isbns = isbns; QQ: QQ 群 : Page 63 of 162

64 public Collection<Book> getbooks() { return books; public void setbooks(collection<book> books) { this.books = books; public String load() { book = dao.getbook(isbn); return SUCCESS; public String list() { books = dao.getbooks(); return SUCCESS; public String store() { dao.storebook(book); return SUCCESS; public String remove() { if(null!= isbn) { dao.removebook(isbn); else { dao.removebooks(isbns); return SUCCESS; BookAction 类中属性 isbn 用于表示待编辑或删除的书籍的编号, 属性 isbns 用于表示多个待删除的书籍的编号数组, 属性 book 表示当前书籍, 属性 books 则表示当前的书籍列表 BookAction 有四个 Action 方法分别是 load list store 和 remove, 也即是 CRUD 都集中在 BookAction 中实现 再下来是 Action 的配置代码 : <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="cruddemo" extends="struts-default"> <action name="list" class="org.xmh.demo.bookaction" method="list"> <result>list.jsp</result> </action> <action name="edit" class="org.xmh.demo.bookaction" method="load"> <result>edit.jsp</result> QQ: QQ 群 : Page 64 of 162

65 </action> <action name="store" class="org.xmh.demo.bookaction" method="store"> <result type="redirect">list.action</result> </action> <action name="remove" class="org.xmh.demo.bookaction" method="remove"> <result type="redirect">list.action</result> </action> </package> </struts> 以上的配置中, 我使用了四个 Action 定义 它们都在 /Book 名值空间内 这样我就可以分别通过 /Book/Edit.action Struts2demo/Book/Store.action 和 来调用 BookAction 的四个 Action 方法进行 CRUD 操作 当然, 这只是个人喜好, 你大可以只定义一个 Action( 假设其名称为 Book ), 之后通过 Struts2demo/Book!list.action 的方式来访问, 详细做法请参考 Struts 2.0 的 Action 讲解 另外, 我由于希望在完成编辑或删除之后回到列表页, 所以使用类型为 redirect( 重定向 ) 的 result 下面是列表页面的代码 : <%@ page language="java" contenttype="text/html; charset=utf-8" pageencoding="utf-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " <html xmlns=" <head> <title>book List</title> <style type="text/css"> table { border: 1px solid black; border-collapse: collapse; table thead tr th { border: 1px solid black; padding: 3px; background-color: #cccccc; table tbody tr td { border: 1px solid black; padding: 3px; </style> </head> <body> <h2>book List</h2> <s:form action="remove" theme="simple"> <table cellspacing="0"> QQ: QQ 群 : Page 65 of 162

66 <thead> <tr> <th>select</th> <th>isbn</th> <th>title</th> <th>price</th> <th>operation</th> </tr> </thead> <tbody> <s:iterator value="books"> <tr> <td><input type="checkbox" name="isbns" value='<s:property value="isbn" />' /></td> <td><s:property value="isbn" /></td> <td><s:property value="title" /></td> <td>$<s:property value="price" /></td> <td> <a href='<s:url action="edit"><s:param name="isbn" value="isbn" /></s:url>'> Edit </a> <a href='<s:url action="remove"><s:param name="isbn" value="isbn" /></s:url>'> Delete </a> </td> </tr> </s:iterator> </tbody> </table> <s:submit value="remove" /><a href="edit.jsp">add Book</a> </s:form> </body> </html> 以上代码, 值得注意的是在 <s:form> 标签, 我设置了 theme 属性为 simple, 这样可以取消其默 认的表格布局 之前, 有些朋友问我 如果不希望提交按钮放在右边应该怎样做?, 上述做汗是答案之一 当然, 更佳的做法自定义一个 theme, 并将其设为默认应用到整个站点, 如此一来就可以得到统一的站点风格 我会在以后的文章中会对此作详细的描述 page language="java" contenttype="text/html; charset=utf-8" pageencoding="utf-8" %> taglib prefix="s" uri="/struts-tags" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " <html xmlns=" <head> <title>book</title> </head> <body> <h2> QQ: QQ 群 : Page 66 of 162

67 <s:if test="null == book"> Add Book </s:if> <s:else> Edit Book </s:else> </h2> <s:form action="store" > <s:textfield name="book.isbn" label="isbn" /> <s:textfield name="book.title" label="title" /> <s:textfield name="book.price" label="price" /> <s:submit /> </s:form> </body> </html> 如果 book 为 null, 则表明该页面用于添加书籍, 反之则为编辑页面 完成上面这个练习后, 接下来以我们一个 Old demo 为例来学习基于数据库的 CRUD 首先数据库设置为 : 工程布局图如下 : 代码清单如下 : package org.xmh.actions; QQ: QQ 群 : Page 67 of 162

68 import java.util.list; import org.xmh.bean.person; import org.xmh.manage.manage; import com.opensymphony.xwork2.actionsupport; public class PersonAction extends ActionSupport { Person person; private String id; List<Person> persons; Manage dao = new Manage(); public String get() { person = dao.get(id); return SUCCESS; public String list() { persons = dao.getall(); return SUCCESS; public String save() { dao.save(person.getid(), person.getname(), person.getclasses(), person.getscore()); return SUCCESS; public String dele() { dao.delete(id); return SUCCESS; public Person getperson() { return person; public void setperson(person person) { this.person = person; public List getpersons() { return persons; public void setpersons(list persons) { this.persons = persons; QQ: QQ 群 : Page 68 of 162

69 public Manage getdao() { return dao; public void setdao(manage dao) { this.dao = dao; public String getid() { return id; public void setid(string id) { this.id = id; package org.xmh.bean; public class Person { private String name; private String classes; private String score; private String id; public String getscore() { return score; public String getname() { return name; public String getid() { return id; public void setclasses(string classes) { this.classes = classes; public void setscore(string score) { this.score = score; public void setname(string name) { this.name = name; public void setid(string id) { this.id = id; QQ: QQ 群 : Page 69 of 162

70 public String getclasses() { return classes; package org.xmh.dbcon; import java.sql.connection; import java.sql.drivermanager; import java.sql.sqlexception; public class DBcon { public DBcon() { public Connection getcon() { Connection con = null; try { Class.forName("com.mysql.jdbc.Driver"); catch (ClassNotFoundException ex) { ex.printstacktrace(); try { String url = "jdbc:mysql://localhost:3306/studb" + "?useunicode=true&characterencoding=gbk"; con = DriverManager.getConnection(url, "root", " "); catch (SQLException ex1) { ex1.printstacktrace(); return con; public static void main(string args[]){ DBcon db = new DBcon(); System.out.println(db.getcon().toString()); package org.xmh.manage; import java.sql.connection; import java.sql.resultset; import java.sql.sqlexception; import java.sql.statement; import java.util.arraylist; import java.util.list; QQ: QQ 群 : Page 70 of 162

71 import org.xmh.bean.person; import org.xmh.dbcon.dbcon; public class Manage { public Manage() { boolean issuc = false; DBcon dbc = new DBcon(); public boolean save(string id,string name, String classes, String score) { Connection con = dbc.getcon(); Statement s = null; String sql=""; if(id==null "".equals(id)) sql="insert into stuinfo (name,classes,score)values('" + name + "','" + classes + "','" + score + "')"; else sql="update stuinfo set name='" + name + "',classes='" + classes + "',score='" + score + "' where id='" + id + "'"; try { s = con.createstatement(); int count = s.executeupdate(sql); if (count > 0) { issuc = true; catch (SQLException ex) { ex.printstacktrace(); finally { close(s, con); return issuc; public List<Person> getall() { List<Person> list = new ArrayList<Person>(); Connection con = dbc.getcon(); Statement s = null; ResultSet rs = null; try { String sql = "select * from stuinfo"; s = con.createstatement(); rs = s.executequery(sql); while (rs.next()) { Person mybean = new Person(); mybean.setid(rs.getstring("id")); mybean.setname(rs.getstring("name")); mybean.setclasses(rs.getstring("classes")); QQ: QQ 群 : Page 71 of 162

72 mybean.setscore(rs.getstring("score")); list.add(mybean); catch (SQLException ex) { ex.printstacktrace(); finally { close(rs, s, con); return list; public boolean delete(string id) { Connection con = dbc.getcon(); Statement s = null; try { s = con.createstatement(); int count = s.executeupdate("delete from stuinfo where id='" + id + "'"); if (count > 0) { issuc = true; catch (SQLException ex) { ex.printstacktrace(); finally { close(s, con); return issuc; public Person get(string id) { Connection con = dbc.getcon(); Person mybean = new Person(); Statement s = null; ResultSet rs = null; try { String sql = "select * from stuinfo where id='" + id + "'"; s = con.createstatement(); rs = s.executequery(sql); if (rs.next()) { mybean.setid(rs.getstring("id")); mybean.setname(rs.getstring("name")); mybean.setclasses(rs.getstring("classes")); mybean.setscore(rs.getstring("score")); catch (SQLException ex) { ex.printstacktrace(); finally { close(rs, s, con); QQ: QQ 群 : Page 72 of 162

73 return mybean; public void close(resultset rs, Statement s, Connection con) { try { rs.close(); s.close(); con.close(); catch (SQLException ex) { ex.printstacktrace(); public void close(statement s, Connection con) { try { s.close(); con.close(); catch (SQLException ex) { ex.printstacktrace(); <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <package name="struts2_crud_demo" extends="struts-default"> <action name="list" class="org.xmh.actions.personaction" method="list"> <result>/list.jsp</result> </action> <action name="edit" class="org.xmh.actions.personaction" method="get"> <result>/edit.jsp</result> </action> <action name="save" class="org.xmh.actions.personaction" method="save"> <result type="redirect">/list.action</result> </action> <action name="delete" class="org.xmh.actions.personaction" method="dele"> <result type="redirect">list.action</result> </action> QQ: QQ 群 : Page 73 of 162

74 </package> </struts> contenttype="text/html; charset=gbk" import="java.util.*"%> taglib prefix="s" uri="/struts-tags"%> <html> <head> <title>select</title> <style>.xmh { font-size: 12px </style> </head> <body bgcolor="#ffffff"> <s:form action="remove" theme="simple"> <table width="442" border="1" cellpadding="1" cellspacing="0" style="border-style: inherit" bordercolordark="#ffffff" class="xmh"> <tr> <td> <strong> 姓名 </strong> </td> <td> <strong> 班级 </strong> </td> <td> <strong> 成绩 </strong> </td> <td> <strong> 修改 </strong> </td> <td> <strong> 删除 </strong> </td> <td> <strong> 添加学员 </strong> </td> </tr> QQ: QQ 群 : Page 74 of 162

75 <s:iterator value="persons"> <tr> <td> <s:property value="name" /> </td> <td> <s:property value="classes" /> </td> <td> <s:property value="score" /> </td> <td> <a href='<s:url action="edit"><s:param name="id" value="id" /></s:url>'> 编辑信息 </a> </td> <td> <a href='<s:url action="delete"><s:param name="id" value="id" /></s:url>'> 删除信息 </a> </td> <td> <a href="edit.jsp"> 添加信息 </a> </td> </tr> </s:iterator> </table> </s:form> </body> </html> contenttype="text/html; charset=gbk"%> taglib prefix="s" uri="/struts-tags"%> <html> <head> <title><s:if test="null == book"> Add Person </s:if> <s:else> Edit Person </s:else> </title> <style>.xmh { font-size: 12px </style> </head> <body> <s:form action="save" theme="simple"> <table width="328" border="1" cellpadding="1" cellspacing="0" QQ: QQ 群 : Page 75 of 162

76 style="border-style: inherit" bordercolordark="#ffffff" class="xmh"> <tr> <td colspan="2"> <h3 align="center"> 您的详细信息如下 </h3> </td> </tr> <tr> <td> 姓名 : </td> <td> <s:textfield name="person.name" /> </td> </tr> <tr> <td> 班级 : </td> <td> <s:textfield name="person.classes" /> </td> </tr> <tr> <td> 成绩 : </td> <td> <s:textfield name="person.score" /> </td> </tr> <tr> <td> <s:hidden name="person.id" /> </td> <td> <s:submit /> </td> </tr> </table> </s:form> </body> </html> QQ: QQ 群 : Page 76 of 162

77 添加的页面效果如下 : <%@page contenttype="text/html; charset=gbk"%> <html> <head> <title>insert</title> <style>.xmh { font-size: 12px </style> </head> <body> <form action="isinsert.jsp" method="post"> <table width="402" border="1" cellpadding="1" cellspacing="0" style="border-style: inherit" bordercolordark="#ffffff" class="xmh"> <tr> <td width="115"> QQ: QQ 群 : Page 77 of 162

78 姓名 : </td> <td width="280"> <label> <input type="text" name="username" /> </label> </td> </tr> <tr> <td> 班级 : </td> <td> <label> <input type="text" name="classes" /> </label> </td> </tr> <tr> <td> 成绩 : </td> <td> <label> <input type="text" name="score" /> </label> </td> </tr> <tr> <td> </td> <td> <input type="submit" name="submit" value=" 提交 " /> <input type="submit" name="submit2" value=" 重置 " /> <a href="select.jsp"> 查看信息 </a> </td> </tr> </table> </form> </body> </html> 第 9 章表达式 OGNL OGNL 的全称是 Object Graph Navigation Language( 对象图导航语言 ), 它是一种强大的表达式语 言, 让你通过简单一致的表达式语法来读取和设置 Java 对象的属性值, 调用对象的方法, 遍历整个对象的结构图, 实现字段类型转换等功能 为什么使用 OGNL? 相对于其它的表达式语言,OGNL 的功能更为强大, 它提供了很多高级而必需的特性, 例如强大的类型转换功能 静态或实例方法的执行 跨集合投影, 以及动态 lambda 表达式定义等 QQ: QQ 群 : Page 78 of 162

79 9.1 OGNL 概述 OGNL 表达式的计算都是围绕 OGNL 上下文来进行的,OGNL 上下文实际上就是一个 Map 对象, 由 ognl.ognlcontext 类 ( 实现了 java.util.map 接口 ) 来表示 OGNL 上下文可以包含一个或多个 JavaBean 对象, 在这些对象中有一个是特殊的, 这个对象就是上下文的根 (root) 对象 如果在写表达式的时候, 没有指定使用上下文中的哪一个对象, 那么根对象将被假定为表达式所依据的对象 下面我们给出一个例子程序, 看看在 OGNL 中如何根据上下文来计算表达式的值 public class Employee{ private String name; public String getname(){ return name; public void setname(string name){ this.name=name; public class Manager{ private String name; public String getname(){ return name; public void setname(string name){ this.name=name; import ognl.ognl; import ognl.ognlcontext; import ognl.ognlexception; public class OgnlExpression{ private Object expression; public OgnlExpression(String expressionstring) throws OgnlException{ expression=ognl.parseexpression(expressionstring); public Object getexpression(){ return expression; public Object getvalue(ognlcontext context, Object rootobject)throws OgnlException{ return Ognl.getValue(getExpression(), context, rootobject); public void setvalue(ognlcontext context, Object rootobject, Object value) throws OgnlException{ Ognl.setValue(getExpression(), context,rootobject, value); public static void main(string[] args) throws OgnlException{ Employee employee=new Employee(); QQ: QQ 群 : Page 79 of 162

80 employee.setname("xmh"); Manager manager=new Manager(); manager.setname("zjy"); // 创建 OGNL 上下文 OgnlContext context=new OgnlContext(); // 将 employee 对象和 manager 对象放到 OGNL 上下文中 context.put("employee",employee); context.put("manager",manager); // 将 employee 对象设置为 OGNL 上下文的根 context.setroot(employee); // 表达式 name, 将执行 employee.getname(), 因为 employee 对象是根对象 OgnlExpression exp=new OgnlExpression("name"); System.out.println(exp.getValue(context, context.getroot())); // 表达式 #employee.name, 将执行 employee.getname() exp=new OgnlExpression("#employee.name"); System.out.println(exp.getValue(context, context.getroot())); // 表达式 #manager.name, 将执行 manager.getname() // 如果你访问的不是 OGNL 上下文中的根对象, 那么必须在前面加上一个名称空问, 例如 #manager exp=new OgnlExpression("#manager.name"); System.out.println(exp.getValue(context, context.getroot())); 控制台显示的结果如下所示 : 在 OGNL 上下文中, 只能有一个根对象, 如果你访问根对象, 那么在写表达式的时候, 直接写对象的属性就可以了 ; 否则, 你需要使用 #key 前缀, 例如表达式 #namager.name 9.2 OGNL 基础 OGNL 表达式 OGNL 表达式的基础单元就是导航链, 通常简称为链 (chain) 最简单的链由下列部分组成: 1 属性名: 如 name 和 manager.name; 2 方法调用: 如 manager.hashcode(), 返回 manager 对象的散列码 ; 3 数组索引: 如 emals[0], 返回当前对象的邮件列表中的第一个邮件地址 所有 OGNL 表达式的计算都是在当前对象的上下文中, 一个链简单地使用链中先前链接的结果作为下一步计算的当前对象 我们看如下所示的链 : name.tochararray()[0].numericvalue.tostring() 这个表达式按照下列的步骤进行计算 : 1 获取根对象的 name 属性 ; QQ: QQ 群 : Page 80 of 162

81 2 在 String 结果上调用 tochararray() 方法 ; 3 从 char 数组中提取第一个字符 ; 4 从提取的字符对象上行到 numericvalue 属性 ( 这个字符被表示为 Character 对象, Character 类有一个 getnumericvalue() 方法 ); 5 在 Integer 对象结果上调用 tostring() 方法 这个表达式最终结果是最后返回的 tostring() 方法调用返回的字符串 常量 OGNL 支持的所有常量类型 : 1 字符串常量: 以单引号或双引号括起来的字符串 如 "hello",'hello' 不过要注意的是, 如果是单个字符的字符串常量, 必须使用双引号 2 字符常量: 以单引号括起来的字符 如 'a' 3 数值常量: 除了 Java 中的 int long float 和 double 外,OGNL 还让你使用 b 或 B 后缀指定 BigDecimal 常量, 用 h H 后缀指定 BigInteger 常量 4 布尔常量: true 和 false 5 null 常量 操作符 OGNL 除了支持所有的 Java 操作符外, 还支持以下几种 : 1 逗号, 与 C 语言中的逗号操作符类似 2 花括号{ 用于创建列表, 元素之间用逗号分隔 3 in 和 not in 用于判断一个值是否在集合中 访问 JavaBean 的属性假如有一个 employee 对象作为 OGNL 上下文的根对象, 那对于下面的表达式 : 1 name 对应的 java 代码是 employee.getname(); 2 address.country 对应的 java 代码是 employee.getaddress().getcountry(); // // 调用静态字段其中 class 必须给出完整的类名 ( 包括包名 ), 如果省略 class, 那么默认使用的类是 java.util.math, @@PI 索引访问 OGNL 支持多种索引方式的访问 1 数组和列表索引在 OGNL 中, 数组和列表可以大致看成是一样的 如 :array[0] list[0] 表达式:{'zhangsan','lisi','wangwu'[1] 等 2 JavaBean 的索引属性要使用索引属性, 需要提供两对 setter 和 getter 方法, 一对用于数组, 一对用于数组中的元素 如 : 有一个索引属性 interest, 它的 getter 和 setter 如下 public String[] interest; public String[] getinterest(){ return interest; QQ: QQ 群 : Page 81 of 162

82 public void setinterest(string[] interest){ this.interest=interest; public String getinterest(int i){ return interest[i] public void setinterest(int i, String newinterest){ interest[i]=newinterest; 对于表达式 interest[2],ognl 可以正确解释这个表达式, 调用 getinterest(2) 方法 如果 是设置的情况下, 会调用 setinterest(2,value) 方法 3 OGNL 对象的索引属性 JavaBean 的索引属性只能使用整型作为索引,OGNL 扩展了索引属性的概念, 可以使用任意的对象来作为索引 对集合进行操作 1 创建集合: 创建列表使用花括号将元素包含起来, 元素之间使用逗号分隔 如 {'zhangsan','lisi','wangwu' 创建数组 OGNL 中创建数组与 Java 语言中创建数组类似 创建 Map Map 使用特殊的语法来创建 #{"key":value,... 如果想指定创建的 Map 类型, 可以在左花括号前指定 Map 实现类的类名 如 : #@java.util.linkedhashmap@{"key":"value",... Map 通过 key 来访问, 如 map["key"] 或 map.key 2 投影 OGNL 提供了一种简单的方式在一个集合中对每一个元素闻调用相同的方法, 或者抽取相同的属性, 并将结果保存为一个新的集合, 称之为投影 假如 employees 是一个包含了 employee 对象的列表, 那么 #employees.{name 将返回所有雇员的名字的列表 在投影期间, 使用 #this 变量来引用迭代中的当前元素 如 : objects.{#this instanceof String? #this:#this.tostring() 3 选择 OGNL 提供了一种简单的方式来使用表达式从集合中选择某些元素, 并将结果保存到新的集合中, 称为选择 如 : #employees.{?#this.salary>3000 将返回薪水大于 3000 的所有雇员的列表 #employees.{^#this.salary>3000 将返回第一个薪水大于 3000 的雇员的列表 #employees.{$#this.salary>3000 将返回最后一个薪水大于 3000 的雇员的列表 lambda 表达式 lambda 表达式的语法是 ::[...] OGNL 中的 lambda 表达式只能使用一个参数, 这个参数通过 #this 引用 如 : #fact= :[ #this<=1? 1 : #this* #fact ( #this-1) ], #fact(30) #fib= :[#this==0? 0 : #this==1? 1 : #fib(#this-2)+#fib(#this-1)], #fib(11) 9.3 struts2 中 OGNL OGNL 是通常要结合 Struts 2 的标志一起使用, 如 <s:property value="xx" /> 等 大家经常遇到的问题是 # % 和 $ 这三个符号的使用 # 主要有三种用途 : 1 访问 OGNL 上下文和 Action 上下文,# 相当于 ActionContext.getContext(); 下表有几个 ActionContext 中有用的属性 : QQ: QQ 群 : Page 82 of 162

83 2 用于过滤和投影 (projecting) 集合, 如 books.{?#this.price<100 ; 3 构造 Map, 如 #{'foo1':'bar1', 'foo2':'bar2' % 符号的用途是在标志的属性为字符串类型时, 计算 OGNL 表达式的值 例如在 Ognl.jsp 中加入以下代码 : < hr /> < h3 > % 的用途 </ h3 > < p >< s:url value ="#foobar['foo1']" /></ p > < p >< s:url value ="%{#foobar['foo1']" /></ p > $ 有两个主要的用途用于在国际化资源文件中, 引用 OGNL 表达式 ( 请参见相应章节 ); 在 Struts 2 配置文件中, 引用 OGNL 表达式, 如 < action name ="AddPhoto" class ="addphoto" > < interceptor-ref name ="fileuploadstack" /> < result type ="redirect" > ListPhotos.action?albumId=${albumId </ result > </ action > 9.4 OGNL 使用示例 package org.xmh.demo; public class Student { private String name; private int age; private int score; public Student() { public Student(String name, int age, int score) { this.name = name; this.age = age; this.score = score; 省略相应的 set/get 方法 Action 类代码清单 : package org.xmh.demo; import java.util.arraylist; QQ: QQ 群 : Page 83 of 162

84 import java.util.list; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpsession; import org.apache.struts2.interceptor.servletrequestaware; import com.opensymphony.xwork2.actionsupport; public class OnglDemoAction extends ActionSupport implements ServletRequestAware { private List<Student> stus = new ArrayList<Student>(); HttpServletRequest request; private static final long serialversionuid = 1L; public void setservletrequest(httpservletrequest request) { this.request = request; public String execute() throws Exception { HttpSession session = request.getsession(); request.setattribute("name", "zah of request"); session.setattribute("name", "xmh of session"); stus.add(new Student("zxx", 23, 63)); stus.add(new Student("wangwu", 19, 60)); stus.add(new Student("zaoli", 26, 87)); stus.add(new Student("xuke", 22, 57)); stus.add(new Student("aihua", 21, 98)); return SUCCESS; public List<Student> getstus() { return stus; public void setstus(list<student> stus) { this.stus = stus; 配置文件要添加如下内容 : <action name="show" class="org.xmh.demo.ongldemoaction"> <result>/showstu.jsp</result> </action> 运行服务, 在 IE 中输入 显示的页面效果如 下图所示 : QQ: QQ 群 : Page 84 of 162

85 第 10 章上传下载 上传单个文件上传文件是很多 Web 程序都具有的功能 在 Struts1.x 中已经提供了用于上传文件的组件 而在 Struts2 中提供了一个更为容易操作的上传文件组件 所不同的是,Struts1.x 的上传组件需要一个 ActionForm 来传递文件, 而 Struts2 的上传组件是一个拦截器 ( 这个拦截器不用配置, 是自动装载的 ) 在本文中先介绍一下如何用 struts2 上传单个文件, 最后介绍一下用 struts2 上传任意多个文件 要用 Struts2 实现上传单个文件的功能非常容易实现, 只要使用普通的 Action 即可 但为了获得一些上传文件的信息, 如上传文件名 上传文件类型以及上传文件的 Stream 对象, 就需要按着一定规则来为 Action 类增加一些 getter 和 setter 方法 在 Struts2 中, 用于获得和设置 java.io.file 对象 (Struts2 将文件上传到临时路径, 并使用 java.io.file 打开这个临时文件 ) 的方法是 getupload 和 setupload 获得和设置文件名的方法是 getuploadfilename 和 setuploadfilename, 获得和设置上传文件内容类型的方法是 getuploadcontenttype 和 setuploadcontenttype 上传需要的 JAR 文件 :commons-io-1.1.jar, commons-fileupload jar 下面是用于上传的动作类的完整代码 : package action; import java.io.*; import com.opensymphony.xwork2.actionsupport; public class UploadAction extends ActionSupport { private File upload; private String filename; private String uploadcontenttype; public String getuploadfilename() { return filename; public void setuploadfilename(string filename) { this.filename = filename; QQ: QQ 群 : Page 85 of 162

86 public File getupload() { return upload; public void setupload(file upload) { this.upload = upload; public void setuploadcontenttype(string contenttype) { this.uploadcontenttype=contenttype; public String getuploadcontenttype() { return this.uploadcontenttype; public String execute() throws Exception { java.io.inputstream is = new java.io.fileinputstream(upload); java.io.outputstream os = new java.io.fileoutputstream("d:\\upload\\" + filename); byte buffer[] = new byte[8192]; int count = 0; while((count = is.read(buffer)) > 0) { os.write(buffer, 0, count); os.close(); is.close(); return SUCCESS; 在 execute 方法中的实现代码就很简单了, 只是从临时文件复制到指定的路径 ( 在这里是 d:\upload) 中 上传文件的临时目录的默认值是 javax.servlet.context.tempdir 的值, 但可以通过 struts.properties( 和 struts.xml 在同一个目录下 ) 的 struts.multipart.savedir 属性设置 Struts2 上传文件的默认大小限制是 2M( 字节 ), 也可以通过 struts.properties 文件中的 struts.multipart.maxsize 修改, 如 struts.multipart.maxsize=2048 表示一次上传文件的总大小不能超过 2K 字节 下面的代码是上传文件的 JSP 页面代码 : <%@ page language="java" import="java.util.*" pageencoding="gbk"%> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title> 上传单个文件 </title> </head> <body> <s:form action="upload" namespace="/test" enctype="multipart/form-data"> <s:file name="upload" label=" 输入要上传的文件名 " /> QQ: QQ 群 : Page 86 of 162

87 <s:submit value=" 上传 " /> </s:form> </body> </html> 也可以在 success.jsp 页中通过 <s:property> 获得文件的属性 ( 文件名和文件内容类型 ), 代码如下 : <s:property value="uploadfilename"/> 上传任意多个文件在 Struts2 中, 上传任意多个文件也非常容易实现 首先, 要想上传任意多个文件, 需要在客户端使用 DOM 技术生成任意多个 <input type= file /> 标签 name 属性值都相同 代码如下 : <html> <head> <script language="javascript"> function addcomponent() { var uploadhtml = document.createelement( "<input type='file' name='upload'/>"); document.getelementbyid("files").appendchild(uploadhtml); uploadhtml = document.createelement( "<p/>"); document.getelementbyid("files").appendchild(uploadhtml); </script> </head> <body> <input type="button" onclick="addcomponent();" value=" 添加文件 " /> <br /> <form onsubmit="return true;" action="/struts2/test/upload.action" method="post" enctype="multipart/form-data"> <span id="files"> <input type='file' name='upload' /> <p /> </span> <input type="submit" value=" 上传 " /> </form> </body> </html> 上面的 javascript 代码可以生成任意多个 <input type= file > 标签,name 的值都为 file( 要注意的是, 上面的 javascript 代码只适合于 IE 浏览器,firefox 等其他浏览器需要使用他的代码 ) 至于 Action 类, 和上传单个文件的 Action 类基本一至, 只需要将三个属性的类型改为 List 即可 代码如下 : package action; import java.io.*; import com.opensymphony.xwork2.actionsupport; public class UploadMoreAction extends ActionSupport { private java.util.list<file> uploads; private java.util.list<string> filenames; private java.util.list<string> uploadcontenttypes; QQ: QQ 群 : Page 87 of 162

88 public java.util.list<string> getuploadfilename() { return filenames; public void setuploadfilename(java.util.list<string> filenames) { this.filenames = filenames; public java.util.list<file> getupload() { return uploads; public void setupload(java.util.list<file> uploads) { this.uploads = uploads; public void setuploadcontenttype(java.util.list<string> contenttypes) { this.uploadcontenttypes = contenttypes; public java.util.list<string> getuploadcontenttype() { return this.uploadcontenttypes; public String execute() throws Exception { if (uploads!= null) { int i = 0; for (; i < uploads.size(); i++) { java.io.inputstream is = new java.io.fileinputstream(uploads.get(i)); java.io.outputstream os = new java.io.fileoutputstream( "d:\\upload\\" + filenames.get(i)); byte buffer[] = new byte[8192]; int count = 0; while ((count = is.read(buffer)) > 0) { os.write(buffer, 0, count); os.close(); is.close(); return SUCCESS; QQ: QQ 群 : Page 88 of 162

89 在 execute 方法中, 只是对 List 对象进行枚举, 在循环中的代码和上传单个文件时的代码基本相同 如果读者使用过 struts1.x 的上传组件, 是不是感觉 Struts2 的上传功能更容易实现呢? 在 Struts1.x 中上传多个文件时, 可是需要建立带索引的属性的 而在 Struts2 中, 就是这么简单就搞定了 图 1 是上传任意多个文件的界面 第 11 章视图浅析 第 12 章集成 Ajax 12.1 JSON 概述 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式, 易于阅读和编写, 同时也易于机器解析和生成 它基于 ECMA262 语言规范 ( 第三版 ) 中 JavaScript 编程语言的一个子集 JSON 采用与编程语言无关的文本格式, 但是也使用了类 C 语言 ( 包括 C, C++, C#, Java, JavaScript, Perl, Python 等 ) 的习惯, 这些特性使 JSON 成为理想的数据交换格式 JSON 的结构基于下面两点 1 " 名称 / 值 " 对的集合不同语言中, 它被理解为对象 (object), 记录 (record), 结构 (struct), 字典 (dictionary), 哈希表 (hash table), 键列表 (keyed list) 等 2 值的有序列表多数语言中被理解为数组 (array) JSON 以一种特定的字符串形式来表示 JavaScript 对象 如果将具有这样一种形式的字符串赋给任意一个 JavaScript 变量, 那么该变量会变成一个对象引用, 而这个对象就是字符串所构建出来的, 好像有点拗口, 我们还是用实例来说明 这里假设我们需要创建一个 User 对象, 并具有以下属性 用户 ID 用户名用户 可以使用以下 JSON 形式来表示 User 对象 : {"UserID":11, "Name":"Truly", " ":"zhuleipro hotmail.com"; 然后如果把这一字符串赋予一个 JavaScript 变量, 那么就可以直接使用对象的任一属性了 完整代码 : <script> var User = {"UserID":11, "Name":"Truly", " ":"zhuleipro hotmail.com"; alert(user.name); </script> 实际使用时可能更复杂一点, 比如为 Name 定义更详细的结构, 使它具有 FirstName 和 LastName: {"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu", " ":"zhuleipro hotmail.com" 完整代码 : <script> var User = {"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu", " ":"zhuleipro hotmail.com"; alert(user.name.firstname); QQ: QQ 群 : Page 89 of 162

90 </script> 现在增加一个新的需求, 我们某个页面需要一个用户列表, 而不仅仅是一个单一的用户信息, 那么这里就需要创建一个用户列表数组 下面代码演示了使用 JSON 形式定义这个用户列表 : [ {"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu", " ":"zhuleipro hotmail.com", {"UserID":12, "Name":{"FirstName":"Jeffrey","LastName":"Richter", " ":"xxx xxx.com", {"UserID":13, "Name":{"FirstName":"Scott","LastName":"Gu", " ":"xxx2 xxx2.com" ] 完整代码 : <script> var UserList = [ {"UserID":11, "Name":{"FirstName":"Truly","LastName":"Zhu", " ":"zhuleipro hotmail.com", {"UserID":12, "Name":{"FirstName":"Jeffrey","LastName":"Richter", " ":"xxx xxx.com", {"UserID":13, "Name":{"FirstName":"Scott","LastName":"Gu", " ":"xxx2 xxx2.com" ]; alert(userlist[0].name.firstname); </script> 事实上除了使用 "." 引用属性外, 我们还可以使用下面语句 : alert(userlist[0]["name"]["firstname"]); 或者 alert(userlist[0].name["firstname"]); 现在读者应该对 JSON 的使用有点认识了, 归纳为以下几点 : 对象是属性 值对的集合 一个对象的开始于 {, 结束于 每一个属性名和值间用 : 提示, 属性间用, 分隔 数组是有顺序的值的集合 一个数组开始于 "[", 结束于 "]", 值之间用 "," 分隔 值可以是引号里的字符串 数字 true false null, 也可以是对象或数组 这些结构都能嵌套 字符串和数字的定义和 C 或 Java 基本一致 12.2 JSON-RPC 概述 JSON-RPC 是一种轻量级的远程过程调用协议, 它允许从 Javascript 进行客户 - 服务器交互 通用机制包含两个建立数据连接的对等体 在连接的生存期内, 对等体可以调用另一个对等体所提供的方法 要调用远程方法, 就要发送请求 请求必须使用应答进行响应, 除非该请求是一个通知 这有什么好处呢? 使用 JSON-RPC, 可以创建定制的 Java 对象, 并可通过 Javascript 和 Ajax 实现对其方法的完全访问 此外, 这些对象在会话的整个生存期中都将存留! 这需要两个库 : 需要驻留在服务器应用程序上的 jsonrpc.jar 以及 jsonrpc.js, 该 JavaScript 库应下载到客户端 12.3 JSON 示例 QQ: QQ 群 : Page 90 of 162

91 配置文件内容 : <?xml version="1.0" encoding="utf-8"?> <web-app version="2.4" xmlns=" xmlns:xsi=" xsi:schemalocation=" <servlet> <servlet-name>jsonrpcservlet</servlet-name> <servlet-class>org.jabsorb.jsonrpcservlet</servlet-class> <init-param> <param-name>gzip_threshold</param-name> <param-value>0</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>jsonrpcservlet</servlet-name> <url-pattern>/json-rpc</url-pattern> </servlet-mapping> </web-app> 导入 JavaScript 库接下来是要将 jsonrpc.js 库包含到 jsp 中 这将链接到一个文本文件 复制该文本并将其粘贴到项目中一个新的 JavaScript 文件中, 可以称之为 jsonrpc.js 要确保将库导入到所有希望在其中使用 jsonrpc 的页面中, 这可通过在 jsp 的 <HEAD> 部分添加下面的代码行来实现 : <script type="text/javascript" src="/path/to/jsonrpc.js"</> 为了让客户端与服务器通信, 需要使用 <jsp:usebean> 标签创建一个 bridge 将下属代码行放在接近 JSP 顶部的某处 : <jsp:usebean id="jsonrpcbridge" scope="session" class="com.metaparadigm.jsonrpc.jsonrpcbridge" /> 然后使用该 bridge 注册一个对象 该对象可以是一个普通 Java 对象, 也可以是您自己创建的对象 <% JSONRPCBridge.registerObject("myObject",new com.package.myobjects.myobject()); %> 另一种替代方案是, 为了与页面流通信, 可以让页面流初始化一个对象, 传递所需的数据, 并通过页面流上的一个 getter 方法使该对象对 JSP 可用 然后在 JSP 上执行 getdata, 以将该对象放在 JSONRPC QQ: QQ 群 : Page 91 of 162

92 可以获取的 pagecontext 上 : <netui-data:getdata resultid="myobject" value="{pageflow.myobject" /> <% JSONRPCBridge.registerObject("myObject", pagecontext.getattribute("myobject")); %> Index.jsp 的代码清单如下所示 : <%@ page language="java" import="java.util.*" pageencoding="gb18030"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <script type="text/javascript" src="script/jsonrpc.js"></script> <script type="text/javascript"> function asyc(result,e){ if(e == null) alert(result); function onload(){ jsonrpc = new JSONRpcClient("JSON-RPC"); window.onload = onload; function invoke(){ var text = document.getelementbyid("text"); var result = jsonrpc.msg.getmessage(asyc, text.value); alert(result); </script> <jsp:usebean id="jsonrpcbridge" scope="session" class="org.jabsorb.jsonrpcbridge" /> <jsp:usebean id="message" scope="session" class="invoke.message" /> <% JSONRPCBridge.registerObject("msg", message); %> </head> <body> <input type="text" id="text" /> <input type="button" value=" 获得信息 " onclick="invoke()" /> </body> </html> 编写 Message 类及相应的方法 public class Message implements java.io.serializable { public String getmessage(string s) { return " 你好 " + s; 部署应用程序, 启动服务, 输入相应的访问地址将显示如下效果 : QQ: QQ 群 : Page 92 of 162

93 输入 大漠孤烟 单击 获得信息 按钮, 将弹出如下图所示的对话框 12.4 struts2 与 JSON 示例 JSON 插件提供了一种名为 json 的 ResultType, 一旦为某个 Action 指定了一个类型为 json 的 Result, 则该 Result 无需映射到任何视图资源 因为 JSON 插件会负责将 Action 里的状态信息序列化成 JSON 格式的数据, 并将该数据返回给客户端页面的 JavaScript 简单地说,JSON 插件允许我们在 JavaScript 中异步调用 Action, 而且 Action 不再需要使用视图资源来显示该 Action 里的状态信息, 而是由 JSON 插件负责将 Action 里的状态信息返回给调用页面 通过这种方式, 就可以完成 Ajax 交互 Struts2 提供了一种可插拔方式来管理插件, 安装 Struts2 的 JSON 插件与安装普通插件并没有太大的区别, 一样只需要将 Struts2 插件的 JAR 文件复制到 Web 应用的 WEB-INF/lib 路径下即可 (1) 登陆 站点, 下载 Struts2 的 JSON 插件的最新版本, 当前最新版本是 0.7, 我们可以下载该版本的 JSON 插件 将 json-plugin.jar 文件复制到 Web 应用的 WEB-INF 路径下, 即可完成 JSON 插件的安装 (2) 把 JSON.js prototype.js 复制到 js 文件夹下在配置文件中有两个值得注意的地方 : 第一个地方是配置 struts.i18n.encoding 常量时, 不再是使用 GBK 编码, 而是 UTF-8 编码, 这是因为 Ajax 的 POST 请求都是以 UTF-8 的方式进行编码的 第二个地方是配置包时, 自己的包继承了 json-default 包, 而不再继承默认的 default 包, 这是因为只有在该包下才有 json 类型的 Result 在 struts 2 中, 要结合 json 的话, 其实是不错的选择, 下面通过做一个登陆系统来演示 struts2 与 json 的用法 当用户输入姓名并移出焦点时, 系统将根据用户输入的信息返回相关的提示 QQ: QQ 群 : Page 93 of 162

94 步骤 1 编写页面代码: <%@ page language="java" import="java.util.*" pageencoding="utf-8"%> <html> <head> <title>struts2</title> <script type="text/javascript" src="js/json.js"></script> <script type="text/javascript" src="js/prototype.js"></script> <script language="javascript"> function validatename() { // 请求的地址 var url = 'validatename.action'; var params = Form.Element.serialize('userBean.name'); // 创建 Ajax.Request 对象, 对应于发送请求 var myajax = new Ajax.Request( url, { // 请求方式 :POST method:'post', // 请求参数 parameters:params, // 指定回调函数 oncomplete: processresponse, // 是否异步发送请求 asynchronous:true ); function processresponse(request) { </script> var jsonobje = request.responsetext.parsejson(); $("tip").innerhtml = jsonobje.tip; $("tip2").innerhtml=' 欢迎您,'+JSON.stringify(jsonObje.userBean.name); QQ: QQ 群 : Page 94 of 162

95 </head> <body> <span id="tip" style="color: red; font-weight: bold"> </span> <span id="tip2" style="color: red; font-weight: bold"> </span> <form action="" method="post" name="form"> <fieldset> <legend> 用户登录 </legend> <p align="center"> 帐 &nbsp 号 : <input type="text" name="userbean.name" onblur="validatename();" /> </p> </fieldset> </form> </body> </html> 步骤 2 编写处理 Action 的代码 : public class ValidateNameAction extends ActionSupport { private UserBean userbean; private String public UserBean getuserbean() { return userbean; public void setuserbean(userbean userbean) { this.userbean = public String gettip() { return tip; public void settip(string tip) { this.tip = tip; public String execute() { try { if (validatename(userbean)) { tip = " 你好!" + userbean.getname() + ", 这个用户名可用!"; else { tip = " 系统中已有 " + userbean.getname() + " 用户名, 请重新选择一个!"; catch (Exception e) { tip = e.getmessage(); QQ: QQ 群 : Page 95 of 162

96 return SUCCESS; public boolean validatename(userbean userbean) { if ("xmh".equals(userbean.getname())) return false; else return true; 步骤 3 修改配置文件 <struts> <!-- 加载默认的 struts2 配置文件 --> <include file="struts-default.xml" /> <!-- 继承默认的 struts2 配置文件 --> <package name="default" extends="json-default" > <action name="validatename" class="org.xmh.demo.validatenameaction"> <result type="json" /> </action> </package> </struts> 步骤 4 发布应用程序, 输入相应的访问网址, 效果图如下所示 : 关于本示例更多的知识请参看本书所配光盘源码部分 第 13 章集成 Hibernate 本章节主要通过一个示例来学习 strtus2 与 hibernate 的集成运用 13.1 系统总体设计图 本系统采用了四层架构, 分别为视图层 控制器层 数据访问层 持久化层 客户端不直接与数据 QQ: QQ 群 : Page 96 of 162

97 库交互, 而是通过控制器与数据访问层建立连接, 再由数据访问层与数据库交互 表现层采用了 JSP, 控制层采用 struts2, 数据访问层使用 Hibernate 封装了对底层数据库的相关操作, 数据库采用了 MySQL 数据库存放数据 13.2 系统用例图 本程序比较简单, 在管理员成功登陆之后对书籍进行增加 删除 修改 查询, 故而根据程序的场 景分析绘制出如图 1 所示的系统用例图 : 图 1 系统用例图 13.3 数据库 系统有管理员表 书籍表, 表结构如图 2 所示 : 图 2 数据库表效果图可以使用如下代码创建上图所示数据库表 : create database if not exists `bookshell`; USE `bookshell`; DROP TABLE IF EXISTS `books`; CREATE TABLE `books` ( `book_id` varchar(11) NOT NULL, `book_name` varchar(150) default NULL, `book_author` varchar(150) default NULL, `book_publish` varchar(150) default NULL, `book_date` datetime default NULL, `book_isbn` varchar(100) default NULL, QQ: QQ 群 : Page 97 of 162

98 `book_page` int(11) default NULL, `book_price` decimal(10,2) default NULL, `book_content` varchar(200) default NULL, PRIMARY KEY (`book_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; DROP TABLE IF EXISTS `login_user`; CREATE TABLE `login_user` ( `Id` int(11) NOT NULL auto_increment, `password` varchar(11) default NULL, `name` varchar(20) default NULL, PRIMARY KEY (`Id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 13.4 系统效果图展示 登陆视图 对于一个系统, 安全是必不可缺少的, 管理员必须登陆之后才能进行相关的操作 登陆页下如图 3 所示 : 图 3 登陆页效果图主要代码如下 : <%@ page contenttype="text/html; charset=utf-8"%> <%@taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title> 用户登录 </title> <meta http-equiv="pragma" content="no-cache"> </head> <body> <h2> 管理员登陆 </h2> <s:form action="/login.action"> QQ: QQ 群 : Page 98 of 162

99 <s:textfield name="user.name" label=" 用户名 " /> <s:password name="user.password" label=" 密码 "/> <s:submit value=" 登录 "/> </s:form> </body> </html> 4.2 登陆失败视图在登陆页输入 用户名 和 密码, 单击 登陆 按钮, 如果用户名和密码不正确, 则提示 登陆失败, 如图 4 所示 : 图 4 登陆失败页效果图页面主要代码如下 : <%@ page contenttype="text/html; charset=utf-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title> 登录失败 </title> </head> <body> 登录失败! 您的用户名或密码有误!<a href="login.jsp"> 返回 </a> </body> </html> 4.3 登陆成功视图如果在登陆页输入 用户名 和 密码 正确, 系统跳转到欢迎页, 如图 5 所示 : 图 5 登陆成功页效果图页面主要代码如下 : <%@page contenttype="text/html; charset=utf-8"%> QQ: QQ 群 : Page 99 of 162

100 taglib prefix="s" uri="/struts-tags"%> <html> <head> <meta http-equiv="content-type" content="text/html; charset=gbk" /> <title> 图书管理系统 </title> </head> <body> 欢迎管理员 :${name,<a href="<s:url action="list" />"> 进入图书管理系统 </a> </body> </html> 4.4 书籍列表视图在登陆成功之后的欢迎页, 点击 进入图书管理系统 系统会进入书籍列表页, 书籍列表 -- 顾名思义该页用来显示所有的书籍信息, 在后台将数据查询出来, 并在页面上迭代出数据 如图 6 所示 : 图 6 图书列表页效果图主要代码如下 : <%@page pageencoding="gb2312" contenttype="text/html; charset=utf-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head><title> 图书管理系统 </title></head> <link rel="stylesheet" type="text/css" href="${pagecontext.request.contextpath/styles.css" media="screen"> <script language="javascript"> function dosearch(){ if(document.all.searchvalue.value=="") { alert(" 请输入查询关键字!"); else{ window.location.href="bookadmin/list.action?queryname="+document.all.searchname.value+"&&queryv alue="+document.all.searchvalue.value; QQ: QQ 群 : Page 100 of 162

101 </script> <body> <br/> <table align="center"> <tr align="center"> <td align="right" height="20"> <select name="searchname"> <option value="bookname"> 书名 </option> <option value="bookauthor"> 作者 </option> <option value="bookpublish"> 出版社 </option> <option value="bookdate"> 出版日期 </option> <option value="bookisbn">isnb</option> <option value="bookpage"> 页数 </option> </select> <input type="text" name="searchvalue" value="" size="10"/> <input type="button" value=" 查询 " onclick="dosearch();"> <a href="<s:url action="list" includeparams="none"/>"> 查看全部 </a> <a href='<s:url action="edit" ></s:url>'> 增加书籍 </a> </td> </tr> </table> <table align="center"> <tr align="center"> <th> 编号 </th> <th> 书名 </th> <th> 作者 </th> <th> 出版社 </th> <th> 出版日期 </th> <th>isnb</th> <th> 页数 </th> <th> 价格 </th> <th> 内容提要 </th> <th> 编辑 </th> <th> 删除 </th> </tr> <tbody> <s:iterator value="availableitems" status="stuts"> <tr align="center"> <td><s:property value="bookid"/></td> <td><s:property value="bookname"/></td> <td><s:property value="bookauthor"/></td> <td><s:property value="bookpublish"/></td> <td><s:text name="format.date"><s:param value="bookdate"/></s:text></td> QQ: QQ 群 : Page 101 of 162

102 <td><s:property value="bookisbn" /></td> <td><s:property value="bookpage" /></td> <td><s:property value="bookprice"/></td> <td><s:property value="bookcontent"/></td> <td><a href='<s:url action="edit"><s:param name="bookid" value="bookid" /></s:url>'> 编辑 </a></td> <td><a href='<s:url action="delete"><s:param name="bookid" value="bookid" /></s:url>'> 删除 </a></td> </tr> </s:iterator> <tr align="right"> <td colspan="11"> 共 <s:property value="totalrows"/> 行 第 <s:property value="currentpage"/> 页 共 <s:property value="pager.gettotalpages()"/> 页 <a href="<s:url value="list.action"> <s:param name="currentpage" value="currentpage"/> <s:param name="pagermethod" value="'first'"/> </s:url>"> 首页 </a> <a href="<s:url value="list.action"> <s:param name="currentpage" value="currentpage"/> <s:param name="pagermethod" value="'previous'"/> </s:url>"> 上一页 </a> <a href="<s:url value="list.action"> <s:param name="currentpage" value="currentpage"/> <s:param name="pagermethod" value="'next'"/> </s:url>"> 下一页 </a> <a href="<s:url value="list.action"> <s:param name="currentpage" value="currentpage"/> <s:param name="pagermethod" value="'last'"/> </s:url>"> 尾页 </a> </td> </tr> </tbody> </table> </body> </html> 4.5 修改书籍视图当需要修改某条信息时, 在列表页单击对应信息的 编辑, 系统将先查询并显示出该信息, 在信息修改完之后单击 修改 即可将修改后的数据保存到数据库, 并返回到列表页将所有的书籍信息显示出来 编辑页如图 7 所示 : QQ: QQ 群 : Page 102 of 162

103 图 7 编辑图书信息效果图页面主要代码如下 : <%@page pageencoding="utf-8" contenttype="text/html; charset=utf-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <html> <head> <title> 编辑图书 </title> <!-- 为了下面时间的插件正常显示, 此句不可少 --> <s:head theme="ajax"/> <style> td { font-size: 12px </style> </head> <body> <h2> <s:if test="null == book"> 增加图书 </s:if> <s:else> 编辑图书 </s:else> </h2> <s:form name="editform" action="save" validate="true" theme="simple"> <table width="250" height="253" border="1" cellpadding="1" cellspacing="0" style="border-style: inherit" bordercolordark="#ffffff"> <tr> <td width="36%"> 书名 :</td> QQ: QQ 群 : Page 103 of 162

104 <td><s:textfield name="book.bookname" /></td> </tr> <tr> <td> 作者 : </td> <td><s:textfield name="book.bookauthor" /></td> </tr> <tr> <td> 出版社 : </td> <td><s:textfield name="book.bookpublish" /></td> </tr> <tr> <td> 出版日期 : </td> <td><s:datetimepicker name="book.bookdate"></s:datetimepicker> </td> </tr> <tr> <td>isbn: </td> <td><s:textfield name="book.bookisbn" /></td> </tr> <tr> <td> 页数 : </td> <td><s:textfield name="book.bookpage" /></td> </tr> <tr> <td> 价格 ( 元 ): </td> <td><s:textfield name="book.bookprice" /></td> </tr> <tr> <td> 内容摘要 : </td> <td><s:textfield name="book.bookcontent" /></td> </tr> <tr> <td colspan="2" align="right"> <s:if test="null == book"> <s:hidden name="book.bookid" value="%{bookid" /> </s:if> <s:else> <s:hidden name="book.bookid" /> </s:else> <s:hidden name="queryname" /> <s:hidden name="queryvalue" /> <s:submit value="%{gettext(' 保存 ')" /> <a href="<s:url action="list"/>"> 返回 </a> </td> </tr> QQ: QQ 群 : Page 104 of 162

105 </table> </s:form> </body> </html> 细心的你应该已经发现该页其实还有肩负添加图书的责任, 不同的是添加的时候没有数据存在 4.6 图书查询视图作为管理员要管理的书籍肯定很多, 对于不计其数的书籍想要记住全部的信息是不太现实的, 为了满足这个需求, 需要提供强大的搜索功能 前面书籍列表页已经提到了, 如图 8 所示 : 图 8 搜索页效果图选择搜索类型 搜索关键字, 单击 查询, 系统将返回数据库中保存的书籍结果 若查询书名为 Java, 图 9 展示了搜索结果 : 图 9 搜索结果效果图 13.5 代码清单 5.1 Action 的实现 AbstractAction.java package com.ssh.commons; import com.opensymphony.xwork2.actionsupport; // 该类继承 ActionSupport 被作为超类使用 public class AbstractAction extends ActionSupport { BookAction.java 该 Action 处理有关书籍的操作 package com.ssh.books.web.actions; // 省略导入的相关包 public class BooksAction extends AbstractAction { private BooksService booksservice; private PagerService pagerservice; QQ: QQ 群 : Page 105 of 162

106 private Books book; private Pager pager; protected Collection availableitems; protected String currentpage; protected String pagermethod; protected String totalrows; protected String bookid; protected String queryname; protected String queryvalue; protected String searchname; protected String searchvalue; protected String querymap; public String list() throws Exception { if (querymap!= null!querymap.equals("")) { String[] str = querymap.split("~"); this.setqueryname(str[0]); this.setqueryvalue(str[1]); int totalrow = booksservice.getrows(this.getqueryname(), this.getqueryvalue()); pager = pagerservice.getpager(this.getcurrentpage(), this.getpagermethod(), totalrow); this.setcurrentpage(string.valueof(pager.getcurrentpage())); this.settotalrows(string.valueof(totalrow)); availableitems = booksservice.getbooks(this.getqueryname(), this.getqueryvalue(), pager.getpagesize(), pager.getstartrow()); this.setqueryname(this.getqueryname()); this.setqueryvalue(this.getqueryvalue()); this.setsearchname(this.getqueryname()); this.setsearchvalue(this.getqueryvalue()); return SUCCESS; public String load() throws Exception { if (bookid!= null) book = booksservice.getbook(bookid); else bookid = booksservice.getmaxid(); return SUCCESS; QQ: QQ 群 : Page 106 of 162

107 public String save() throws Exception { if (this.getbook().getbookprice().equals("")) { this.getbook().setbookprice("0.0"); String id = this.getbook().getbookid(); Books book = booksservice.getbook(id); if (book == null) booksservice.addbook(this.getbook()); else booksservice.updatebook(this.getbook()); this.setqueryname(this.getqueryname()); this.setqueryvalue(this.getqueryvalue()); if (this.getqueryname() == null this.getqueryvalue() == null this.getqueryname().equals("") this.getqueryvalue().equals("")) { else { querymap = this.getqueryname() + "~" + this.getqueryvalue(); return SUCCESS; public String delete() throws Exception { booksservice.deletebook(this.getbookid()); if (this.getqueryname() == null this.getqueryvalue() == null this.getqueryname().equals("") this.getqueryvalue().equals("")) { else { querymap = this.getqueryname() + "~" + this.getqueryvalue(); return SUCCESS; // 省略相关属性的 setter/getter 方法 LoginAction.java package com.ssh.books.web.actions; // 省略导入的相关包 public class LoginAction implements Action, ServletRequestAware { private User user; QQ: QQ 群 : Page 107 of 162

108 private UserService userservice; HttpServletRequest request = null; public String execute() throws Exception { String retval = ERROR; boolean islogin = userservice.islogin(user); if (islogin) retval = SUCCESS; request.setattribute("name", user.getname()); return retval; // 省略属性的 setter/getter 方法 5.2 Service 的实现 BooksServce.java package com.ssh.books.services.iface; import java.util.list; import com.ssh.books.model.books; public interface BooksService { public List getall();// 获得所有记录 public List getbooks(int pagesize, int startrow);// 获得所有记录 public int getrows();// 获得总行数 public int getrows(string fieldname, String value);// 获得总行数 public List querybooks(string fieldname, String value);// 根据条件查询 public List getbooks(string fieldname, String value, int pagesize,int startrow);// 根据条件查询 public Books getbook(string bookid);// 根据 ID 获得记录 public String getmaxid();// 获得最大 ID 值 public void addbook(books pd);// 添加记录 public void updatebook(books pd);// 修改记录 public void deletebook(string bookid);// 删除记录 QQ: QQ 群 : Page 108 of 162

109 UserService.java package com.ssh.books.services.iface; import com.ssh.books.model.user; public interface UserService { public boolean islogin(user user); public boolean islogin(string name, String password); 5.3 ServiceImpl 的实现 BooksServiceImpl.java package com.ssh.books.services; import java.util.list; import com.ssh.books.dao.iface.booksdao; import com.ssh.books.model.books; import com.ssh.books.services.iface.booksservice; public class BooksServiceImpl implements BooksService{ private BooksDao booksdao; public BooksServiceImpl(){ /** * 函数说明 : 添加信息 * 参数说明 : 对象 * 返回值 : */ public void addbook(books book) { booksdao.addbook(book); /** * 函数说明 : 删除信息 * 参数说明 : 对象 * 返回值 : */ public void deletebook(string bookid) { Books book=booksdao.getbook(bookid); booksdao.deletebook(book); /** * 函数说明 : 获得所有的信息 * 参数说明 : QQ: QQ 群 : Page 109 of 162

110 * 返回值 : 信息的集合 */ public List getall() { return booksdao.getall(); /** * 函数说明 : 获得总行数 * 参数说明 : * 返回值 : 总行数 */ public int getrows() { return booksdao.getrows(); /** * 函数说明 : 获得所有的信息 * 参数说明 : * 返回值 : 信息的集合 */ public List getbooks(int pagesize, int startrow) { return booksdao.getbooks(pagesize, startrow); /** * 函数说明 : 获得一条的信息 * 参数说明 : ID * 返回值 : 对象 */ public Books getbook(string bookid) { return booksdao.getbook(bookid); /** * 函数说明 : 获得最大 ID * 参数说明 : * 返回值 : 最大 ID */ public String getmaxid() { return booksdao.getmaxid(); /** * 函数说明 : 修改信息 * 参数说明 : QQ: QQ 群 : Page 110 of 162

111 * 返回值 : */ public void updatebook(books book) { booksdao.updatebook(book); /** * 函数说明 : 查询信息 * 参数说明 : 集合 * 返回值 : */ public List querybooks(string fieldname,string value) { return booksdao.querybooks(fieldname, value); /** * 函数说明 : 获得总行数 * 参数说明 : * 返回值 : 总行数 */ public int getrows(string fieldname,string value) { return booksdao.getrows(fieldname, value); /** * 函数说明 : 查询信息 * 参数说明 : 集合 * 返回值 : */ public List getbooks(string fieldname,string value,int pagesize, int startrow) { return booksdao.getbooks(fieldname, value,pagesize,startrow); public BooksDao getbooksdao() { return booksdao; public void setbooksdao(booksdao booksdao) { this.booksdao = booksdao; UserServiceImpl.java package com.ssh.books.services; QQ: QQ 群 : Page 111 of 162

112 import com.ssh.books.dao.iface.userdao; import com.ssh.books.model.user; import com.ssh.books.services.iface.userservice; public class UserServiceImpl implements UserService { private UserDao userdao; public void setuserdao(userdao userdao) { this.userdao = userdao; public boolean islogin(user user) { boolean islogin = false; try { User u = userdao.find(user.getname(), user.getpassword()); if (u!= null) { islogin = true; catch (Exception e) { System.out.println("isLogin error\n" + e.getmessage()); return islogin; public boolean islogin(string name, String password) { boolean islogin = false; try { User u = userdao.find(name, password); if (u!= null) { islogin = true; catch (Exception e) { System.out.println("isLogin error\n" + e.getmessage()); return islogin; 5.4 Dao 的实现 BooksDao.java package com.ssh.books.dao.iface; import java.util.list; import com.ssh.books.model.books; QQ: QQ 群 : Page 112 of 162

113 public interface BooksDao { public List getall();// 获得所有记录 public List getbooks(int pagesize, int startrow);// 获得所有记录 public int getrows();// 获得总行数 public int getrows(string fieldname, String value);// 获得总行数 public List querybooks(string fieldname, String value);// 根据条件查询 public List getbooks(string fieldname, String value, int pagesize, int startrow);// 根据条件查询 public Books getbook(string bookid);// 根据 ID 获得记录 public String getmaxid();// 获得最大 ID 值 public void addbook(books book);// 添加记录 public void updatebook(books book);// 修改记录 public void deletebook(books book);// 删除记录 UserDao.java package com.ssh.books.dao.iface; import com.ssh.books.model.user; public interface UserDao { public User find(string name, String password); 5.5 DaoImpl 的实现 BooksDaoImpl.java package com.ssh.books.dao.hibernate; import java.sql.sqlexception; import java.util.iterator; import java.util.list; import org.hibernate.hibernateexception; import org.hibernate.query; import org.hibernate.session; import org.springframework.orm.hibernate3.hibernatecallback; QQ: QQ 群 : Page 113 of 162

114 import org.springframework.orm.hibernate3.support.hibernatedaosupport; import com.ssh.books.dao.iface.booksdao; import com.ssh.books.model.books; import com.ssh.commons.publicutil; /** cwf * */ public class BooksDaoImpl extends HibernateDaoSupport implements BooksDao { public BooksDaoImpl(){ /** * 函数说明 : 添加信息 * 参数说明 : 对象 * 返回值 : */ public void addbook(books book) { this.gethibernatetemplate().save(book); /** * 函数说明 : 删除信息 * 参数说明 : 对象 * 返回值 : */ public void deletebook(books book) { this.gethibernatetemplate().delete(book); /** * 函数说明 : 获得所有的信息 * 参数说明 : * 返回值 : 信息的集合 */ public List getall() { String sql="from Books ORDER BY bookname"; return this.gethibernatetemplate().find(sql); /** * 函数说明 : 获得总行数 * 参数说明 : QQ: QQ 群 : Page 114 of 162

115 * 返回值 : 总行数 */ public int getrows() { String sql="from Books ORDER BY bookname"; List list=this.gethibernatetemplate().find(sql); return list.size(); /** * 函数说明 : 获得所有的信息 * 参数说明 : * 返回值 : 信息的集合 */ public List getbooks(int pagesize, int startrow) throws HibernateException { final int pagesize1=pagesize; final int startrow1=startrow; return this.gethibernatetemplate().executefind(new HibernateCallback(){ ); public List doinhibernate(session session) throws HibernateException, SQLException { // TODO 自动生成方法存根 Query query=session.createquery("from Books ORDER BY bookname"); query.setfirstresult(startrow1); query.setmaxresults(pagesize1); return query.list(); /** * 函数说明 : 获得一条的信息 * 参数说明 : ID * 返回值 : 对象 */ public Books getbook(string bookid) { return (Books)this.getHibernateTemplate().get(Books.class,bookId); /** * 函数说明 : 获得最大 ID * 参数说明 : * 返回值 : 最大 ID */ public String getmaxid() { String date=publicutil.getstrnowdate(); String sql="select MAX(bookId)+1 FROM Books "; QQ: QQ 群 : Page 115 of 162

116 String nostr = null; List ll = (List) this.gethibernatetemplate().find(sql); Iterator itr = ll.iterator(); if (itr.hasnext()) { Object noint = itr.next(); if(noint == null){ nostr = "1"; else{ nostr = noint.tostring(); if(nostr.length()==1){ nostr="000"+nostr; else if(nostr.length()==2){ nostr="00"+nostr; else if(nostr.length()==3){ nostr="0"+nostr; else{ nostr=nostr; return nostr; /** * 函数说明 : 修改信息 * 参数说明 : 对象 * 返回值 : */ public void updatebook(books pd) { this.gethibernatetemplate().update(pd); /** * 函数说明 : 查询信息 * 参数说明 : 集合 * 返回值 : */ public List querybooks(string fieldname,string value) { String sql="from Books where "+fieldname+" like '%"+value+"%'"+"order BY bookname"; return this.gethibernatetemplate().find(sql); /** * 函数说明 : 获得总行数 QQ: QQ 群 : Page 116 of 162

117 * 参数说明 : * 返回值 : 总行数 */ public int getrows(string fieldname,string value) { String sql=""; if(fieldname==null fieldname.equals("") fieldname==null fieldname.equals("")) sql="from Books ORDER BY bookname"; else sql="from Books where "+fieldname+" like '%"+value+"%'"+"order BY bookname"; List list=this.gethibernatetemplate().find(sql); return list.size(); /** * 函数说明 : 查询信息 * 参数说明 : 集合 * 返回值 : */ public List getbooks(string fieldname,string value,int pagesize, int startrow) { final int pagesize1=pagesize; final int startrow1=startrow; final String queryname=fieldname; final String queryvalue=value; String sql=""; if(queryname==null queryname.equals("") queryvalue==null queryvalue.equals("")) sql="from Books ORDER BY bookname"; else sql="from Books where "+fieldname+" like '%"+value+"%'"+"order BY bookname"; final String sql1=sql; return this.gethibernatetemplate().executefind(new HibernateCallback(){ ); public List doinhibernate(session session) throws HibernateException, SQLException { // TODO 自动生成方法存根 Query query=session.createquery(sql1); query.setfirstresult(startrow1); query.setmaxresults(pagesize1); return query.list(); UserDaoImpl.java QQ: QQ 群 : Page 117 of 162

118 package com.ssh.books.dao.hibernate; import java.util.list; import org.springframework.orm.hibernate3.support.hibernatedaosupport; import com.ssh.books.dao.iface.userdao; import com.ssh.books.model.user; public class UserDaoImpl extends HibernateDaoSupport implements UserDao { public User find(string name, String password) { List<User> list = this.gethibernatetemplate().findbynamedparam( "FROM User AS u WHERE u.name =:name AND u.password =:password", new String[] { "name", "password", new String[] { name, password ); if (list.size() == 0) return null; else return list.get(0); 5.6 其他功能分析 Pager.java 分页的 JavaBean package com.ssh.commons; public class Pager { private int totalrows; // 总行数 private int pagesize = 5; // 每页显示的行数 private int currentpage; // 当前页号 private int totalpages; // 总页数 private int startrow; // 当前页在数据库中的起始行 public Pager() { public Pager(int _totalrows) { totalrows = _totalrows; totalpages=totalrows/pagesize; int mod=totalrows%pagesize; if(mod>0){ totalpages++; currentpage = 1; QQ: QQ 群 : Page 118 of 162

119 startrow = 0; public int getstartrow() { return startrow; public int gettotalpages() { return totalpages; public int getcurrentpage() { return currentpage; public int getpagesize() { return pagesize; public void settotalrows(int totalrows) { this.totalrows = totalrows; public void setstartrow(int startrow) { this.startrow = startrow; public void settotalpages(int totalpages) { this.totalpages = totalpages; public void setcurrentpage(int currentpage) { this.currentpage = currentpage; public void setpagesize(int pagesize) { this.pagesize = pagesize; public int gettotalrows() { return totalrows; public void first() { currentpage = 1; startrow = 0; public void previous() { if (currentpage == 1) { return; currentpage--; startrow = (currentpage - 1) * pagesize; public void next() { QQ: QQ 群 : Page 119 of 162

120 if (currentpage < totalpages) { currentpage++; startrow = (currentpage - 1) * pagesize; public void last() { currentpage = totalpages; startrow = (currentpage - 1) * pagesize; public void refresh(int _currentpage) { currentpage = _currentpage; if (currentpage > totalpages) { last(); PagerService.java package com.ssh.commons; public class PagerService { public Pager getpager(string currentpage, String pagermethod, int totalrows) { // 定义 pager 对象, 用于传到页面 Pager pager = new Pager(totalRows); // 如果当前页号为空, 表示为首次查询该页 // 如果不为空, 则刷新 pager 对象, 输入当前页号等信息 if (currentpage!= null) { pager.refresh(integer.parseint(currentpage)); // 获取当前执行的方法, 首页, 前一页, 后一页, 尾页 if (pagermethod!= null) { if (pagermethod.equals("first")) { pager.first(); else if (pagermethod.equals("previous")) { pager.previous(); else if (pagermethod.equals("next")) { pager.next(); else if (pagermethod.equals("last")) { pager.last(); return pager; QQ: QQ 群 : Page 120 of 162

121 Struts.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" " <struts> <constant name="struts.enable.dynamicmethodinvocation" value="false" /> <constant name="struts.devmode" value="true" /> <constant name="struts.i18n.encoding" value="utf-8" /> <!-- Add packages here --> <package name="login" extends="struts-default"> <action name="login" class="loginaction"> <result name="success">/index.jsp</result> <result name="error">/error.jsp</result> </action> </package> <package name="products" extends="struts-default"> <!--default-interceptor-ref name="validation"/--> <!-- Add actions here --> <action name="list" class="bookaction" method="list"> <result>/list.jsp</result> </action> <action name="delete" class="bookaction" method="delete"> <result type="redirect"> list.action?querymap=${querymap </result> </action> <action name="*" class="com.ssh.commons.abstractaction"> </action> <result>/{1.jsp</result> <action name="edit" class="bookaction" method="load"> </action> <result>/editbook.jsp</result> <action name="save" class="bookaction" method="save"> <interceptor-ref name="params" /> <interceptor-ref name="validation" /> <result name="input">/editbook.jsp</result> <result type="redirect"> QQ: QQ 群 : Page 121 of 162

122 list.action?querymap=${querymap </result> </action> </package> </struts> applicationcontext.xml <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" " <beans> <!-- datasource config --> <bean id="datasource" class="org.apache.commons.dbcp.basicdatasource" destroy-method="close"> <property name="driverclassname" value="com.mysql.jdbc.driver" /> <property name="url" value="jdbc:mysql://localhost:3306/bookshell" /> <property name="username" value="root" /> <property name="password" value="root" /> </bean> <bean id="examplehibernateproperties" class="org.springframework.beans.factory.config.propertiesfactorybean"> <property name="properties"> <props> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.query.substitutions"> true 'T', false 'F' </prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.c3p0.minpoolsize">5</prop> <prop key="hibernate.c3p0.maxpoolsize">20</prop> <prop key="hibernate.c3p0.timeout">600</prop> <prop key="hibernate.c3p0.max_statement">50</prop> <prop key="hibernate.c3p0.testconnectiononcheckout"> false </prop> </props> </property> </bean> <!-- Hibernate SessionFactory --> <bean id="sessionfactory" class="org.springframework.orm.hibernate3.localsessionfactorybean"> <property name="datasource"> QQ: QQ 群 : Page 122 of 162

123 <ref bean="datasource" /> </property> <property name="hibernateproperties"> <props> <prop key="hibernate.dialect"> org.hibernate.dialect.mysqldialect </prop> <prop key="show_sql">true</prop> </props> </property> <property name="mappingresources"> <list> <value>com/ssh/books/model/books.hbm.xml</value> <value>com/ssh/books/model/user.hbm.xml</value> </list> </property> </bean> <!-- TransactionManager 声明式事务定义 --> <bean id="transactionmanager" class="org.springframework.orm.hibernate3.hibernatetransactionmanager"> <property name="sessionfactory"> <ref local="sessionfactory" /> </property> </bean> <bean id="transactioninterceptor" class="org.springframework.transaction.interceptor.transactioninterceptor"> <property name="transactionmanager" ref="transactionmanager" /> <property name="transactionattributes"> <props> <prop key="islogin*"> PROPAGATION_REQUIRED,readOnly </prop> <prop key="update*">propagation_required</prop> <prop key="add*">propagation_required</prop> <prop key="delete*">propagation_required</prop> </props> </property> </bean> <bean id="proxyuserservice" class="org.springframework.aop.framework.autoproxy.beannameautoproxycreator"> <property name="beannames"> <list> <value>userservice</value> QQ: QQ 群 : Page 123 of 162

124 <value>bookservice</value> </list> </property> <property name="interceptornames" value="transactioninterceptor" /> </bean> <bean id="bookaction" class="com.ssh.books.web.actions.booksaction" singleton="false"> <property name="booksservice"> <ref bean="booksservice" /> </property> <property name="pagerservice"> <ref bean="pagerservice" /> </property> </bean> <!-- Services --> <bean id="booksservice" class="com.ssh.books.services.booksserviceimpl"> <property name="booksdao"> <ref bean="booksdao" /> </property> </bean> <!-- DAO --> <bean id="booksdao" class="com.ssh.books.dao.hibernate.booksdaoimpl"> <property name="sessionfactory"> <ref bean="sessionfactory" /> </property> </bean> <bean id="pagerservice" class="com.ssh.commons.pagerservice" /> <!-- view --> <bean id="loginaction" class="com.ssh.books.web.actions.loginaction"> <property name="userservice" ref="userservice" /> </bean> <!-- Services --> <bean id="userservice" class="com.ssh.books.services.userserviceimpl"> <property name="userdao" ref="userdao" /> </bean> <!-- DAO --> <bean id="userdao" class="com.ssh.books.dao.hibernate.userdaoimpl"> <property name="sessionfactory" ref="sessionfactory" /> </bean> QQ: QQ 群 : Page 124 of 162

125 </beans> 更详细的代码请参看程序源代码, 这里不再一一列出 13.6 代码树形图 图 10 代码树形效果图 第 14 章集成 Spring 第 15 章集成 ibatis ibatis 是又一个 O/R Mapping 解决方案,j2ee 的 O/R 方案真是多, 和 Hibernate 相比,iBatis 最大的特点就是小巧, 上手很快 如果你不需要太多复杂的功能,iBatis 是能满足你的要求又足够灵活的最简单的解决方案 ibatis 最大的特点是简单, 最新版本 2.0, 只要有 SQL 基础, 这一章节即便没有 ibatis 基础也可 QQ: QQ 群 : Page 125 of 162

126 以阅读 有关 struts2 spring ibatis 的集成方案如下图所示 : 当服务容器启动的时候, 会根据 web.xml 中配置的 spring 路径, 初始化 spring 环境 Spring 会加载配置的 ibaits 环境 ibstis 的配置主要由两种文件 : 1 有关项目的总体配置, 如连接的数据源, 连接池, 缓存等的配置, 也即 sqlmapconfig.xml 文件的配置 2 sqlmap.xml 文件的配置, 也即对象与表的操作映射的配置 下面分别来讲述这两个配置文件的内容 sqlmapconfig.xml 首先查找 spring 配置文件, 查找 sqlmapconfig.xml 文件的位置 <bean id="sqlmapclient" class="org.springframework.orm.ibatis.sqlmapclientfactorybean"> <property name="configlocation" value="web-inf/classes/com/xmh/config/sqlmapconfig.xml" /> <property name="datasource" ref="datasource" /> </bean> SqlMapClient 对于 ibatis 的意义类似于 Session 与 Hibernate 以及 Connection 与 JDBC, 这里的 sqlmapclient 节点实际上配置了一个 sqlmapclient 的创建工厂类 configlocation 属性配置了 ibatis 映射文件的名称 sqlmapconfig.xml 文件包括了 DataSource 的详细配置信息 可以把相关的数据库配置信息写在属性文件中, 然后通过 <properties resource=" 路径 /SqlMapConfig.properties"/> 来获取配置文件 这样做在属性文件中定义的属性可以作为变量在 SqlMap 配置文件中以及多包含的 SqlMap 映射文件中使用 通过 Settings 元素优化 SqlMapClient 实例的各选项比如 :cachemodelsenabled 属性是否启用 SqlMapClient 的 model 的缓存处理,enhancementEnabled 属性, 是否运行时增强字节码, lazyloadingenabled 属性是否启用所有的延迟加载, 调试程序时使用,maxRequests 属性同时执行 SQL 语句的最大线程数, 大于这个值的线程将阻塞直到另一个线程执行结束, 不同的 DBMS 有不同的限定值, 减少这个参数值能提高性能, 通常是 maxtransations 数值的 10 倍,maxTransations 属性, 同时进入 SqlMapClient.startTransaction() 的最大线程数, 大于这个线程的数值将被阻塞, 直到另一个线程的结束 不同的 DBMS 有不同的限制, 这个参数值应该小于或等于 maxsessions 数值 --> <settings cachemodelsenabled="true" enhancementenabled="true" lazyloadingenabled="true" maxsessions="64" maxtransactions="12" maxrequests="128" /> QQ: QQ 群 : Page 126 of 162

127 最后为 SqlMap 配置事务管理服务,type 用来指定事务管理的类型, 这个属性值可以是一个类名, 也可以是一个别名 包含在框架中的事务管理器分别是 JDBC JTA EXTERNAL <transactionmanager type="jdbc"> <!-- datasource 元素为 SqlMap 数据源配置一系列的属性信息 --> <datasource type="jndi"> <property name="dbjndicontext" value="java:comp/env/jdbc/webpublish"/> </datasource> </transactionmanager> <sqlmap resource=" 路径 / 映射文件 "/> </sqlmapconfig> 因为数据源的配置与事务的配置均由 spring 负责, 所以在 spring 与组合的框架中, sqlmapconfig.xml 文件的内容被简化, 只需要设置 sqlmap 即可 <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE sqlmapconfig PUBLIC "-//ibatis.com//dtd SQL Map Config 2.0//EN" " <sqlmapconfig> <sqlmap resource="com/xmh/dao/impl/maps/sbook.xml"/> </sqlmapconfig> 映射文件, 亦即 sql 语句与对象间的桥梁文件 <insert id="savebook" parameterclass="sbook"> <selectkey keyproperty="id" resultclass="int"> <![CDATA[ SELECT LAST_INSERT_ID() AS VALUE ]]> </selectkey> <![CDATA[ INSERT INTO sbook(title,author,total,price,isbn,publisher) VALUES(#title#,#author#,#total#,#price#,#isbn#,#publisher#) ]]> </insert> 如果对上述内容还有疑义, 请参考相关的 ibatis 文档 下面通过一个简单的 CRUD 示例来了解 spring struts2 ibatis 的组合运用 项目的布局如下 : QQ: QQ 群 : Page 127 of 162

128 持久层代码分析 首先从 maps 下的 Sbook.xml 文件入手, 代码清单如下 : <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE sqlmap PUBLIC "-//ibatis.apache.org//dtd SQL Map 2.0//EN" " <sqlmap> <typealias alias="sbook" type="com.xmh.pojo.sbook" /> <!-- 添加一本新书 --> <insert id="savebook" parameterclass="sbook"> <selectkey keyproperty="id" resultclass="int"> <![CDATA[ SELECT LAST_INSERT_ID() AS VALUE ]]> </selectkey> <![CDATA[ INSERT INTO sbook(title,author,total,price,isbn,publisher) VALUES(#title#,#author#,#total#,#price#,#isbn#,#publisher#) ]]> </insert> QQ: QQ 群 : Page 128 of 162

129 <!-- 删除图书 --> <delete id="deletebook" parameterclass="int"> <![CDATA[ DELETE FROM SBOOK WHERE ID=#id# ]]> </delete> <!-- 通过出版社名称查找此出版社出版的图书 --> <select id="findbookbypublisher" parameterclass="string" resultclass="sbook"> <![CDATA[ SELECT * FROM sbook WHERE publisher=#publisher# ]]> </select> <!-- 通过图书唯一的 ISBN 号码查找图书 --> <select id="findbookbyisbn" parameterclass="string" resultclass="sbook"> <![CDATA[ SELECT * FROM sbook WHERE isbn=#isbn# ]]> </select> <!-- 查找所有的图书 --> <select id="findallbook" resultclass="sbook"> <![CDATA[ SELECT * FROM sbook ]]> </select> <!-- 更新图书信息 --> <update id="updatebook" parameterclass="sbook"> <![CDATA[ UPDATE SBOOK SET title=#title#,author=#author#,price=#price#,total=#total#,isbn=#isbn#,publisher=# publisher# WHERE id=#id# ]]> </update> <!-- 查找特定图书 --> <select id="findbookbyid" parameterclass="int" resultclass="sbook"> <![CDATA[ SELECT * FROM sbook WHERE ID=#id# ]]> </select> </sqlmap> 持久化层的核心代码如下 : package com.xmh.dao.impl; public class SBookDAO extends SqlMapClientDaoSupport implements ISBookDAO { public SBook findbookbyisbn(string isbn) throws RuntimeException { return (SBook) this.getsqlmapclienttemplate().queryforobject( "findbookbyisbn", isbn); QQ: QQ 群 : Page 129 of 162

130 @SuppressWarnings("unchecked") public List<SBook> findbooksbypublisher(string publisher) throws RuntimeException { return this.getsqlmapclienttemplate().queryforlist( "findbookbypublisher", publisher); public void savebook(sbook book) throws RuntimeException { this.getsqlmapclienttemplate().insert("savebook", book); public void deletebook(int id) throws RuntimeException { this.getsqlmapclienttemplate().delete("deletebook", id); public void updatebook(sbook book) throws RuntimeException { this.getsqlmapclienttemplate().update("updatebook", public List findallbook() throws RuntimeException { return this.getsqlmapclienttemplate().queryforlist("findallbook"); public SBook findbookbyid(int id) throws RuntimeException { return (SBook) this.getsqlmapclienttemplate().queryforobject( "findbookbyid", id); SqlMapClientDaoSupport 这个类为 Spring 的 ibatis 模版类 它提供很多模版方法, 并且 Spring 提供了异常处理, 使用比较方便 SqlMapClientDaoSupport 的 api 文档截图如下 : QQ: QQ 群 : Page 130 of 162

131 从文档中可以看出使用这个类必须要为其注入 SqlMapClient, 所以在 spring 的配置文件中, 需要如下设置 <!-- 注入 BookDAO 层 --> <bean id="sbookdao" class="com.xmh.dao.impl.sbookdao"> <property name="sqlmapclient" ref="sqlmapclient" /> </bean> 业务层调用持久层, 业务层的代码如下 : package com.xmh.services.impl; public class SBookServices implements ISBookServices { private ISBookDAO public List getallbook() throws RuntimeException { return sbookdao.findallbook(); public SBook getbookbyisbn(string isbn) throws RuntimeException { return sbookdao.findbookbyisbn(isbn); public List<SBook> getbooksbypublisher(string publisher) throws RuntimeException { return sbookdao.findbooksbypublisher(publisher); public void setsbookdao(isbookdao sbookdao) { this.sbookdao = sbookdao; QQ: QQ 群 : Page 131 of 162

132 Spring 容器为其注入持久层对象, 所以 spring 的配置文件中需要设置 : <bean id="sbookservices" class="com.xmh.services.impl.sbookservices"> <property name="sbookdao" ref="sbookdao"/> </bean> Strtus2 的控制层调用业务层, 控制层的代码如下 : package com.xmh.action; public class SBookAction extends ActionSupport{ private static final long serialversionuid = 1L; private ISBookServices sbookservices; private SBook sbook; private String tips; private String private List booklist; public void setsbookservices(isbookservices sbookservices) { this.sbookservices = public List getbooklist() { return booklist; 在整合的框架之下,struts2 中的 Action 是交由容器管理的 如下代码所示 : <bean id="sbookaction" class="com.xmh.action.sbookaction"> <property name="sbookservices" ref="sbookservices"/> </bean> 在 struts2 的配置文件中,action 就不再需要完整的路径 <action name="addsbook" class="sbookaction" method="addsbook"> <result name="success">/addbook.jsp</result> <result name="input">/addbook.jsp</result> <result name="error">/addbook.jsp</result> </action> 事务处理的方式 : pring 通过 AOP 来拦截方法的调用, 从而在这些方法上面添加声明式事务处理的能力 典型配置如下 : <!-- 配置事务特性 --> <tx:advice id="txadvice" transaction-manager=" 事务管理器名称 "> <tx:attributes> <tx:method name="add*" propagation="required"/> <tx:method name="del*" propagation="required"/> <tx:method name="update*" propagation="required"/> <tx:method name="*" read-only="true"/> </tx:attributes> </tx:advice> 配置哪些类的方法需要进行事务管理 QQ: QQ 群 : Page 132 of 162

133 <aop:config> <aop:pointcut id="allmanagermethod" expression="execution(* com.ibatis.manager.*.*(..))"/> <aop:advisor advice-ref="txadvice" pointcut-ref="allmanagermethod"/> </aop:config> 下面对 execution 进行说明 : execution(modifier-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?) 括号中各个 pattern 分别表示修饰符匹配 (modifier-pattern?) 返回值匹配(ret-type-pattern) 类路径匹配 (declaring-type-pattern?) 方法名匹配(name-pattern) 参数匹配((param-pattern)) 异常类型匹配 (throws-pattern?), 其中后面跟着? 的是可选项 在各个 pattern 中可以使用 * 来表示匹配所有 在 (param-pattern) 中, 可以指定具体的参数类型, 多个参数间用, 隔开, 各个也可以用 * 来表示匹配任意类型的参数, 如 (String) 表示匹配一个 String 参数的方法 ;(*,String) 表示匹配有两个参数的方法, 第一个参数可以是任意类型, 而第二个参数是 String 类型 ; 可以用 (..) 表示零个或多个任意参数 现在来看看几个例子 : 1)execution(* *(..)) 表示匹配所有方法 2 ) execution(public * com. savage.service.userservice.*(..)) 表示匹配 com.savage.server.userservice 中所有的公有方法 3)execution(* com.savage.server..*.*(..)) 表示匹配 com.savage.server 包及其子包下的所有方法注意 : 这些事务都是声明在业务逻辑层的对象上的 创建一个事务管理器, 对事务进行管理 <bean id="txmanager" class=" 包名.DataSourceTransactionManager"> <property name="datasource" ref="datasource"/> </bean> <bean id="datasource" class="org.apache.commons.dbcp.basicdatasource"> <property name="driverclassname" value="com.mysql.jdbc.driver"/> <property name="url" value="jdbc:mysql://localhost/ 数据库名 "/> <property name="username" value=" 用户名 "/> <property name="password" value=" 密码 "/> </bean> 其它部分代码请参见本书源代码部分, 部署此工程, 输入相应的访问 URL, 将会获得如下图所示的效果 QQ: QQ 群 : Page 133 of 162

134 第 16 章集成 Jquery 16.1 什么是 Jquery jquery 是一个 JavaScript 库, 它有助于简化 JavaScript 以及 Asynchronous JavaScript + XML (Ajax) 编程 与类似的 JavaScript 库不同,jQuery 具有独特的基本原理, 可以简洁地表示常见的复杂代码 学习 jquery 基本原理, 探索其特性和功能, 执行一些常见的 Ajax 任务并掌握如何使用插件扩展 jquery jquery 由 John Resig 创建于 2006 年初, 对于任何使用 JavaScript 代码的程序员来说, 它是一个非常有用的 JavaScript 库 无论您是刚刚接触 JavaScript 语言, 并且希望获得一个能解决文档对象模型 (Document Object Model,DOM) 脚本和 Ajax 开发中一些复杂问题的库, 还是作为一个厌倦了 DOM 脚本和 Ajax 开发中无聊的重复工作的资深 JavaScript 专家,jQuery 都会是您的首选 jquery 能帮助您保证代码简洁易读 您再也不必编写大堆重复的循环代码和 DOM 脚本库调用了 使用 jquery, 您可以把握问题的要点, 并使用尽可能最少的代码实现您想要的功能 jquery 可以跨浏览器进行 DOM 操作 事件处理 样式更换和外部通信 它超轻巧 (20KB), 并且执行速度超快, 所以赢得了众多 JS 开发者的青睐, 至少, 在我刚完成的一个彩票投注系统中就大量的应用到 jquery, 也使得我有机会在此把工作中的应用体会补充在本文档之中 16.2 Jquery 操作 CSS Jquery 对 css 的操作相当方便, 能很方便我们去通过 js 修改 css 传统 javascript 对 css 的操作相当繁琐, 比如 <div id="a" style="background:blue">css</div> 取它的 background 语法是 : document.getelementbyid("a").style.background 而 jquery 对 css 更方便的操作 : $("#a").background(); $("#a").background( red ) 说明如下 QQ: QQ 群 : Page 134 of 162

135 $("#a") 得到 jquery 对象 [ <div id="a" /div> ] $("#a").background() 将取出该对象的 background 样式 $("#a").background( red ) 将该对象的 background 样式设为 red jquery 提供了以下方法, 来操作 css: 这里需要讲解一下 css(name);css(prop);css(key, value), 其他的看名字都知道什么作用了! <div id="a" style="background:blue; color:red">css</div><p id="b">test</p> css(name): 获取样式名为 name 的样式 $("#a").css("color"): 将得到样式中 color 值 red,("#a").css("background ") 将得到 blue css(prop):prop 是一个 hash 对象, 用于设置大量的 css 样式 $("#b").css({ color: "red", background: "blue" ); 最终效果是 <p id="b" style="background:blue; color:red">test</p> { color: "red", background: "blue",hash 对象,color 为 key,"red" 为 value, css(key, value) 用于设置一个单独得 css 样式 $("#b").css("color","red"); 最终效果是 <p id="b" style="color:red">test</p> 下面通过用 jquery 来控制文字大小来学习 jquery 对 css 的操控 : 单击上面的按钮, 文字变大 : QQ: QQ 群 : Page 135 of 162

136 单击下面的按钮, 文字变小 : 代码如下所示 : <script language="javascript"> $(document).ready(function(){ $('div.button').click(function(){ // 增大或减小字体 var $speech=$('div.speech'); var currentsize=$speech.css('fontsize'); //alert(currentsize); var num=parsefloat(currentsize,10); //alert(num);// 这是返回的是数字 var unit=currentsize.slice(-2); //alert(unit);// 这里反回的是 PX.. if(this.id=='switcher-large'){ num*=1.5; else if(this.id=='switcher-small'){ num/=1.5; $speech.css('fontsize',num+unit);//num+unit 就等价于数字 +px( 例如 22px) ); ); </script> 详细代码请参看源码部分 16.3 Jquery 操作 DOM 在 HTML 中 <img id="a" scr="5.jpg"/> 是经常可见的代码 在原始的 javascript 里面可以用 var o=document.getelementbyid('a') 取的 id 为 a 的节点对象, 在用 o.src 来取得或修改该节点的 scr 属性, 在 jquery 里 $("#a") 将得到 jquery 对象 [ <img id="a" scr="5.jpg"/> ], 然后可以用 jquery 提供的很多方法来进行操作, 如 $("#a").scr() 将得到 5.jpg,$("#a").scr("1.jpg") 将该对象 src 属性改为 1,jpg 下面我们来讲 jquery 提供的众多 jquery 方法, 方便大家快速对 DOM 对象进行操作 herf() herf(val) 说明 : 对 jquery 对象属性 herf 的操作 QQ: QQ 群 : Page 136 of 162

137 例子 : 未执行 jquery 前 <a href="1.htm" id="test" onclick="jq()">jquery</a> jquery 代码及功能 : function jq(){ alert($("#test").href()); $("#test").href("2.html"); 运行 : 先弹出对话框显示 id 为 test 的连接 url, 在将其 url 改为 2.html, 当弹出对话框后会看到转向到 2.html 同理,jQuery 还提供类似的其他方法, 大家可以分别试验一下 : herf() herf(val) html() html(val) id() id (val) name() name (val) rel() rel (val) src() src (val) title() title (val) val() val(val) 操作 after(html) 在匹配元素后插入一段 html <a href="#" id="test" onclick="jq()">jquery</a> jquery 代码及功能 : function jq(){ $("#test").after("<b>hello</b>"); 执行后相当于 : <a href="#" id="test" onclick="jq()">jquery</a><b>hello</b> after(elem) after(elems) 将指定对象 elem 或对象组 elems 插入到在匹配元素后 <p id="test">after</p><a href="#" onclick="jq()">jquery</a> jquery 代码及功能 function jq(){ $("a").after($("#test")); 执行后相当于 <a href="#" onclick="jq()">jquery</a><p id="test">after</p> append(html) 在匹配元素内部, 且末尾插入指定 html <a href="#" id="test" onclick="jq()">jquery</a> jquery 代码及功能 : function jq(){ $("#test").append("<b>hello</b>"); 执行后相当于 <a href="#" onclick="jq()">jquery<b>hello</b></a> 同理还有 append(elem) append(elems) before(html) before(elem) before(elems) 请执行参照 append 和 after 的方来测试 理解! appendto(expr) 与 append(elem) 相反 <p id="test">after</p><a href="#" onclick="jq()">jquery</a> jquery 代码及功能 function jq(){ $("a"). appendto ($("#test")); QQ: QQ 群 : Page 137 of 162

138 执行后相当于 <p id="test">after<a href="#" onclick="jq()">jquery</a> </p> clone() 复制一个 jquery 对象 <p id="test">after</p><a href="#" onclick="jq()">jquery</a> jquery 代码及功能 : function jq(){ $("#test").clone().appendto($("a")); 复制 $("#test") 然后插入到 <a> 后, 执行后相当于 <p id="test">after</p><a href="#" onclick="jq()">jquery</a><p id="test">after</p> empty() 删除匹配对象的所有子节点 <div id="test"> <span>span</span> <p>after</p> </div> <a href="#" onclick="jq()">jquery</a> jquery 代码及功能 : function jq(){ $("#test").empty(); 执行后相当于 <div id="test"></div><a href="#" onclick="jq()">jquery</a> insertafter(expr) insertbefore(expr) insertafter(expr) 相当于 before(elem),insertbefore(expr) 相当于 after (elem) prepend (html) prepend (elem) prepend (elems) 在匹配元素的内部且开始出插入通过下面例子区分 append(elem) appendto(expr) prepend (elem) <p id="a">p</p> <div>div</div> 执行 $("#a").append($("div")) 后相当于 <p id="a"> P <div>div</div> </p> 执行 $("#a").appendto($("div")) 后相当于 <div> div <p id="a">p</p> </div> 执行 $("#a").prepend ($("div")) 后相当于 <p id="a"> <div>div</div> P </p> remove() 删除匹配对象注意区分 empty(),empty() 移出匹配对象的子节点,remove(), 移出匹配对象 wrap(htm) 将匹配对象包含在给出的 html 代码内 <p>test Paragraph.</p> <a href="#" onclick="jq()">jquery</a> QQ: QQ 群 : Page 138 of 162

139 jquery 代码及功能 : function jq(){ $("p").wrap("<div class='wrap'></div>"); 执行后相当于 <div class='wrap'><p>test Paragraph.</p></div> wrap(elem) 将匹配对象包含在给出的对象内 <p>test Paragraph.</p><div id="content"></div> <a href="#" onclick="jq()">jquery</a> jquery 代码及功能 : function jq(){ $("p").wrap( document.getelementbyid('content') ); 执行后相当于 <div id="content"><p>test Paragraph.</p></div> 遍历 组合 add(expr) 在原对象的基础上在附加符合指定表达式的 jquery 对象 <p>hello</p><p><span>hello Again</span></p> <a href="#" onclick="jq()">jquery</a> jquery 代码及功能 : function jq(){ var f=$("p").add("span"); for(var i=0;i < $(f).size();i++){ alert($(f).eq(i).html()); 执行 $("p") 得到匹配 <p> 的对象, 有两个,add("span") 是在 ("p") 的基础上加上匹配 <span > 的对象, 所有一共有 3 个, 从上面的函数运行结果可以看到 $("p").add("span") 是 3 个对象的集合, 分别是 [<p>hello</p>],[<p><span>hello Again</span></p>],[<span>Hello Again</span>] add(el) 在匹配对象的基础上在附加指定的 dom 元素 $("p").add(document.getelementbyid("a")); add(els) 在匹配对象的基础上在附加指定的一组对象,els 是一个数组 <p>hello</p><p><span>hello Again</span></p> jquery 代码及功能 : function jq(){ var f=$("p").add([document.getelementbyid("a"), document.getelementbyid("b")]) for(var i=0;i < $(f).size();i++){ alert($(f).eq(i).html()); 注意 els 是一个数组, 这里的 [ ] 不能漏掉 ancestors () 一依次以匹配结点的父节点的内容为对象, 根节点除外 <div> <p>one</p> <span> <u>two</u> </span> </div> jquery 代码及功能 : function jq(){ QQ: QQ 群 : Page 139 of 162

140 var f= $("u").ancestors(); for(var i=0;i < $(f).size();i++){ alert($(f).eq(i).html()); 第一个对象是以 <u> 的父节点的内容为对象,[ <u>two</u> ] 第一个对象是以 <u> 的父节点的父节点 (div) 的内容为对象,[<p>one</p><span><u>two</u></span> ] 一般一个文档还有 <body> 和 <html>, 依次类推下去 ancestors (expr) 在 ancestors() 的基础上之取符合表达式的对象如上各例子讲 var f 改为 var f= $("u").ancestors( div ), 则只返回一个对象 : [ <p>one</p><span><u>two</u></span> ] children() 返回匹配对象的子介点 <p>one</p> <div id="ch"> <span>two</span> </div> jquery 代码及功能 : function jq(){ alert($("#ch").children().html()); $("#ch").children() 得到对象 [ <span>two</span> ]. 所以.html() 的结果是 two children(expr) 返回匹配对象的子介点中符合表达式的节点 <div id="ch"> <span>two</span> <span id="sp">three</span> </div> jquery 代码及功能 function jq(){ alert($("#ch").children( #sp ).html()); $("#ch").children() 得到对象 [<span>two</span><span id="sp">three</span> ]. $("#ch").children( #sp ) 过滤得到 [<span id="sp">three</span> ] parent () parent (expr) 取匹配对象父节点的 参照 children 帮助理解 contains(str) 返回匹配对象中包含字符串 str 的对象 <p>this is just a test.</p><p>so is this</p> jquery 代码及功能 : function jq(){ alert($("p").contains("test").html()); $("p") 得到两个对象, 而包含字符串 test 只有一个 所有 $("p").contains("test") 返回 [ <p>this is just a test.</p> ] end() 结束操作, 返回到匹配元素清单上操作前的状态. filter(expr) filter(exprs) 过滤现实匹配符合表达式的对象 exprs 为数组, 注意添加 [ ] <p>hello</p><p>hello Again</p><p class="selected">and Again</p> jquery 代码及功能 : function jq(){ alert($("p").filter(".selected").html()) QQ: QQ 群 : Page 140 of 162

141 $("p") 得到三个对象,$("p").contains("test") 只返回 class 为 selected 的对象 find(expr) 在匹配的对象中继续查找符合表达式的对象 <p>hello</p><p id="a">hello Again</p><p class="selected">and Again</p> Query 代码及功能 : function jq(){ alert($("p").find("#a").html()) 在 $("p") 对象中查找 id 为 a 的对象 is(expr) 判断对象是否符合表达式, 返回 boolen 值 <p>hello</p><p id="a">hello Again</p><p class="selected">and Again</p> Query 代码及功能 : function jq(){ alert($("#a").is("p")); next() next(expr) 返回匹配对象剩余的兄弟节点 <p>hello</p><p id="a">hello Again</p><p class="selected">and Again</p> jquery 代码及功能 function jq(){ alert($("p").next().html()); alert($("p").next(".selected").html()); $("p").next() 返回 [ <p id="a">hello Again</p>, <p class="selected">and Again</p> ] 两个对象 ;$("p").next(".selected) 只返回 [<p class="selected">and Again</p> ] 一个对象 prev () prev (expr) 参照 next 理解 not(el) not(expr) 从 jquery 对象中移出匹配的对象,el 为 dom 元素,expr 为 jquery 表达式 <p>one</p><p id="a">two</p> <a href="#" onclick="js()">jquery</a> jquery 代码及功能 : function js(){ alert($("p").not(document.getelementbyid("a")).html()); alert($("p").not( #a ).html()); $("p") 由两个对象, 排除后的对象为 [<p>one</p> ] siblings () siblings (expr) jquery 匹配对象中其它兄弟级别的对象 <p>one</p> <div> <p id="a">two</p> </div> <a href="#" onclick="js()">jquery</a> jquery 代码及功能 : function js(){ alert($("div").siblings().eq(1).html()); $("div").siblings() 的结果实返回两个对象 [<p>one</p>, <a href="#" onclick="js()">jquery</a> ] alert($("div").siblings( a ) 返回一个对象 [<a href="#" onclick="js()">jquery</a> ] QQ: QQ 群 : Page 141 of 162

142 其他 addclass(class) 为匹配对象添加一个 class 样式 removeclass (class) 将第一个匹配对象的某个 class 样式移出 attr (name) 获取第一个匹配对象的属性 <img src="test.jpg"/><a href="#" onclick="js()">jquery</a> jquery 代码及功能 : function js(){ alert($("img").attr("src")); 返回 test.jpg attr (prop) 为第一个匹配对象的设置属性,prop 为 hash 对象, 用于为某对象批量添加众多属性 <img/><a href="#" onclick="js()">jquery</a> jquery 代码及功能 : function js(){ $("img").attr({ src: "test.jpg", alt: "Test Image" ); 运行结果相当于 <img src="test.jpg" alt="test Image"/> attr (key,value) 为第一个匹配对象的设置属性,key 为属性名,value 为属性值 <img/><a href="#" onclick="js()">jquery</a> jquery 代码及功能 function js(){ $("img").attr( src, test.jpg ); 运行结果相当于 <img src="test.jpg"/> removeattr (name) 将第一个匹配对象的某个属性移出 <img alt="test"/><a href="#" onclick="js()">jquery</a> jquery 代码及功能 : function js(){ $("img"). removeattr("alt"); 运行结果相当于 <img /> toggleclass (class) 将当前对象添加一个样式, 不是当前对象则移出此样式, 返回的是处理后的对象 <p>hello</p><p class="selected">hello Again</p><a href="#" onclick="js()">jquery</a> $("p") 的结果是返回对象 [<p>hello</p>,<p class="selected">hello Again</p> ] $("p").toggleclass("selected") 的结果是实返回对象 [ <p class="selected">hello</p>, <p>hello Again</p> ] 16.4 Jquery 处理 text 经常会在页面上运用异步效果, 下面就来看看用 jquery 实现登录效果 QQ: QQ 群 : Page 142 of 162

143 代码如下所示 : <script type="text/javascript" language="javascript" src="/js/jquery min.js"></script> <!-- 以下是修改功能代码 //--> <script type="text/javascript"> function sendinsertrequest() { var username = $("#username").val(); var pass = $("#pass").val(); $.ajax({ type: "POST",// 指定是 post 还是 get, 当然此处要提交, 当然就要用 post 了 cache: "false",//( 默认 : true,datatype 为 script 时默认为 false) jquery 1.2 新功能, 设置为 false 将不会从浏览器缓存中加载请求信息 url: "ajax?method=insertcustomer",// 发送请求的地址 data: "username=" + username + "&pass=" + pass,// 发送到服务器的数据 datatype: "text",// 返回纯文本字符串 timeout:10000,// 设置请求超时时间 ( 毫秒 ) error: function (XMLHttpRequest, textstatus, errorthrown) {// 请求失败时调用函数 $("#infospan").html("<img src='/assets/images/standard_msg_error.gif'/><span class='estilo11' style='color: #aaaaaa'> 请求失败!</span>");, success:responseinsertrequest// 请求成功后回调函数 ); return false; function responseinsertrequest(message) { alert(message); if(message=="ok") { window.location.href = "/welcome.jsp"; else{ window.location.href = "/insert.jsp"; </script> QQ: QQ 群 : Page 143 of 162

144 16.5 Jquery 处理 xml 16.4 Jquery 处理 json 第 17 章投票管理系统 系统的登录窗口 : 第 18 章无纸化办公管理系统 办公系统个人通讯录部分列表页面效果 : QQ: QQ 群 : Page 144 of 162

145 添加联系人 : 修改联系人资料 : 首先, 注意在截图的中, 大家会发现访问的地址后缀为.do 格式, 在前面的示例中都采用的是.action 方式, 如何来进行相关的设置呢? struts.properties 文件该文件定义了 Struts 2 框架的大量属性, 开发者可以通过改变这些属性来满足应用的需求 struts.properties 文件是一个标准的 Properties 文件, 该文件包含了系列的 key-value 对象, QQ: QQ 群 : Page 145 of 162

146 key 就是一个 Struts 2 属性 该 key 对应的 value 就是一个 Struts 2 属性值 struts.properties 文件通常放在 Web 应用的 WEB-INF/classes 路径下 实际上, 只要将该文件放在 Web 应用的 CLASSPATH 路径下,Struts 2 框架就可以加载该文件 struts.properties 配置文件提供了一种改变框架默认行为的机制 一般来讲我们没必要修改这个文件, 除非你想拥有一个更加友好的开发调试环境 struts.properties 文件中所包含的所有属性都可以在 web.xml 配置文件中使用 init-param 标签进行配置, 或者在 struts.xml 文件中使用 constant 标签进行配置 dao 只是针对数据的操作层,service 是业务逻辑, 因为业务逻辑很复杂就涉及到事务的管理, 而且他还可以提供对外的服务, 比如 webservice 服务, 都是由 service 做的, action 只负责请求转发, 因为我们的业务需求简单, 读者朋友请不要把 dao 和 service 做的事情视为一样, 这里要强调一下 : 事务在 service, 业务逻辑却在 service 在 SSH2 的整合过程中, 我建议采用如上图所示的组合方式 : 将 Action 的创建交由 Spring 管理, 在 struts.xml 中直接引用创建的对象即可 并且 Action 所需的对象直接通过设置注入即可 在 spring 中调用 hibernate 建议采用继承 HibernateDaoSupport 的方式进行 下面来看看相关内容清单 : Login.jsp <%@ page contenttype="text/html; charset=utf-8"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title><s:text name="login.page.title" /> </title> </head> <body> <form name="form1" action="login.do" method="post"> <table width="300" border="1"> <tr> <td colspan="2"> <s:text name="login.page.title" /> </td> </tr> <tr> <td> <s:text name="login.page.username" /> </td> <td> <s:textfield name="username" /> QQ: QQ 群 : Page 146 of 162

147 <s:fielderror> <s:param>username</s:param> </s:fielderror> </td> </tr> <tr> <td> <s:text name="login.page.password" /> </td> <td> <s:password name="password" /> <s:fielderror> <s:param>password</s:param> </s:fielderror> </td> </tr> <tr> <td colspan="2"> <s:submit key="login.page.login" /> <a href="register!init.do"><s:text name="login.page.register" /> </a> </td> </tr> </table> <br> <br> <s:actionerror /> <s:actionmessage /> </form> </body> </html> <s:text../> 是从资源文件中读取相关数据, 查看配置文件,login 对应的是 LoginAction, <s:textfield > <s:password> 标签分别是绑定 LoginAction 相应的 set 方法 <s:fielderror> 是当验证出错时, 要显示的相关设置 当用户单击 登录 时, 将提交 login.do 请求, 由 loginaction 处理 这里未指明具体的方法, 所以请求将交由 execute() 方法处理 <action name="login" class="loginaction"> <result name="success">welcome.jsp</result> <result name="input">login.jsp</result> </action> 处理成功将返回 welcome.jsp 页面, 否则将返回 login.jsp 页面 package com.demo.struts2.actions; public class LoginAction extends ActionSupport { private static final long serialversionuid = 1L; private UserDAO userdao; private String username; private String password; public void validate() { QQ: QQ 群 : Page 147 of 162

148 clearerrorsandmessages(); if (username == null username.equals("")) { addfielderror("username", gettext("login.error.username")); if (password == null password.equals("")) { addfielderror("password", gettext("login.error.password")); public String execute() throws Exception { if (this.userdao.isvalid(username, password)) { ActionContext.getContext().getSession().put(Constants.USERNAME_KEY, username); return SUCCESS; else { super.addactionerror(super.gettext("login.message.failed")); return INPUT; public UserDAO getuserdao() { return userdao; public void setuserdao(userdao userdao) { this.userdao = userdao; public String getusername() { return username; public void setusername(string username) { this.username = username; public String getpassword() { return password; public void setpassword(string password) { this.password = password; execute() 方法在执行前会先调用 validate() 方法 在 IUserDAO 是一个接口, 它里面定义了与底层数据库打交道的方法, 代码清单如下所示 : package com.demo.hibernate.dao; QQ: QQ 群 : Page 148 of 162

149 import java.util.list; import com.demo.hibernate.beans.user; public interface IUserDAO { public boolean isvalid(final String username, final String password); public boolean isexist(string username); public void insertuser(user user); public User getuser(string userid); public List getusers(); public void deleteuser(string userid); 在这儿只讨论 isvalid 方法, 查看其实现类的代码如下所示 : public class UserDAO extends BaseDao implements IUserDAO { public boolean isvalid(final String username, final String password) { List list = (List) gethibernatetemplate().execute( new HibernateCallback() { public Object doinhibernate(session session) throws HibernateException { List result = session.createcriteria(user.class).add( Restrictions.eq("username", username)).add( Restrictions.eq("password", password)).list(); return result; ); if (list.size() > 0) { return true; else { return false; 可能读者朋友对 BaseDao 感觉陌生 它的代码清单如下所示 : package com.demo.hibernate.dao; import org.springframework.orm.hibernate3.support.hibernatedaosupport; public class BaseDao extends HibernateDaoSupport { 因为在 spring 的配置文件中, 要对底层的实现类注入 SessionFactory 对象, 如果实现类较多的话, 配置文件看上去就显得不够简炼 : <bean id="userdao" class="com.demo.hibernate.dao.userdao"> <property name="sessionfactory"> <ref local="sessionfactory" /> </property> QQ: QQ 群 : Page 149 of 162

150 </bean> <bean id="addressdao" class="com.demo.hibernate.dao.addressdao"> <property name="sessionfactory"> <ref local="sessionfactory" /> </property> </bean> 如果采用一个类文件过渡一下, 则只需要给父类注入 SessionFactory 对象即可, 代码看上去清爽多了, 如下所示 : <!-- 定义 DAO --> <bean id="basedao" class="com.demo.hibernate.dao.basedao"> <property name="sessionfactory" ref="sessionfactory" /> </bean> <bean id="userdao" class="com.demo.hibernate.dao.userdao" parent="basedao" /> <bean id="addressdao" class="com.demo.hibernate.dao.addressdao" parent="basedao" /> 在编码的时候推荐使用回调函数, 采用这种做法的好处是不用关心事务 session 的创建和销毁, 一切都在程序内部完成 它的名字 doinhibernate 即已说明它的功能 只是代码看起来有些凌乱 gethibernatetemplate().getsessionfactory() 有 getcurrentsession () 和 opensession() 两个方法, 前者表示使用当前的 session, 后者表示重新建立一个新的 session 但是这种做法, 是需要自己去手动关闭 session 的 所以你需要配置 opensessioninview, 个人认为不推荐使用! 再一次温故回调函数的写法 public boolean isexist(final String username) { List list = (List) gethibernatetemplate().execute(new HibernateCallback() { public Object doinhibernate(session session) throws HibernateException { List result = session.createcriteria(user.class).add( Restrictions.eq("username", username)).list(); return result; ); if (list.size() > 0) { return true; else { return false; 业务接口与业务层关系如下 : 具体的代码参见源文件 在前面已表述 service 层的作用, 请读者朋友不要将 service 层与 dao 层的作用混为一谈 下面看看 Action 部分的代码 :... QQ: QQ 群 : Page 150 of 162

Microsoft Word - Hibernate与Struts2和Spring组合指导.doc

Microsoft Word - Hibernate与Struts2和Spring组合指导.doc 1.1 组合 Hibernate 与 Spring 1. 在 Eclipse 中, 新建一个 Web project 2. 给该项目增加 Hibernate 开发能力, 增加 Hibernate 相关类库到当前项目的 Build Path, 同时也提供了 hibernate.cfg.xml 这个配置文件 3. 给该项目增加 Spring 开发能力, 增加 spring 相关类库到当前项目的 Build

More information

本章学习目标 小风 Java 实战系列教程 SpringMVC 简介 SpringMVC 的入门案例 SpringMVC 流程分析 配置注解映射器和适配器 注解的使用 使用不同方式的跳转页面 1. SpringMVC 简介 Spring web mvc

本章学习目标 小风 Java 实战系列教程 SpringMVC 简介 SpringMVC 的入门案例 SpringMVC 流程分析 配置注解映射器和适配器 注解的使用 使用不同方式的跳转页面 1. SpringMVC 简介 Spring web mvc 本章学习目标 SpringMVC 简介 SpringMVC 的入门案例 SpringMVC 流程分析 配置注解映射器和适配器 配置视图解析器 @RequestMapping 注解的使用 使用不同方式的跳转页面 1. SpringMVC 简介 Spring web mvc 和 Struts2 都属于表现层的框架, 它是 Spring 框架的一部分, 我们可 以从 Spring 的整体结构中看得出来 :

More information

在所有的项目开发中, 一定是多人协作的团队开发, 但是使用框架就会出现一个问题, 我们所 有的 Action 以及相关的路径都要求在我们的 struts.xml 文件中配置, 如果所有的人去修改一个 文件, 那么就会变得混乱, 而且有可能出现冲突, 那么在 struts.xml 文件中为了解决这个问

在所有的项目开发中, 一定是多人协作的团队开发, 但是使用框架就会出现一个问题, 我们所 有的 Action 以及相关的路径都要求在我们的 struts.xml 文件中配置, 如果所有的人去修改一个 文件, 那么就会变得混乱, 而且有可能出现冲突, 那么在 struts.xml 文件中为了解决这个问 内置对象的取得和多人开发 一 内置对象的取得 在使用的 servlet 的时候可以通过 HttpServletResquest 获取到一些内置对象, 但是在 struts2 中为了方便取得内置对象, 专门提供了一个 ServletActionContext 这个类取得取得内置对象, 观察如下方法 public static javax.servlet.jsp.pagecontext() 取得 pagecontext

More information

untitled

untitled JavaEE+Android - 6 1.5-2 JavaEE web MIS OA ERP BOSS Android Android Google Map office HTML CSS,java Android + SQL Sever JavaWeb JavaScript/AJAX jquery Java Oracle SSH SSH EJB+JBOSS Android + 1. 2. IDE

More information

(TestFailure) JUnit Framework AssertionFailedError JUnit Composite TestSuite Test TestSuite run() run() JUnit

(TestFailure) JUnit Framework AssertionFailedError JUnit Composite TestSuite Test TestSuite run() run() JUnit Tomcat Web JUnit Cactus JUnit Java Cactus JUnit 26.1 JUnit Java JUnit JUnit Java JSP Servlet JUnit Java Erich Gamma Kent Beck xunit JUnit boolean JUnit Java JUnit Java JUnit Java 26.1.1 JUnit JUnit How

More information

Microsoft Word - 第3章.doc

Microsoft Word - 第3章.doc 第 3 章 Struts2 框架是 Apache 开源社区原有的 Struts 框架和 Open Symphony 社区 WebWork2 框架的合并版本, 它集成了这两大流行的 MVC 框架各自的优点, 主要以 WebWork 的设计思想为核心, 提供了更加灵活的控制层和组件实现技术 Struts2 的体系结构 Struts2 的安装与配置 Struts2 框架的主要配置文件 3.1 Struts2

More information

1 1 大概思路 创建 WebAPI 创建 CrossMainController 并编写 Nuget 安装 microsoft.aspnet.webapi.cors 跨域设置路由 编写 Jquery EasyUI 界面 运行效果 2 创建 WebAPI 创建 WebAPI, 新建 -> 项目 ->

1 1 大概思路 创建 WebAPI 创建 CrossMainController 并编写 Nuget 安装 microsoft.aspnet.webapi.cors 跨域设置路由 编写 Jquery EasyUI 界面 运行效果 2 创建 WebAPI 创建 WebAPI, 新建 -> 项目 -> 目录 1 大概思路... 1 2 创建 WebAPI... 1 3 创建 CrossMainController 并编写... 1 4 Nuget 安装 microsoft.aspnet.webapi.cors... 4 5 跨域设置路由... 4 6 编写 Jquery EasyUI 界面... 5 7 运行效果... 7 8 总结... 7 1 1 大概思路 创建 WebAPI 创建 CrossMainController

More information

J2EE MVC with Webwork2 Xwork, to J2EE MVC with Webwork2 Xwork

J2EE MVC with Webwork2 Xwork,  to J2EE MVC with Webwork2 Xwork MVC with Webwork2 Xwork Action...1 ActionContext...3 ActionProxyFactory Factory...4 ActionProxyFactory Proxy AOP...7 XworkInterceptor...8 Interceptor...9 LoginAction...10 LoginInterceptor...12 Action Result

More information

拦截器(Interceptor)的学习

拦截器(Interceptor)的学习 二 拦截器 (Interceptor) 的学习 拦截器可以监听程序的一个或所有方法 拦截器对方法调用流提供了细粒度控制 可以在无状态会话 bean 有状态会话 bean 和消息驱动 bean 上使用它们 拦截器可以是同一 bean 类中的方法或是一个外部类 下面介绍如何在 Session Bean 类中使用外部拦截器类 @Interceptors 注释指定一个或多个在外部类中定义的拦截器 下面拦截器

More information

1.JasperReport ireport JasperReport ireport JDK JDK JDK JDK ant ant...6

1.JasperReport ireport JasperReport ireport JDK JDK JDK JDK ant ant...6 www.brainysoft.net 1.JasperReport ireport...4 1.1 JasperReport...4 1.2 ireport...4 2....4 2.1 JDK...4 2.1.1 JDK...4 2.1.2 JDK...5 2.1.3 JDK...5 2.2 ant...6 2.2.1 ant...6 2.2.2 ant...6 2.3 JasperReport...7

More information

在Spring中使用Kafka:Producer篇

在Spring中使用Kafka:Producer篇 在某些情况下, 我们可能会在 Spring 中将一些 WEB 上的信息发送到 Kafka 中, 这时候我们就需要在 Spring 中编写 Producer 相关的代码了 ; 不过高兴的是,Spring 本身提供了操作 Kafka 的相关类库, 我们可以直接通过 xml 文件配置然后直接在后端的代码中使用 Kafka, 非常地方便 本文将介绍如果在 Spring 中将消息发送到 Kafka 在这之前,

More information

Guava学习之Resources

Guava学习之Resources Resources 提供提供操作 classpath 路径下所有资源的方法 除非另有说明, 否则类中所有方法的参数都不能为 null 虽然有些方法的参数是 URL 类型的, 但是这些方法实现通常不是以 HTTP 完成的 ; 同时这些资源也非 classpath 路径下的 下面两个函数都是根据资源的名称得到其绝对路径, 从函数里面可以看出,Resources 类中的 getresource 函数都是基于

More information

基于CDIO一体化理念的课程教学大纲设计

基于CDIO一体化理念的课程教学大纲设计 Java 语 言 程 序 设 计 课 程 教 学 大 纲 Java 语 言 程 序 设 计 课 程 教 学 大 纲 一 课 程 基 本 信 息 1. 课 程 代 码 :52001CC022 2. 课 程 名 称 :Java 语 言 程 序 设 计 3. 课 程 英 文 名 称 :Java Programming 4. 课 程 类 别 : 理 论 课 ( 含 实 验 上 机 或 实 践 ) 5. 授

More information

Microsoft Word - 01.DOC

Microsoft Word - 01.DOC 第 1 章 JavaScript 简 介 JavaScript 是 NetScape 公 司 为 Navigator 浏 览 器 开 发 的, 是 写 在 HTML 文 件 中 的 一 种 脚 本 语 言, 能 实 现 网 页 内 容 的 交 互 显 示 当 用 户 在 客 户 端 显 示 该 网 页 时, 浏 览 器 就 会 执 行 JavaScript 程 序, 用 户 通 过 交 互 式 的

More information

Microsoft Word - ShkZw3.doc

Microsoft Word - ShkZw3.doc 第 3 章 Struts2 的高级组件 本章将介绍 Struts2 中比较常用的高级组件 通过对这些高级组件的学习, 将有助于进一步了解和使用 Struts2 框架 本章主要内容 : (1)Struts2 对国际化的支持 (2)Struts2 常用拦截器的使用 (3)Struts2 的数据验证功能 (4)Struts2 对文件上传和下载的支持 3.1 Struts2 的国际化 国际化 是指一个应用程序在运行时能够根据客户端请求所来自国家或地区语言的不同而显示不同的用户界面

More information

EJB-Programming-4-cn.doc

EJB-Programming-4-cn.doc EJB (4) : (Entity Bean Value Object ) JBuilder EJB 2.x CMP EJB Relationships JBuilder EJB Test Client EJB EJB Seminar CMP Entity Beans Session Bean J2EE Session Façade Design Pattern Session Bean Session

More information

untitled

untitled PowerBuilder Tips 利 PB11 Web Service 年度 2 PB Tips PB9 EAServer 5 web service PB9 EAServer 5 了 便 web service 來說 PB9 web service 力 9 PB11 release PB11 web service 力更 令.NET web service PB NVO 論 不 PB 來說 說

More information

resp.getwriter().print(j + "*" + i + "=" + j * i+" "); resp.getwriter().print("<br/>"); protected void dopost(httpservletrequest req, HttpServletRespo

resp.getwriter().print(j + * + i + = + j * i+ ); resp.getwriter().print(<br/>); protected void dopost(httpservletrequest req, HttpServletRespo 第三章补充案例 案例 3-1 HttpServlet 一 案例描述 1 考核知识点名称 :HttpServlet 编号 : 2 练习目标 掌握 HttpServlet 的 doget() 方法和 dopost() 方法 3 需求分析由于大多数 Web 应用都是通过 HTTP 协议和客户端进行交互, 因此, 在 Servlet 接口中, 提供了 一个抽象类 javax.servlet.http.httpservlet,

More information

Microsoft Word - PHP7Ch01.docx

Microsoft Word - PHP7Ch01.docx PHP 01 1-6 PHP PHP HTML HTML PHP CSSJavaScript PHP PHP 1-6-1 PHP HTML PHP HTML 1. Notepad++ \ch01\hello.php 01: 02: 03: 04: 05: PHP 06:

More information

Struts2自定义类型转换.doc

Struts2自定义类型转换.doc Struts2 自定义类型转换 原理 struts2 的自定义类型转换机制为复杂类型的输入输出处理提供了便捷.struts2 已经为我们提供了几乎所有的 primitive 类型以及常用类型 ( 如 Date) 的类型转换器, 我们也可以为我们自定义类添加自定义类型转化器. struts2 为我们提供了一个类型转化器的入口 : ognl.defaulttypeconverter, 或继承 org.apache.struts2.util.strutstypeconverter,

More information

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数

OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 18 日晚 9 点 关于抄袭 没有分数 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double

More information

无类继承.key

无类继承.key 无类继承 JavaScript 面向对象的根基 周爱 民 / aimingoo aiming@gmail.com https://aimingoo.github.io https://github.com/aimingoo rand = new Person("Rand McKinnon",... https://docs.oracle.com/cd/e19957-01/816-6408-10/object.htm#1193255

More information

res/layout 目录下的 main.xml 源码 : <?xml version="1.0" encoding="utf 8"?> <TabHost android:layout_height="fill_parent" xml

res/layout 目录下的 main.xml 源码 : <?xml version=1.0 encoding=utf 8?> <TabHost android:layout_height=fill_parent xml 拓展训练 1- 界面布局 1. 界面布局的重要性做应用程序, 界面是最基本的 Andorid 的界面, 需要写在 res/layout 的 xml 里面, 一般情况下一个 xml 对应一个界面 Android 界面布局有点像写 html( 连注释代码的方式都一样 ), 要先给 Android 定框架, 然后再在框架里面放控件,Android 提供了几种框架,AbsoluteLayout,LinearLayout,

More information

优迈科技教学大纲2009版本

优迈科技教学大纲2009版本 java 软 件 工 程 师 培 训 教 学 大 纲 1 JAVA 软 件 工 程 师 培 训 教 学 大 纲 深 圳 软 件 园 人 才 实 训 基 地 2009 年 3 月 目 录 java 软 件 工 程 师 培 训 教 学 大 纲 2 教 学 阶 段...3 第 一 章 JAVA 起 步...3 第 二 章 面 向 对 象 的 编 程...4 第 三 章 数 据 结 构 IO 线 程 网 络...5

More information

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基

SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 odps-sdk 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基 开放数据处理服务 ODPS SDK SDK 概要 使用 Maven 的用户可以从 Maven 库中搜索 "odps-sdk" 获取不同版本的 Java SDK: 包名 odps-sdk-core odps-sdk-commons odps-sdk-udf odps-sdk-mapred odps-sdk-graph 描述 ODPS 基础功能的主体接口, 搜索关键词 "odpssdk-core" 一些

More information

设计模式 Design Patterns

设计模式 Design Patterns 丁勇 Email:18442056@QQ.com 学习目标 描述 JSP 表达式语言的语法 认识使用 JSP 表达式的优点 在 JSP 中使用表达式语言 表达式语言简介 5 1 EL 为表达式语言 由两个组开发 JSP 标准标签库专家组 JSP 2.0 专家组 JSP 表达式语言的语法 ${EL Expression} JSP EL 表达式用于以下情形 静态文本 标准标签和自定义标签 表达式语言简介

More information

Microsoft PowerPoint - 05-Status-Codes-Chinese.ppt

Microsoft PowerPoint - 05-Status-Codes-Chinese.ppt 2004 Marty Hall 服务器响应的生成 : HTTP 状态代码 JSP, Servlet, & Struts Training Courses: http://courses.coreservlets.com Available in US, China, Taiwan, HK, and Worldwide 2 JSP and Servlet Books from Sun Press: http://www.coreservlets.com

More information

epub83-1

epub83-1 C++Builder 1 C + + B u i l d e r C + + B u i l d e r C + + B u i l d e r C + + B u i l d e r 1.1 1.1.1 1-1 1. 1-1 1 2. 1-1 2 A c c e s s P a r a d o x Visual FoxPro 3. / C / S 2 C + + B u i l d e r / C

More information

untitled

untitled -JAVA 1. Java IDC 20 20% 5 2005 42.5 JAVA IDC JAVA 60% 70% JAVA 3 5 10 JAVA JAVA JAVA J2EE J2SE J2ME 70% JAVA JAVA 20 1 51 2. JAVA SUN JAVA J2EE J2EE 3. 1. CSTP CSTP 2 51 2. 3. CSTP IT CSTP IT IT CSTP

More information

输入 project name 选择完成

输入 project name 选择完成 JAVA 程序访问 HighGo DB 的环境准备 山东瀚高科技有限公司版权所有仅允许不作任何修改的转载和转发 Hibernate 的配置 MyEclipse 中创建新项目 : 选择菜单栏 file---new---project 选择 web project 进行下一步 输入 project name 选择完成 4. 单击 " 添加 JAR/ 文件夹 ", 会如下图出现 JDBC 下载 Hibernate

More information

一 登录 crm Mobile 系统 : 输入 ShijiCare 用户名和密码, 登录系统, 如图所示 : 第 2 页共 32 页

一 登录 crm Mobile 系统 : 输入 ShijiCare 用户名和密码, 登录系统, 如图所示 : 第 2 页共 32 页 第 1 页共 32 页 crm Mobile V1.0 for IOS 用户手册 一 登录 crm Mobile 系统 : 输入 ShijiCare 用户名和密码, 登录系统, 如图所示 : 第 2 页共 32 页 二 crm Mobile 界面介绍 : 第 3 页共 32 页 三 新建 (New) 功能使用说明 1 选择产品 第 4 页共 32 页 2 填写问题的简要描述和详细描述 第 5 页共

More information

OSWorkflow Documentation

OSWorkflow Documentation OSWorkflow Documentation Update Time: 05/09/15 OSWorkflow Java workflow engine API 理 flow 行 XML 來 流 Database UI 不 流 GUI Designer end user 行 JSP+Servlet 行 OSWorkflow 2.8 說 2.7 2.7 了 OSWorkflow library library

More information

Microsoft Word - json入门.doc

Microsoft Word - json入门.doc Json 入门 送给亲爱的女朋友, 祝她天天快乐 作者 :hlz QQ:81452743 MSN/Email:hulizhong2008@163.com json 入门 (1) json 是 JavaScript Object Notation 的简称 ; 在 web 系统开发中与 AJAX 相结合用的比较多 在 ajax 中数据传输有 2 中方式 : 文本类型, 常用 responsetext 属性类获取

More information

EJB-Programming-3.PDF

EJB-Programming-3.PDF :, JBuilder EJB 2.x CMP EJB Relationships JBuilder EJB Test Client EJB EJB Seminar CMP Entity Beans Value Object Design Pattern J2EE Design Patterns Value Object Value Object Factory J2EE EJB Test Client

More information

Kubenetes 系列列公开课 2 每周四晚 8 点档 1. Kubernetes 初探 2. 上 手 Kubernetes 3. Kubernetes 的资源调度 4. Kubernetes 的运 行行时 5. Kubernetes 的 网络管理理 6. Kubernetes 的存储管理理 7.

Kubenetes 系列列公开课 2 每周四晚 8 点档 1. Kubernetes 初探 2. 上 手 Kubernetes 3. Kubernetes 的资源调度 4. Kubernetes 的运 行行时 5. Kubernetes 的 网络管理理 6. Kubernetes 的存储管理理 7. Kubernetes 包管理理 工具 Helm 蔺礼强 Kubenetes 系列列公开课 2 每周四晚 8 点档 1. Kubernetes 初探 2. 上 手 Kubernetes 3. Kubernetes 的资源调度 4. Kubernetes 的运 行行时 5. Kubernetes 的 网络管理理 6. Kubernetes 的存储管理理 7. Kubernetes

More information

Stateless Session Beans(无状态bean)的学习

Stateless Session Beans(无状态bean)的学习 一 Stateless Session Beans( 无状态 bean) 的学习 第一步 : 要定义一个会话 Bean, 首先需要定义一个包含他所有业务方法的接口 这个接口不需要任何注释, 就像普通的 java 接口那样定义 调用 EJB 的客户端通过使用这个接口引用从 EJB 容器得到的会话 Bean 对象 stub 接口的定义如下: HelloWorld.java package com.foshanshop.ejb3;

More information

软件工程文档编制

软件工程文档编制 实训抽象类 一 实训目标 掌握抽象类的定义 使用 掌握运行时多态 二 知识点 抽象类的语法格式如下 : public abstract class ClassName abstract void 方法名称 ( 参数 ); // 非抽象方法的实现代码 在使用抽象类时需要注意如下几点 : 1 抽象类不能被实例化, 实例化的工作应该交由它的子类来完成 2 抽象方法必须由子类来进行重写 3 只要包含一个抽象方法的抽象类,

More information

帝国CMS下在PHP文件中调用数据库类执行SQL语句实例

帝国CMS下在PHP文件中调用数据库类执行SQL语句实例 帝国 CMS 下在 PHP 文件中调用数据库类执行 SQL 语句实例 这篇文章主要介绍了帝国 CMS 下在 PHP 文件中调用数据库类执行 SQL 语句实例, 本文还详细介绍了帝国 CMS 数据库类中的一些常用方法, 需要的朋友可以参考下 例 1: 连接 MYSQL 数据库例子 (a.php)

More information

使用Cassandra和Spark 2.0实现Rest API服务

使用Cassandra和Spark 2.0实现Rest API服务 使用 Cassandra 和 Spark 2.0 实现 Rest API 服务 在这篇文章中, 我将介绍如何在 Spark 中使用 Akkahttp 并结合 Cassandra 实现 REST 服务, 在这个系统中 Cassandra 用于数据的存储 我们已经见识到 Spark 的威力, 如果和 Cassandra 正确地结合可以实现更强大的系统 我们先创建一个 build.sbt 文件, 内容如下

More information

设计模式 Design Patterns

设计模式 Design Patterns 丁勇 Email:18442056@QQ.com 学习目标 理解 Struts 框架的工作原理 掌握使用 Struts 框架开发 Web 应用的基本步骤 熟悉 MyEclipse 对 Struts 开发的支持 Web 框架事实标准 : Web 框架的事实标准 http://struts.apache.org Java EE 主流技术趋势图 主流 Web 框架趋势图 使用 Struts 实现加法器 使用开发的

More information

關於本書 l 3 PhoneGap Appcelerator Titanium Sencha Touch (wrapper framework) Native App PhoneGap Build Native App Hybrid App Java Objective-C Android SDK

關於本書 l 3 PhoneGap Appcelerator Titanium Sencha Touch (wrapper framework) Native App PhoneGap Build Native App Hybrid App Java Objective-C Android SDK 2 l 跨裝置網頁設計 Android ios Windows 8 BlackBerry OS Android HTML 5 HTML 5 HTML 4.01 HTML 5 CSS 3 CSS 3 CSS 2.01 CSS 3 2D/3D PC JavaScript

More information

序号:001

序号:001 第 一 组 选 题 简 介 序 号 :001 题 目 : 基 于 BPEL 的 网 上 订 餐 系 统 的 设 计 与 实 现 网 上 订 餐 系 统 是 在 互 联 网 上 进 行 菜 单 信 息 发 布 网 上 订 餐 以 及 维 护 客 户 关 系 的 电 子 商 务 系 统, 餐 饮 企 业 可 以 通 过 这 个 电 子 商 务 系 统 发 布 自 己 的 菜 单 信 息 以 供 客 户

More information

计算机软件技术专业教学计划

计算机软件技术专业教学计划 计 算 机 软 件 技 术 专 业 人 才 培 养 方 案 ( 服 务 外 包 方 向 ) 专 业 大 类 名 称 ( 代 码 ):++(++) 专 业 类 名 称 ( 代 码 ):++++++(++++) 专 业 名 称 ( 代 码 ):+++++++(++++++) 修 业 年 限 : 三 年, 全 日 制 招 生 对 象 : 三 年 制 普 通 高 中 及 对 口 中 职 专 业 毕 业 生

More information

PowerPoint プレゼンテーション

PowerPoint プレゼンテーション Perl CGI 1 Perl CGI 2 Perl CGI 3 Perl CGI 4 1. 2. 1. #!/usr/local/bin/perl 2. print "Content-type: text/html n n"; 3. print " n"; 4. print " n"; 3. 4.

More information

XXXXXXXX http://cdls.nstl.gov.cn 2 26

XXXXXXXX http://cdls.nstl.gov.cn 2 26 [ ] [ ] 2003-7-18 1 26 XXXXXXXX http://cdls.nstl.gov.cn 2 26 (2003-7-18) 1...5 1.1...5 1.2...5 1.3...5 2...6 2.1...6 2.2...6 2.3...6 3...7 3.1...7 3.1.1...7 3.1.2...7 3.1.2.1...7 3.1.2.1.1...8 3.1.2.1.2...10

More information

2. AOP 底层技术实现 小风 Java 实战系列教程 关键词 : 代理模式 代理模型分为两种 : 1) 接口代理 (JDK 动态代理 ) 2) 子类代理 (Cglib 子类代理 ) 需求 :CustomerService 业务类, 有 save,update 方法, 希望在 save,updat

2. AOP 底层技术实现 小风 Java 实战系列教程 关键词 : 代理模式 代理模型分为两种 : 1) 接口代理 (JDK 动态代理 ) 2) 子类代理 (Cglib 子类代理 ) 需求 :CustomerService 业务类, 有 save,update 方法, 希望在 save,updat 本章学习目标 小风 Java 实战系列教程 AOP 思想概述 AOP 底层技术实现 AOP 术语介绍 SpringAOP 的 XML 方式 HelloWorld SpringAOP 的 XML 方式配置细节 SpringAOP 的注解方式 SpringAOP 的零配置方式 1. AOP 思想概述 1.1. AOP 思想简介 1.2. AOP 的作用 2. AOP 底层技术实现 小风 Java 实战系列教程

More information

Java

Java Java 应用框架 Struts 实验指导书 软件学院 - 1 - 实验一 熟悉 struts 环境 工具 实验目的 : 通过完成 HelloWorld 程序熟悉 Struts2 的开发流程和工具实验类型 : 验证实验要求 : 必做实验内容 : 1 在 MyEclipse 中新建 web 工程 2 在 struts-2.2.1.1-all\struts-2.2.1.1 解压 struts2-blank.war(

More information

05 01 accordion UI containers 03 Accordion accordion UI accordion 54

05 01 accordion UI containers 03 Accordion accordion UI accordion 54 jquery UI plugin Accordion 05 01 accordion UI containers 03 Accordion accordion UI accordion 54 05 jquery UI plugin 3-1

More information

C++ 程序设计 告别 OJ1 - 参考答案 MASTER 2019 年 5 月 3 日 1

C++ 程序设计 告别 OJ1 - 参考答案 MASTER 2019 年 5 月 3 日 1 C++ 程序设计 告别 OJ1 - 参考答案 MASTER 2019 年 月 3 日 1 1 INPUTOUTPUT 1 InputOutput 题目描述 用 cin 输入你的姓名 ( 没有空格 ) 和年龄 ( 整数 ), 并用 cout 输出 输入输出符合以下范例 输入 master 999 输出 I am master, 999 years old. 注意 "," 后面有一个空格,"." 结束,

More information

没 有 多 余 的 Contruol 或 Action 了 原 来 Domain 层 被 服 务 层 Service layer 遮 挡, 在 右 边 图 中, 则 Domain 层 直 接 暴 露 给 前 台 了, 没 有 被 遮 挡, 裸 露 了 这 样 一 步 到 位 实 现 领 域 模 型

没 有 多 余 的 Contruol 或 Action 了 原 来 Domain 层 被 服 务 层 Service layer 遮 挡, 在 右 边 图 中, 则 Domain 层 直 接 暴 露 给 前 台 了, 没 有 被 遮 挡, 裸 露 了 这 样 一 步 到 位 实 现 领 域 模 型 文 章 编 号 :1007-757X(2012)1-0036-04 领 域 驱 动 模 型 的 WEB 软 件 系 统 设 计 研 究 摘 要 : J2EE 3 JDK1.7 Tomcat WEB 关 键 词 : 中 图 分 类 号 :TP311 文 献 标 志 码 :A 0 引 言 Web 软 件 系 统 的 分 层 结 构 典 型 的 J2EE 软 件 系 统 开 发 方 法 分 为 三 层 结

More information

序 言 本 专 业 人 才 培 养 方 案 以 适 应 市 场 需 求 为 目 标, 根 据 学 校 校 企 双 主 体 人 才 培 养 的 要 求 和 移 动 应 用 开 发 专 业 的 特 点 设 置 课 程 体 系, 体 现 了 课 程 为 市 场 服 务 的 特 点 本 专 业 要 求 学

序 言 本 专 业 人 才 培 养 方 案 以 适 应 市 场 需 求 为 目 标, 根 据 学 校 校 企 双 主 体 人 才 培 养 的 要 求 和 移 动 应 用 开 发 专 业 的 特 点 设 置 课 程 体 系, 体 现 了 课 程 为 市 场 服 务 的 特 点 本 专 业 要 求 学 广 东 新 安 职 业 技 术 学 院 计 算 机 移 动 应 用 开 发 专 业 2016 级 人 才 培 养 方 案 专 业 方 向 负 责 人 : 梅 红 系 主 任 : 毛 立 冰 计 算 机 系 二 一 五 年 六 月 序 言 本 专 业 人 才 培 养 方 案 以 适 应 市 场 需 求 为 目 标, 根 据 学 校 校 企 双 主 体 人 才 培 养 的 要 求 和 移 动 应 用 开

More information

Microsoft PowerPoint - 02-Servlet-Basics-Chinese.ppt

Microsoft PowerPoint - 02-Servlet-Basics-Chinese.ppt 2004 Marty Hall servlet 基础 JSP, Servlet, & Struts Training Courses: http://courses.coreservlets.com Available in US, China, Taiwan, HK, and Worldwide 2 JSP and Servlet Books from Sun Press: http://www.coreservlets.com

More information

59 1 CSpace 2 CSpace CSpace URL CSpace 1 CSpace URL 2 Lucene 3 ID 4 ID Web 1. 2 CSpace LireSolr 3 LireSolr 3 Web LireSolr ID

59 1 CSpace 2 CSpace CSpace URL CSpace 1 CSpace URL 2 Lucene 3 ID 4 ID Web 1. 2 CSpace LireSolr 3 LireSolr 3 Web LireSolr ID 58 2016. 14 * LireSolr LireSolr CEDD Ajax CSpace LireSolr CEDD Abstract In order to offer better image support services it is necessary to extend the image retrieval function of our institutional repository.

More information

RUN_PC連載_10_.doc

RUN_PC連載_10_.doc PowerBuilder 8 (10) Jaguar CTS ASP Jaguar CTS PowerDynamo Jaguar CTS Microsoft ASP (Active Server Pages) ASP Jaguar CTS ASP Jaguar CTS ASP Jaguar CTS ASP Jaguar CTS ASP Jaguar CTS ASP Jaguar Server ASP

More information

通过Hive将数据写入到ElasticSearch

通过Hive将数据写入到ElasticSearch 我在 使用 Hive 读取 ElasticSearch 中的数据 文章中介绍了如何使用 Hive 读取 ElasticSearch 中的数据, 本文将接着上文继续介绍如何使用 Hive 将数据写入到 ElasticSearch 中 在使用前同样需要加入 elasticsearch-hadoop-2.3.4.jar 依赖, 具体请参见前文介绍 我们先在 Hive 里面建个名为 iteblog 的表,

More information

D getinitparameternames() 9 下 列 选 项 中, 属 于 Servlet API 中 提 供 的 request 对 象 的 包 装 类 的 是 ( ) A HttpServletRequestWrapper B HttpServletRequest C HttpServ

D getinitparameternames() 9 下 列 选 项 中, 属 于 Servlet API 中 提 供 的 request 对 象 的 包 装 类 的 是 ( ) A HttpServletRequestWrapper B HttpServletRequest C HttpServ 第 四 章 Filter( 过 滤 器 ) 样 题 A 卷 一 选 择 题 ( 每 小 题 2 分, 共 20 分 ) 1 下 面 选 项 中, 用 于 实 现 初 始 化 过 滤 器 的 方 法 是 ( ) A init(filterconfig filterconfig) B dofilter(servletrequest req,servletresponse resp,filterchain

More information

(CIP) Web /,. :,2005. 1 ISBN 7 81058 782 X.W............T P393.4 CIP (2004) 118797 Web ( 99 200436) ( http:/ / www.shangdapress.com 66135110) : * 787

(CIP) Web /,. :,2005. 1 ISBN 7 81058 782 X.W............T P393.4 CIP (2004) 118797 Web ( 99 200436) ( http:/ / www.shangdapress.com 66135110) : * 787 Web (CIP) Web /,. :,2005. 1 ISBN 7 81058 782 X.W............T P393.4 CIP (2004) 118797 Web ( 99 200436) ( http:/ / www.shangdapress.com 66135110) : * 787 1092 1/ 16 30.75 748 2005 1 1 2005 1 1 : 1 3 100

More information

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点

OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 OOP with Java Yuanbin Wu cs@ecnu OOP with Java 通知 Project 4: 4 月 19 日晚 9 点 复习 类的复用 组合 (composition): has-a 关系 class MyType { public int i; public double d; public char c; public void set(double x) { d

More information

Microsoft Word - 第5章.doc

Microsoft Word - 第5章.doc 第 5 章 解密 Struts 之核心文件 在第 4 章中简单介绍了 Java Web 开发的三大框架之一的 Struts 2 框架的一些基本概念及其开发的流程 在 Struts 2 项目开发中, 有一个重要的过程就是其配置文件的配置 本章将介绍 Struts 核心配置文件的具体作用及其配置方式, 然后介绍 Struts 2 框架中的另一种文件 Action 类文件, 本章的主要内容如下 : 配置文件

More information

chapter 2 HTML5 目錄iii HTML HTML HTML HTML HTML canvas

chapter 2 HTML5 目錄iii HTML HTML HTML HTML HTML canvas Contents 目錄 chapter 1 1-1... 1-2 1-2... 1-3 HTML5... 1-3... 1-5 1-3... 1-9 Web Storage... 1-9... 1-10 1-4 HTML5... 1-14... 1-14... 1-15 HTML5... 1-15... 1-15... 1-16 1-5... 1-18 Apps... 1-18 HTML5 Cache

More information

职 位 类 别 : 测 试 工 程 师 工 作 经 验 或 实 习 经 历 : 不 限 岗 位 要 求 : 1. 本 科 及 其 以 上 学 历, 计 算 机 相 关 专 业 2014 届 毕 业 生 ; 2. 实 习 时 间 要 求, 尽 量 一 周 五 个 工 作 日 ; 3. 熟 悉 Wind

职 位 类 别 : 测 试 工 程 师 工 作 经 验 或 实 习 经 历 : 不 限 岗 位 要 求 : 1. 本 科 及 其 以 上 学 历, 计 算 机 相 关 专 业 2014 届 毕 业 生 ; 2. 实 习 时 间 要 求, 尽 量 一 周 五 个 工 作 日 ; 3. 熟 悉 Wind 企 业 信 息 表 公 司 名 称 : 中 铁 信 安 ( 北 京 ) 信 息 安 全 技 术 有 限 公 司 公 司 性 质 : 国 企 控 股 公 司 规 模 : 100 人 左 右 所 属 行 业 : 互 联 网 计 算 机 软 件 招 聘 人 数 :12 工 作 地 点 : 北 京 市 海 淀 区 公 司 能 够 提 供 的 福 利 : 五 险 一 金 晋 升 旅 游 节 假 日 礼 物 加

More information

RUN_PC連載_8_.doc

RUN_PC連載_8_.doc PowerBuilder 8 (8) Web DataWindow ( ) DataWindow Web DataWindow Web DataWindow Web DataWindow PowerDynamo Web DataWindow / Web DataWindow Web DataWindow Wizard Web DataWindow Web DataWindow DataWindow

More information

WWW PHP

WWW PHP WWW PHP 2003 1 2 function function_name (parameter 1, parameter 2, parameter n ) statement list function_name sin, Sin, SIN parameter 1, parameter 2, parameter n 0 1 1 PHP HTML 3 function strcat ($left,

More information

WebSphere Studio Application Developer IBM Portal Toolkit... 2/21 1. WebSphere Portal Portal WebSphere Application Server stopserver.bat -configfile..

WebSphere Studio Application Developer IBM Portal Toolkit... 2/21 1. WebSphere Portal Portal WebSphere Application Server stopserver.bat -configfile.. WebSphere Studio Application Developer IBM Portal Toolkit... 1/21 WebSphere Studio Application Developer IBM Portal Toolkit Portlet Doug Phillips (dougep@us.ibm.com),, IBM Developer Technical Support Center

More information

5-1 nav css 5-2

5-1 nav css 5-2 5 HTML CSS HTML CSS Ê Ê Ê Ê 5-1 nav css 5-2 5-1 5 5-1-1 5-01 css images 01 index.html 02 5-3 style.css css 03 CH5/5-01/images 04 images index.html style.css 05

More information

jsp

jsp JSP Allen Long Email: allen@huihoo.com http://www.huihoo.com 2004-04 Huihoo - Enterprise Open Source http://www.huihoo.com 1 JSP JSP JSP JSP MVC Huihoo - Enterprise Open Source http://www.huihoo.com 2

More information

Microsoft PowerPoint - ch6 [相容模式]

Microsoft PowerPoint - ch6 [相容模式] UiBinder wzyang@asia.edu.tw UiBinder Java GWT UiBinder XML UI i18n (widget) 1 2 UiBinder HelloWidget.ui.xml: UI HelloWidgetBinder HelloWidget.java XML UI Owner class ( Composite ) UI XML UiBinder: Owner

More information

數位圖書館/博物館相關標準 2

數位圖書館/博物館相關標準 2 數 2 立 XML (Extensibility) XML 行 (Self-description) (Structure) XML (Validation) XML DTD 行 XML 列 XML-Language SGML without tears Self-describing Documents Well-formed and Valid Documents XML-Link Power

More information

IoC容器和Dependency Injection模式.doc

IoC容器和Dependency Injection模式.doc IoC Dependency Injection /Martin Fowler / Java Inversion of Control IoC Dependency Injection Service Locator Java J2EE open source J2EE J2EE web PicoContainer Spring Java Java OO.NET service component

More information

Chapter 9: Objects and Classes

Chapter 9: Objects and Classes Java application Java main applet Web applet Runnable Thread CPU Thread 1 Thread 2 Thread 3 CUP Thread 1 Thread 2 Thread 3 ,,. (new) Thread (runnable) start( ) CPU (running) run ( ) blocked CPU sleep(

More information

關於本書 Part 3 CSS XHTML Ajax Part 4 HTML 5 API JavaScript HTML 5 API Canvas API ( ) Video/Audio API ( ) Drag and Drop API ( ) Geolocation API ( ) Part 5

關於本書 Part 3 CSS XHTML Ajax Part 4 HTML 5 API JavaScript HTML 5 API Canvas API ( ) Video/Audio API ( ) Drag and Drop API ( ) Geolocation API ( ) Part 5 網頁程式設計 HTML JavaScript CSS HTML JavaScript CSS HTML 5 JavaScript JavaScript HTML 5 API CSS CSS Part 1 HTML HTML 5 API HTML 5 Apple QuickTime Adobe Flash RealPlayer Ajax XMLHttpRequest HTML 4.01 HTML 5

More information

付宝容器 jsapi 档 册 PDF 版本 本版本为实验版本, 为线下独 查看使, 受制于 成 PDF 程序的限制, 样式问题还没有很好的解决, 例如分 切图 代码 亮 推荐使 在线版本, 便实时查看 jsapi 运 效果 如需搜索, 使 阅读 PDF 软件 带功能即可 更多细节样式调整和 录索引探

付宝容器 jsapi 档 册 PDF 版本 本版本为实验版本, 为线下独 查看使, 受制于 成 PDF 程序的限制, 样式问题还没有很好的解决, 例如分 切图 代码 亮 推荐使 在线版本, 便实时查看 jsapi 运 效果 如需搜索, 使 阅读 PDF 软件 带功能即可 更多细节样式调整和 录索引探 付宝容器 jsapi 档 册 PDF 版本 本版本为实验版本, 为线下独 查看使, 受制于 成 PDF 程序的限制, 样式问题还没有很好的解决, 例如分 切图 代码 亮 推荐使 在线版本, 便实时查看 jsapi 运 效果 如需搜索, 使 阅读 PDF 软件 带功能即可 更多细节样式调整和 录索引探索建设中 成时间 : 2017-07-20 12:13:21 Since 8.6 定义键盘 定义键盘使

More information

前言 C# C# C# C C# C# C# C# C# microservices C# More Effective C# More Effective C# C# C# C# Effective C# 50 C# C# 7 Effective vii

前言 C# C# C# C C# C# C# C# C# microservices C# More Effective C# More Effective C# C# C# C# Effective C# 50 C# C# 7 Effective vii 前言 C# C# C# C C# C# C# C# C# microservices C# More Effective C# More Effective C# C# C# C# Effective C# 50 C# C# 7 Effective vii C# 7 More Effective C# C# C# C# C# C# Common Language Runtime CLR just-in-time

More information

ebook

ebook 26 JBuilder RMI Java Remote Method Invocation R M I J a v a - - J a v a J a v J a v a J a v a J a v a R M I R M I ( m a r s h a l ) ( u n m a r c h a l ) C a ff e i n e J a v a j a v a 2 i i o p J a v

More information

北 风 网 讲 师 原 创 作 品 ---- 仅 供 学 员 内 部 交 流 使 用 前 言 吾 尝 终 日 而 思 矣, 不 如 须 臾 之 所 学 也 ; 吾 尝 跂 而 望 矣, 不 如 登 高 之 博 见 也 登 高 而 招, 臂 非 加 长 也, 而 见

北 风 网 讲 师 原 创 作 品 ---- 仅 供  学 员 内 部 交 流 使 用 前 言 吾 尝 终 日 而 思 矣, 不 如 须 臾 之 所 学 也 ; 吾 尝 跂 而 望 矣, 不 如 登 高 之 博 见 也 登 高 而 招, 臂 非 加 长 也, 而 见 北 风 网 讲 师 原 创 作 品 ---- 仅 供 www.ibeifeng.com 学 员 内 部 交 流 使 用 前 言 吾 尝 终 日 而 思 矣, 不 如 须 臾 之 所 学 也 ; 吾 尝 跂 而 望 矣, 不 如 登 高 之 博 见 也 登 高 而 招, 臂 非 加 长 也, 而 见 者 远 ; 顺 风 而 呼, 声 非 加 疾 也, 而 闻 者 彰 假 舆 马 者, 非 利 足 也,

More information

PowerPoint Presentation

PowerPoint Presentation 1 版权所有 2011,Oracle 和 / 或其分支机构 保留所有权利 从幻灯片 8 中插入信息保护策略分类 WAC Widget Java ME 手机新动向陈志宇 2 版权所有 2011,Oracle 和 / 或其分支机构 保留所有权利 从幻灯片 8 中插入信息保护策略分类 以下内容旨在概述我们产品总的发展方向 该内容仅供参考, 不可纳入任何合同 该内容不构成提供任何材料 代码或功能的承诺, 并且不应该作为制定购买决策的依据

More information

第03章 控制反转(Spring IoC)

第03章  控制反转(Spring IoC) 3 Spring IoC GoF Design Patterns: Elements of Reusable Object-Oriented Software Programming to an Interface not an Implementation Java Java Java GoF Service Locator IoC IoC Spring IoC 3.1 IoC IoC IoC Dependency

More information

使用MapReduce读取XML文件

使用MapReduce读取XML文件 使用 MapReduce 读取 XML 文件 XML( 可扩展标记语言, 英语 :extensible Markup Language, 简称 : XML) 是一种标记语言, 也是行业标准数据交换交换格式, 它很适合在系统之间进行数据存储和交换 ( 话说 Hadoop H ive 等的配置文件就是 XML 格式的 ) 本文将介绍如何使用 MapReduce 来读取 XML 文件 但是 Had oop

More information

目 录 1. 业 务 流 程 系 统 开 发 面 临 的 挑 战 与 机 遇... 3 1.1 业 务 流 程 管 理... 4 2. 新 一 代 开 源 业 务 流 程 开 发 平 台 BPMX3... 5 2.1 BPMX3 是 什 么... 5 2.2 为 什 么 要 优 先 采 用 BPMX

目 录 1. 业 务 流 程 系 统 开 发 面 临 的 挑 战 与 机 遇... 3 1.1 业 务 流 程 管 理... 4 2. 新 一 代 开 源 业 务 流 程 开 发 平 台 BPMX3... 5 2.1 BPMX3 是 什 么... 5 2.2 为 什 么 要 优 先 采 用 BPMX BPMX3 技 术 白 皮 书 业 务 流 程 开 发 平 台 介 绍 目 录 1. 业 务 流 程 系 统 开 发 面 临 的 挑 战 与 机 遇... 3 1.1 业 务 流 程 管 理... 4 2. 新 一 代 开 源 业 务 流 程 开 发 平 台 BPMX3... 5 2.1 BPMX3 是 什 么... 5 2.2 为 什 么 要 优 先 采 用 BPMX3... 5 2.2.1 BPMX3

More information

云数据库 RDS SDK

云数据库 RDS SDK 云数据库 RDS SDK SDK SDK 下载 SDK 下载 最新版本 java_sdk.zip python_sdk.zip php_sdk.zip c#_sdk.zip 历史版本 2015-11-3 java_sdk.zip python_sdk.zip php_sdk.zip c#_sdk.zip JAVA 教程 JAVA 创建 Access Key 登陆阿里云账号 打开 我的 Access

More information

设计模式 Design Patterns

设计模式 Design Patterns 丁勇 Email:18442056@QQ.com 组件技术概述 现在软件开发都已经转向了基于组件的开发, 目前具备代表性的组件技术有微软的 COM COM+, 有 Sun 的 Bean 和 EJB(Enterprise Bean), 另外还有 CORBA(Common Object Request Broker Architecture, 公 共对象请求代理结构 ) Bean Bean 规范将 软件组件

More information

Microsoft Word - 王彬_已修改_.doc

Microsoft Word - 王彬_已修改_.doc 第 39 卷 第 1 期 应 用 科 技 Vol.39, No.1 2012 年 2 月 Applied Science and Technology Feb. 2012 doi:10.3969/j.issn.1009-671x.201110009 基 于 J2EE 网 络 教 学 系 统 的 设 计 与 实 现 李 静 梅, 王 彬, 彭 晴 晴 哈 尔 滨 工 程 大 学 计 算 机 科 学 与

More information

Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0,

Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0, http://debut.cis.nctu.edu.tw/~chi Java java.lang.math Java Java.util.Random : ArithmeticException int zero = 0; try { int i= 72 / zero ; }catch (ArithmeticException e ) { // } 0, : POSITIVE_INFINITY NEGATIVE_INFINITY

More information

Office Office Office Microsoft Word Office Office Azure Office One Drive 2 app 3 : [5] 3, :, [6]; [5], ; [8], [1], ICTCLAS(Institute of Computing Tech

Office Office Office Microsoft Word Office Office Azure Office One Drive 2 app 3 : [5] 3, :, [6]; [5], ; [8], [1], ICTCLAS(Institute of Computing Tech - OfficeCoder 1 2 3 4 1,2,3,4 xingjiarong@mail.sdu.edu.cn 1 xuchongyang@mail.sdu.edu.cn 2 sun.mc@outlook.com 3 luoyuanhang@mail.sdu.edu.cn 4 Abstract. Microsoft Word 2013 Word 2013 Office Keywords:,, HTML5,

More information

06-4.indd

06-4.indd 1 02 07 13 16 20 28 33 38 42 46 48 51 57 64 65 65 66 67 68 2 3 4 5 6 7 8 9 10 11 12 13 LL T : 14 LL T 15 16 扫描电子显微镜成像模拟的 MPI 及 OpenMP 并行化 17 18 19 20 21 22 ~ ~ ~ 23 24 ~ ~ ~ ~ ~ ~ ~ 25 26 27 28 29 图 3

More information

目 录(目录名)

目  录(目录名) 目录 目录...1-1 1.1 域名解析配置命令... 1-1 1.1.1 display dns domain... 1-1 1.1.2 display dns dynamic-host... 1-1 1.1.3 display dns server... 1-2 1.1.4 display ip host... 1-3 1.1.5 dns domain... 1-4 1.1.6 dns resolve...

More information

1. 2. Flex Adobe 3.

1. 2. Flex Adobe 3. 1. 2. Flex Adobe 3. Flex Adobe Flex Flex Web Flex Flex Flex Adobe Flash Player 9 /rich Internet applications/ria Flex 1. 2. 3. 4. 5. 6. SWF Flash Player Flex 1. Flex framework Adobe Flex 2 framework RIA

More information

p.2 1 <HTML> 2 3 <HEAD> 4 <TITLE> </TITLE> 5 </HEAD> 6 7 <BODY> 8 <H3><B> </B></H3> 9 <H4><I> </I></H4> 10 </BODY> </HTML> 1. HTML 1. 2.

p.2 1 <HTML> 2 3 <HEAD> 4 <TITLE> </TITLE> 5 </HEAD> 6 7 <BODY> 8 <H3><B> </B></H3> 9 <H4><I> </I></H4> 10 </BODY> </HTML> 1. HTML 1. 2. 2005-06 p.1 HTML HyperText Mark-up Language 1. HTML Logo, Pascal, C++, Java HTML 2. HTML (tag) 3. HTML 4. HTML 1. HTML 2. 3. FTP HTML HTML html 1. html html html cutehtmleasyhtml 2. wyswyg (What you see

More information

FileMaker 16 ODBC 和 JDBC 指南

FileMaker 16 ODBC 和 JDBC 指南 FileMaker 16 ODBC JDBC 2004-2017 FileMaker, Inc. FileMaker, Inc. 5201 Patrick Henry Drive Santa Clara, California 95054 FileMaker FileMaker Go FileMaker, Inc. FileMaker WebDirect FileMaker Cloud FileMaker,

More information

IP505SM_manual_cn.doc

IP505SM_manual_cn.doc IP505SM 1 Introduction 1...4...4...4...5 LAN...5...5...6...6...7 LED...7...7 2...9...9...9 3...11...11...12...12...12...14...18 LAN...19 DHCP...20...21 4 PC...22...22 Windows...22 TCP/IP -...22 TCP/IP

More information

Servlet

Servlet Servlet Allen Long Email: allen@huihoo.com http://www.huihoo.com 2004-04 Huihoo - Enterprise Open Source http://www.huihoo.com 1 Huihoo - Enterprise Open Source http://www.huihoo.com 2 GET POST Huihoo

More information

J2ME ISBN J2ME MIDP MIDP 2.0 API J2ME Netbeans IDE 4.1 Mobility Pack 4.1 MIDlet MIDlet MIDP PUSH API PDA API Ja

J2ME ISBN J2ME MIDP MIDP 2.0 API J2ME Netbeans IDE 4.1 Mobility Pack 4.1 MIDlet MIDlet MIDP PUSH API PDA API Ja Java JBuilder 2005 2005 6 ISBN 7-121-01166-2 69.00 1 688 JBuilder JBuilder 2005 JBuilder JBuilder 2005 JBuilder 2005 Java Java Java JBuilder 17 J2ME 2006 1 ISBN 7-121-02210-9 39.00 1 436 J2ME MIDP MIDP

More information

关 于 参 加 毕 业 生 供 需 见 面 会 有 关 事 项 的 公 告 为 确 保 学 校 举 办 的 毕 业 生 供 需 见 面 会 有 序 进 行, 特 就 参 会 的 有 关 事 项 公 告 如 下 : 一 本 次 招 聘 大 会 参 会 人 员 仅 为 大 学 毕 业 生 用 人 单 位 招 聘 负 责 人 以 及 大 会 工 作 人 员, 其 它 社 会 人 员 不 得 进 入 会 场

More information

PrintWriter s = new PrintWriter(writer); ex.printstacktrace(s); mv.addobject("exception", writer.tostring()); mv.setviewname("error"); return

PrintWriter s = new PrintWriter(writer); ex.printstacktrace(s); mv.addobject(exception, writer.tostring()); mv.setviewname(error); return 本章学习目标 小风 Java 实战系列教程 SpringMVC 异常处理 SpringMVC 文件上传 SpringMVC 处理 JSON 格式数据 SpringMVC 拦截器 SpringMVC 对 restful 风格的支持 1. SpringMVC 异常处理 1.1. @ExceptionHandler 注解处理异常 @ExceptionHandler 该注解使用在异常处理方法上面 1.1.1.

More information

手册 doc

手册 doc 1. 2. 3. 3.1 3.2 3.3 SD 3.4 3.5 SD 3.6 3.7 4. 4.1 4.2 4.3 SD 4.4 5. 5.1 5.2 5.3 SD 6. 1. 1~3 ( ) 320x240~704x288 66 (2G SD 320x2401FPS ) 32M~2G SD SD SD SD 24V DC 3W( ) -10~70 10~90% 154x44x144mm 2. DVR106

More information

1

1 PRIMETON TECHNOLOGIES, LTD. EOS EOS Manager No part of this document may be reproduced, stored in any electronic retrieval system, or transmitted in any form or by any means, mechanical, photocopying,

More information

软 件 工 程 专 业 习 指 南 目 录 一 软 件 工 程 专 业 设 置 背 景 与 发 展 前 景... 3 二 软 件 工 程 专 业 实 践 教 条 件... 4 三 软 件 工 程 专 业 课 程 类 型 及 核 方 式... 6 1. 软 件 工 程 专 业 课 程 类 型...7

软 件 工 程 专 业 习 指 南 目 录 一 软 件 工 程 专 业 设 置 背 景 与 发 展 前 景... 3 二 软 件 工 程 专 业 实 践 教 条 件... 4 三 软 件 工 程 专 业 课 程 类 型 及 核 方 式... 6 1. 软 件 工 程 专 业 课 程 类 型...7 计 算 机 系 软 件 工 程 专 业 习 指 南 广 东 科 技 院 计 算 机 系 2015-9-1 软 件 工 程 专 业 习 指 南 目 录 一 软 件 工 程 专 业 设 置 背 景 与 发 展 前 景... 3 二 软 件 工 程 专 业 实 践 教 条 件... 4 三 软 件 工 程 专 业 课 程 类 型 及 核 方 式... 6 1. 软 件 工 程 专 业 课 程 类 型...7

More information

1 4 1.1 4 1.2..4 2..4 2.1..4 3.4 3.1 Java.5 3.1.1..5 3.1.2 5 3.1.3 6 4.6 4.1 6 4.2.6 5 7 5.1..8 5.1.1 8 5.1.2..8 5.1.3..8 5.1.4..9 5.2..9 6.10 6.1.10

1 4 1.1 4 1.2..4 2..4 2.1..4 3.4 3.1 Java.5 3.1.1..5 3.1.2 5 3.1.3 6 4.6 4.1 6 4.2.6 5 7 5.1..8 5.1.1 8 5.1.2..8 5.1.3..8 5.1.4..9 5.2..9 6.10 6.1.10 Java V1.0.1 2007 4 10 1 4 1.1 4 1.2..4 2..4 2.1..4 3.4 3.1 Java.5 3.1.1..5 3.1.2 5 3.1.3 6 4.6 4.1 6 4.2.6 5 7 5.1..8 5.1.1 8 5.1.2..8 5.1.3..8 5.1.4..9 5.2..9 6.10 6.1.10 6.2.10 6.3..10 6.4 11 7.12 7.1

More information

javascript sdk javascript sdk 列出 Bucket 内的对象上传 textarea 内容到 Bucket 上传本地文件生成私有下载链接生成带过期时间的私有链接删除对象下载对象拷贝对象查看文件访问权限设置文件访问权限获取静态网站配置设置静态网站删除静态网站查询对象元数据查询

javascript sdk javascript sdk 列出 Bucket 内的对象上传 textarea 内容到 Bucket 上传本地文件生成私有下载链接生成带过期时间的私有链接删除对象下载对象拷贝对象查看文件访问权限设置文件访问权限获取静态网站配置设置静态网站删除静态网站查询对象元数据查询 javascript sdk javascript sdk 列出 Bucket 内的对象上传 textarea 内容到 Bucket 上传本地文件生成私有下载链接生成带过期时间的私有链接删除对象下载对象拷贝对象查看文件访问权限设置文件访问权限获取静态网站配置设置静态网站删除静态网站查询对象元数据查询桶的多版本 Bucket 开启对象多版本支持挂起 Bucket 的多版本对象功能浏览器客户端浏览器客户端的

More information