Java SSH 框架案例教程学生用书


翰子昂实训体系前导理论教材丛书序 翰子昂实训体系前导理论教材丛书序 在政府大力支持职业教育的良好背景下,高等职业教育已成为促进经济发展、社会进步 和提高劳动者就业率的一条重要途径。通过几十年的教育积累,职业院校已经形成了一整套 完整的教育理论及实施体系,并拥有强大的专业化教师队伍,为我国教育长期发展奠定了坚 实的人才基础。随着社会经济的飞速发展,职业教育作为国家发展的持续推动力,其重要程 度已为社会各界所认知。 不可否认,职业教育体制在不断发展和完善的同时,其自身的一些局限性随着社会发展 对人才需求格局的变化也逐渐显现出来。高等职业院校要办出特色,必须避免走“纯粹学历 教育”的误区,要在教育思想上突破传统学历教育所形成的禁锢,从人才的培养目标出发, 以先进的职业教育思想为指导,改革传统的教材教学方法体系,回归高等职业教育的本义。 职业教育中的软件职业教育更是面临新的挑战:软件产业是当今世界上发展最快的朝阳 产业,并将成为二十一世纪世界经济增长和社会发展的重要推动力,软件人才是产生这种推 动力的主要源泉,软件职业教育人才输出的质量与数量将对软件产业的发展影响重大。 为提高软件职业教育的人才输出的质量和数量,北京翰子昂教育科技发展有限公司把多 年积累的 IT 从业经验、丰富的软件开发经验、系统的实训人才培养经验相结合,同时调研走 访了上百家国内软件企业的相关岗位和人力资源负责人,分析了多家国内外职业教育机构的 课程体系,最后在几十位软件领域专家和资深软件工程师、项目经理的协同配合下潜心研发 出“翰子昂实训体系”,目的是让更多、更广的职业院校学生接受“软件实训”,从而提高 学生整体职业素养和技术水平,在校即达到企业对软件人才的需求,缩小学生与企业之间的 距离,从而实现学生从学校到企业的平稳过渡。 翰子昂实训体系前导理论教材丛书的作用对于翰子昂实训体系是不可或缺的。在进行项 目实训之前学生的知识结构水平参差不齐,理论知识不够牢固,直接进行项目的开发会存在 一定的知识真空区,所以理论知识的学习是非常有必要的。这套前导理论教材是在分析了大 量的项目,并且总结了当前流行的开发技术特点之后归纳出的一系列基础知识理论,这些理 论知识能够在将来的项目实训中得到很广泛的应用。可以说前导理论教材就是为了实训项目 而生。实训过程中,牢固的基础知识能够提高项目开发的效率,所谓“工欲善其事,必先利 其器”就是这个道理。 人事部中国高级公务员培训中心、教育部全国高等学校计算机教育研究会、全国信息化 计算机应用技术资格认证管理中心从翰子昂实训体系前导理论教材的策划开始就表示了关注 与支持,并且在丛书开发过程中给予了很多富有针对性的意见与建议。产品能够得到国家有 关部门的认可,我们深感荣幸的同时也更加认识到责任的重大。 为了研发这套前导理论教材,北京翰子昂教育科技发展有限公司汇聚了多名来自知名 IT 公司的软件开发人员、系统分析设计人员、项目经理、人力资源部负责人以及全国重点职业 院校的学术带头人,在此特别感谢他们给予的意见与建议,同时也感谢付出辛勤劳动的研发 小组的成员,更要感谢国家有关部门的支持。 北京翰子昂教育科技发展有限公司 产品开发部 前言 前言 Struts,Hibernet,Spring 简称 SSH,是目前 JavaEE 开发当中最为流行的框架整合方式, Ajax 异步的 JavaScript And XML,是 web2.0 跨时代的技术产物。WebService 通过面向服务 的方式为我们提供了功能更为强大的技术平台,本书以 Struts,Hibernate,Spring,Ajax,We b Service 为核心详细的讲解了这五个组件的基础知识和基本使用方法,并通过一系列集成开 发实例讲解了这五个组件在 JavaEE 项目中的应用。本书对每一个组件的讲解都是以入门级实 例开始的,然后对组件的架构以及各方面的功能进行了深入的探讨。这样安排的好处是使学 员在具备了初步知识的基础上对组件进行更深入的理解,并通过一系列贴近实际的实例来提 高学员对组件的把握能力进而迅速丰富开发经验。 本书共分 6 个专题,14 个案例,通过实现不同的功能来讲解以上技术: 专题一为 Struts 篇,通过四个案例讲解了 Struts 的工作机制,以及 Struts 当中各个组件的 功能和协同工作方式,利用 Struts 提供的 Validator 框架编写实现国际化的程序,并对数据库 连接池进行了深入讨论。 专题二为 Hibernate 篇,通过四个案例,介绍了 Hibernate 原理,Hibernate 核心 API,以 及关联映射关系,并对 Hibernate 的高级查询进行讲解。 专题三为 Spring 篇,通过两个案例深入讨论了 IoC 和 AOP 两个概念,培养学生面向接口 的编程习惯。 专题四为 SSH 整合篇,介绍了 Spring,Struts 和 Hibernate 的多种整合方式,以及三个框 架在项目中的地位和功能。还有一些常用的整合技巧,并提出了声明式事务管理的概念,为 企业级应用提供了更好的解决方法。 专题五为 Ajax 篇,本专题通过两个案例,介绍 Ajax 的基本原理以及对 DWR 框架进行初 步使用。 专题六为 Web Service 篇,本专题通过一个案例,简单介绍 Web Service 的原理和简单 应用。 本书由北京翰子昂教育科技发展有限公司软件工程师实训产品开发部完成。 北京翰子昂教育科技发展有限公司 产品开发部 2008 年 6 月 前言 目录 翰子昂系列理论教材 - I - 目录 专题一 Struts ..................................................................................................................................... 1 教学目标............................................................................................................................. 1 案例一 初识Struts ..................................................................................................................... 2 1. 教学目标........................................................................................................................ 2 2. 工作任务........................................................................................................................ 2 3. 相关实践知识................................................................................................................ 2 3.1 本书相关实例介绍.............................................................................................. 2 3.2 使用MyEclipse6.0 建立Struts工程 ..................................................................... 2 3.3 使用MyEclipse6.0 加速Struts开发 ................................................................... 18 4. 相关理论知识.............................................................................................................. 23 4.1 Struts概念 ........................................................................................................... 23 4.2 Struts与Model2 的关系 ...................................................................................... 24 4.3 Web应用中Struts所处MVC的位置 ................................................................... 24 4.4 Struts的中心控制类ActionServlet ..................................................................... 25 4.5 Struts当中的FormBean....................................................................................... 26 4.6 Struts当中的Action 类....................................................................................... 27 4.7 Struts的核心配置文件struts-config.xml ............................................................ 28 5. 试验.............................................................................................................................. 32 6. 作业.............................................................................................................................. 32 案例二 深入Struts ................................................................................................................... 33 1. 教学目标...................................................................................................................... 33 2. 工作任务...................................................................................................................... 33 3. 相关实践知识.............................................................................................................. 33 3.1 本书相关实例介绍............................................................................................ 33 3.2 使用MyEclipse6.0 实现实例功能..................................................................... 33 4. 相关理论知识.............................................................................................................. 50 4.1 Struts当中的DispatchAction............................................................................... 50 4.2 Struts当中其他控制器组件................................................................................ 52 4.3 Struts当中的多模块编程.................................................................................... 56 5. 试验.............................................................................................................................. 57 6. 作业.............................................................................................................................. 57 案例三 Struts当中使用数据连接池........................................................................................ 58 1. 教学目标...................................................................................................................... 58 2. 工作任务...................................................................................................................... 58 3. 相关实践知识.............................................................................................................. 58 3.1 本章相关实例介绍............................................................................................ 58 3.2 使用Sturts数据库连接池实现论坛登录实例................................................... 59 3.3 使用tomcate数据库连接池实现论坛登录实例 ............................................... 64 4. 相关理论知识.............................................................................................................. 68 4.1 数据源介............................................................................................................ 68 目录 - II - 翰子昂系列理论教材 4.2 Struts配置数据源................................................................................................ 69 4.3 Tomcat6.0 配置数据源....................................................................................... 71 4.4 Struts和I18N ....................................................................................................... 74 5. 试验.............................................................................................................................. 77 6. 作业.............................................................................................................................. 77 案例四 Struts标签和Validator框架......................................................................................... 81 1. 教学目标...................................................................................................................... 81 2. 工作任务...................................................................................................................... 81 3. 相关实践知识.............................................................................................................. 81 4. 相关理论知识.............................................................................................................. 96 4.1 Struts 标签库 ..................................................................................................... 96 4.2 Validator验证框架 ............................................................................................ 105 5. 试验.............................................................................................................................111 6. 作业.............................................................................................................................111 专题二 Hibernate............................................................................................................................113 教学目标..........................................................................................................................113 案例一 Hibernate初探............................................................................................................114 1. 教学目标.....................................................................................................................114 2. 工作任务.....................................................................................................................114 3. 相关实践知识.............................................................................................................114 3.1 手工配置Hibernate框架...................................................................................114 3.2 使用DB Brower进行数据库管理 ................................................................... 125 3.3 使用Hibernate框架实现论坛注册 .................................................................. 130 4. 相关理论知识............................................................................................................ 142 4.1 什么是Hibernate,为什么要用Hibernate ...................................................... 142 4.2 hibernate.cfg.xml文件元素分析....................................................................... 145 4.3 UserInfo.hbm.xml文件元素分析 ..................................................................... 145 4.4 知识扩展.......................................................................................................... 148 5. 实验............................................................................................................................ 149 6. 课后作业.................................................................................................................... 149 案例二 Hibernate核心API..................................................................................................... 150 1. 教学目标.................................................................................................................... 150 2. 工作任务.................................................................................................................... 150 3. 相关实践知识............................................................................................................ 150 3.1 使用Hibernate完成登录任务 .......................................................................... 150 3.2 使用Hibernate对表进行修改和删除 .............................................................. 153 4. 相关理论知识............................................................................................................ 156 5. 实验............................................................................................................................ 163 6. 作业............................................................................................................................ 164 案例三 Hibernate 关联映射................................................................................................. 165 1. 教学目标.................................................................................................................... 165 2. 工作任务.................................................................................................................... 165 目录 翰子昂系列理论教材 - III - 3. 相关实践知识............................................................................................................ 165 3.1 配置Hibernate关联通过二级栏目得到栏目的信息 ...................................... 165 3.2 添加一个栏目“Ruby”,增加Ruby下属的三个子栏目............................... 175 3.3 删除一级栏目中的“.net”项........................................................................ 177 3.4. 将“Ruby”子项“书籍教程”移动到“Java”项目中 ............................. 177 3.5. 建立学生表和教师表完成多对多关系映射................................................. 178 4. 相关理论知识............................................................................................................ 183 4.1 一对多关联...................................................................................................... 183 4.2 双向多对多关联.............................................................................................. 187 5. 实验............................................................................................................................ 188 6. 作业............................................................................................................................ 188 案例四 Hibernate高级查询................................................................................................... 189 1. 教学目标.................................................................................................................... 189 2. 工作任务.................................................................................................................... 189 3. 相关实践知识............................................................................................................ 189 3.1 本章相关实例介绍.......................................................................................... 189 3.2 使用MyEclipse6.0 实现实例功能................................................................... 189 4. 相关理论知识............................................................................................................ 208 4.1 Hibernate的检索方式....................................................................................... 208 4.2 HQL检索方式................................................................................................... 209 4.3 QBC检索方式................................................................................................... 210 4.4 SQL检索方式 ....................................................................................................211 4.5 使用别名.......................................................................................................... 212 4.6 多态查询.......................................................................................................... 213 4.7 对查询结果排序.............................................................................................. 213 4.8 分页查询.......................................................................................................... 214 4.9 检索单个对象.................................................................................................. 215 5. 试验............................................................................................................................ 216 6. 课后作业.................................................................................................................... 216 专题三 Spring................................................................................................................................ 217 教学目标......................................................................................................................... 217 案例一 Spring IoC基础......................................................................................................... 218 1. 教学目标.................................................................................................................... 218 2. 工作任务.................................................................................................................... 218 3. 相关实践知识............................................................................................................ 218 3.1 使用MyEclipse添加Spring开发环境.............................................................. 218 3.2 Spring 第一个“Hello Word”程序................................................................ 223 3.3 Spring IoC实现简单员工管理程序 ................................................................. 224 4. 相关理论知识............................................................................................................ 229 4.1 Spring概念........................................................................................................ 229 4.2 Spring历史........................................................................................................ 230 4.3 Spring包含的模块............................................................................................ 230 目录 - IV - 翰子昂系列理论教材 4.4 Spring的Ioc容器............................................................................................... 231 5. 试验............................................................................................................................ 248 6. 作业............................................................................................................................ 248 案例二 Spring AOP基础 ....................................................................................................... 249 1. 教学目标.................................................................................................................... 249 2. 工作任务.................................................................................................................... 249 3. 相关实践知识............................................................................................................ 249 4. 相关理论知识............................................................................................................ 259 4.1 Spring AOP简介 ............................................................................................... 259 4.2 AOP中的概念................................................................................................... 260 4.3 AOP代理........................................................................................................... 260 4.4 Spring对AOP的支持 ........................................................................................ 265 5. 试验............................................................................................................................ 285 6. 作业............................................................................................................................ 285 专题四 SSH整合 ........................................................................................................................... 287 1. 教学目标.................................................................................................................... 287 2. 工作任务.................................................................................................................... 287 3. 相关实践知识............................................................................................................ 287 4. 相关理论知识............................................................................................................ 314 4.1 SSH简介 ........................................................................................................... 314 4.2 Spring整合Hibernate ........................................................................................ 315 4.3 管理SessionFactory ......................................................................................... 315 4.4 Spring对Hibernate的简化 ................................................................................ 316 4.5 使用HibernateTemplate ................................................................................... 317 4.6 Hibernate的DAO实现 ...................................................................................... 321 4.7 声明式事务管理.............................................................................................. 324 4.8 整合Struts ........................................................................................................ 328 5. 试验............................................................................................................................ 332 6. 作业............................................................................................................................ 332 专题五 Ajax................................................................................................................................... 333 教学目标......................................................................................................................... 333 案例一 AJAX基础知识......................................................................................................... 334 1. 教学目标.................................................................................................................... 334 2. 工作任务.................................................................................................................... 334 3. 相关实践知识............................................................................................................ 334 3.1 建立一个类似Google Suggest应用................................................................. 334 3.2 建立用户注册时用户名验证应用.................................................................. 339 4. 相关理论知识............................................................................................................ 346 4.1 什么是AJAX,为什么要使用AJAX?.......................................................... 346 4.2 AJAX使用的技术............................................................................................. 349 4.3 AJAX开发流程................................................................................................. 356 5. 实验............................................................................................................................ 359 目录 翰子昂系列理论教材 - V - 6. 作业............................................................................................................................ 359 案例二 DWR框架入门 ......................................................................................................... 360 1. 教学目标.................................................................................................................... 360 2. 工作任务.................................................................................................................... 360 3. 相关实践知识............................................................................................................ 360 3.1 用DWR建立一个Hello World应用................................................................. 360 3.2 用DWR框架建立二级菜单联动的应用......................................................... 365 4. 相关理论知识............................................................................................................ 369 4.1 什么是DWR .................................................................................................... 369 4.2 web.xml文件配置............................................................................................. 370 4.3 dwr.xml文件配置.............................................................................................. 371 4.4 engine.js 功能 .................................................................................................. 373 4.5 util.js 功能........................................................................................................ 374 4.6 综合分析Hello World应用 .............................................................................. 376 5. 实验............................................................................................................................ 377 6. 作业............................................................................................................................ 377 专题六 Web Service基础知识 ...................................................................................................... 379 1. 教学目标.................................................................................................................... 379 2. 工作任务.................................................................................................................... 379 3. 相关实践知识............................................................................................................ 379 4. 相关理论知识............................................................................................................ 400 4.1 WebService起源................................................................................................ 400 4.2 WebService概念................................................................................................ 401 4.3 WebService工作原理........................................................................................ 402 4.4 什么是SOAP.................................................................................................... 403 4.5 什么是WSDL .................................................................................................. 404 4.6 什么是UDDI.................................................................................................... 406 4.7 什么是Axis引擎 .............................................................................................. 407 4.8 XFire构建WebService步骤 .............................................................................. 408 5. 实验............................................................................................................................ 408 6. 作业............................................................................................................................ 409 专题一 Struts 翰子昂系列理论教材 - 1 - 专题一 Struts 教学目标 掌握 Struts 基本原理 深入理解 Struts 当中的控制器组件 Struts 当中使用数据库连接池 Struts 标签及 Validator 框架 Java 框架 - 2 - 翰子昂系列理论教材 案例一 初识 Struts 1. 教学目标 1.1 使用工具怎样建立一个 Struts 工程 1.2 Struts 是怎样实现 MVC 三层架构的 1.3 理解 Struts 当中的 ActionServlet,FormBean 和 Action 的用法 1.4 Struts 相关配置文件的写法 2. 工作任务 2.1 使用 Struts 框架实现论坛登录示例 3. 相关实践知识 3.1 本书相关实例介绍 在本书中,将给出一个完整的论坛实例,该论坛的功能如下:普通网民可以访问论坛注 册成为会员,该论坛会员可以登录论坛发表帖子和回复他人帖子,论坛管理员可以登录论坛 后台管理论坛用户、帖子以及帖子类别。 本实例使用 MyEclipse6.0 开发,数据库采用 MySql,并最终将程序部署到 Tomcat6.0 服 务器当中,实例数据库 mybbs 当中共涉及六张表,分别是 admin,answer,item,subitem,t opic,userinfo,我们会在适当的章节一一向学员介绍每张表的结构。 3.2 使用 MyEclipse6.0 建立 Struts 工程 打开 MyEclipse6.0,创建步骤具体如下: 1. 首先建立一个 Web 项目,点击菜单栏【File】Î【New】Î【Web Project】,在 Proje ct Name 当中输入项目名称“MyBBS”,然后点击【Finish】。 图 1-1 建立 Web 工程 专题一 Struts 翰子昂系列理论教材 - 3 - 图 1-2 命名 Web 工程 图 1-3 Web 工程结构 2. 然后在项目中加入 Struts 支持,右键点击项目根节点【MyBBS】Î【MyEclipse】Î【A dd Struts Capabilities】,然后点击【Finish】。 Java 框架 - 4 - 翰子昂系列理论教材 图 1-4 添加Struts 支持 专题一 Struts 翰子昂系列理论教材 - 5 - 图 1-5 添加 Struts 支持 图 1-6 添加了Struts 的 Web 工程 3. 这样,一个可支持 Struts 的工程就建立完成了我们可以看见项目中加入了 Struts 1.2 Libraries 类库,打开 web.xml 可以看到在 web-app 节点下增加了如下信息: action org.apache.struts.action.ActionServlet Java 框架 - 6 - 翰子昂系列理论教材 config /WEB-INF/struts-config.xml debug 3 detail 3 0 action *.do 4. 下面我们用 Struts 来实现 MyBBS 当中的论坛登录功能,此功能设计到 userinfo 表,结 构如下: 字段名称 类型 大小 是否为空 备注 userid int 4 否 主键,自增,代表人员编号 username varchar 20 否 代表用户名 userpwd varchar 20 否 代表用户密码 userques varchar 50 否 代表密码找回问题 userans varchar 50 否 代表密码找回答案 integral int 4 否 代表用户积分 grade varchar 20 否 代表用户等级 useremail varchar 20 否 代表用户 Email sex bit 2 否 代表用户性别 1 男 0 女 5. 在 MySql 数据库中创建 mybbs 数据库和 userinfo 表的脚本如下: create database mybbs; use mybbs; create table `mybbs`.`userinfo`( `userid` int not null auto_increment, `username` varchar(20) default '' not null, `userpwd` varchar(20) default '' not null, `userques` varchar(50) default '' not null, 专题一 Struts 翰子昂系列理论教材 - 7 - `userans` varchar(50) default '' not null, `integral` int not null, `grade` varchar(20) default '' not null, `useremail` varchar(20) default '' not null, `sex` bit, primary key (`userid`) )TYPE=INNODB, default character set gbk; 6. 实现这样的功能,我们首先需要一个 Web 页面,用以用户登录时填写登录信息,在 W ebRoot 下建立 jsp 文件夹,并在其中建立 login.jsp 代码如下:
用户名: 密  码: 验证码:
7. 建立 success.jsp 代码如下: 登录成功 8. 建立 error.jsp 代码如下: 您的用户名或密码错误 9. 建立用以生成验证码的 image.jsp 代码如下: <%@ page contentType="image/jpeg" import="java.awt.*, java.awt.image.*,java.util.*,javax.imageio.*" pageEncoding="GB2312" %> <%! Color getRandColor(int fc,int bc){//给定范围获得随机颜色 Random random = new Random(); if(fc>255) fc=255; if(bc>255) bc=255; int r=fc+random.nextInt(bc-fc); int g=fc+random.nextInt(bc-fc); int b=fc+random.nextInt(bc-fc); Java 框架 - 8 - 翰子昂系列理论教材 return new Color(r,g,b); } %> <% //设置页面不缓存 response.setHeader("Pragma","No-cache"); response.setHeader("Cache-Control","no-cache"); response.setDateHeader("Expires", 0); //在内存中创建图象 int width=60, height=20; BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); //获取图形上下文 Graphics g = image.getGraphics(); //生成随机类 Random random = new Random(); //设定背景色 g.setColor(getRandColor(200,250)); g.fillRect(0, 0, width, height); //设定字体 g.setFont(new Font("Times New Roman",Font.PLAIN,18)); //随机产生 155 条干扰线,使图象中的认证码不易被其它程序探测到 g.setColor(getRandColor(160,200)); for (int i=0;i<155;i++) { int x = random.nextInt(width); int y = random.nextInt(height); int xl = random.nextInt(12); int yl = random.nextInt(12); g.drawLine(x,y,x+xl,y+yl); } //取随机产生的认证码(4 位数字) String sRand=""; for (int i=0;i<4;i++){ String rand=String.valueOf(random.nextInt(10)); sRand+=rand; //将认证码显示到图象中 g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110) )); //调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成 g.drawString(rand,13*i+6,16); 专题一 Struts 翰子昂系列理论教材 - 9 - } //将认证码存入 SESSION session.setAttribute("rand",sRand); //图象生效 g.dispose(); //输出图象到页面 ImageIO.write(image, "JPEG", response.getOutputStream()); out.clear(); out = pageContext.pushBody(); %> 10. 新建 com.mybbs.pojo 包并在包下建立叫做 Userinfo 的 pojo 类,其中包括如下字段: //用户 ID private int userid; //用户名 private String username; //密码 private String userpwd; //密码找回问题 private String userques; //密码找回答案 private String userans; //用户积分 private int ntegral; //用户等级 private String grade; //用户 Email private String useremail; //性别 private byte sex; 并用 MyEclipse 生成访问器。 11. 新建 com.mybbs.dao 包并在包下建立 JdbcDao.java 和 UserinfoDao.java 类用于访问数 据库,分别在类中,键入如下代码: import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; Java 框架 - 10 - 翰子昂系列理论教材 public class JdbcDao { //数据库连接对象 protected Connection conn=null; //定义 Statement 变量 protected Statement stmt=null; //定义 PreparedStatement 变量 protected PreparedStatement pstmt=null; //结果集 protected ResultSet rs=null; //MySql 数据库驱动 Private String driverClass_Name="com.mysql.jdbc.Driver"; //MySql 数据库 url Private String url="jdbc:mysql://localhost:3306/mybbs?user=root&password=root"; //默认的构造函数 public JdbcDao(){} //得到数据库连接 public void getConnection() { Connection conn=null; try { //JDBC 连接 Class.forName(driverClass_Name); conn=DriverManager.getConnection(url); this.conn=conn; } catch(SQLException ex) { ex.printStackTrace(); } catch(ClassNotFoundException ex) { ex.printStackTrace(); } } //关闭数据库连接 public void closeConnction() { if(rs!=null) { try 专题一 Struts 翰子昂系列理论教材 - 11 - { rs.close(); } catch(SQLException ex) { ex.printStackTrace(); } } if(stmt!=null) { try { stmt.close(); } catch(SQLException ex) { ex.printStackTrace(); } } if(pstmt!=null) { try { pstmt.close(); } catch(SQLException ex) { ex.printStackTrace(); } } if(conn!=null) { try { conn.close(); } catch(SQLException ex) { ex.printStackTrace(); } } } Java 框架 - 12 - 翰子昂系列理论教材 } import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.List; import java.util.ArrayList; import com.mybbs.pojo.Userinfo; public class UserinfoDao extends JdbcDao { //定义自身的一个变量 private static UserinfoDao userinfoDao = null; //定义自身私有的构造函数 private UserinfoDao() { }; //通过一个简单的单利模式将 UserinfoDao 返回到客户端 public synchronized static UserinfoDao getUserinfoDao() { if (userinfoDao != null) { return userinfoDao; } userinfoDao = new UserinfoDao(); return userinfoDao; } //得到用户信息,以 list 的形式返回到客户端 public List getPerson(Userinfo user) { //声明用以存放用户信息的集合 List list = new ArrayList(); //得到数据库连接 super.getConnection(); //定义要执行的 sql 语句 String sqlString = "select * from userinfo where username=? and userpwd=?"; try { //预编译 sql 返回一个 PrePareStatement 对象 pstmt = conn.prepareStatement(sqlString); //传入参数 pstmt.setString(1, user.getUsername()); pstmt.setString(2, user.getUserpwd()); //执行 sql 并返回结果集 rs = pstmt.executeQuery(); //循环遍历结果集 if (rs.next()) { Userinfo myuser = new Userinfo(); 专题一 Struts 翰子昂系列理论教材 - 13 - myuser.setUsername(rs.getString(2)); myuser.setUserpwd(rs.getString(3)); list.add(myuser); } } catch (Exception ex) { ex.printStackTrace(); } finally { super.closeConnction(); } return list; } } 12. 新建 com.mybbs.service 包并在包下建立 UserinfoService.java 类,判断用户登陆,键 入如下代码: import com.mybbs.pojo.Userinfo; import com.mybbs.dao.UserinfoDao; import java.util.List; import java.util.Iterator; public class UserinfoService { //得到操作用户信息的 DAO private UserinfoDao userinfoDao=UserinfoDao.getUserinfoDao(); //验证用户登录信息,用户名密码正确返回 true public boolean userLogin(Userinfo user) { boolean isLogin=false; //根据传入的 user 对象从数据库中查询结果集 List list=userinfoDao.getPerson(user); Iterator it=list.iterator(); //如果集合中有数据说明用户可以登录 if(it.hasNext()) { isLogin=true; } return isLogin; } } 13. 新建 com.mybbs.actionform 包并在包下建立 LoginActionform.java 类,键入如下代码: import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.ActionErrors; Java 框架 - 14 - 翰子昂系列理论教材 import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; public class LoginActionform extends ActionForm { //用户密码 private String password; //用户名 private String name; //验证码 private String rand; public String getRand() { return rand; } public void setRand(String rand) { this.rand = rand; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } //actioform 特有的验证方法 public ActionErrors validate(ActionMapping mapping,HttpServletRequest request) { return null; } //actionform 特有的重置方法 public void reset(ActionMapping mapping,HttpServletRequest request) { } } 14. 新建 com.mybbs.action 包并在包下建立 LoginAction.java 类,键入如下代码: import javax.servlet.http.HttpServletRequest; 专题一 Struts 翰子昂系列理论教材 - 15 - import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import com.mybbs.actionform.LoginActionform; import com.mybbs.pojo.Userinfo; import com.mybbs.service.UserinfoService; import javax.servlet.http.HttpSession; public class LoginAction extends Action { String scriptStr=null; public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { //得到 LoginActionForm 对象 LoginActionform LoginActionForm = (LoginActionform) form; //得到 session 对象 HttpSession session=request.getSession(); //判断验证码是否输入正确 if(!session.getAttribute("rand").toString().equals(LoginActionForm.ge tRand())) { //页面跳转 return mapping.findForward("self"); } //实例化用户 bean Userinfo user=new Userinfo(); //得到用户输入的用户名 user.setUsername(LoginActionForm.getName()); //得到用户输入密码 user.setUserpwd(LoginActionForm.getPassword()); //声明业务逻辑对象 UserinfoService userinfoService=new UserinfoService(); //如果用户名和密码输入正确登录成功 if(userinfoService.userLogin(user)) { return mapping.findForward("success"); } //如果输入错误提示错误 else { return mapping.findForward("error"); Java 框架 - 16 - 翰子昂系列理论教材 } } } 15. 打开 WEB-INF 当中的,struts-config.xml 文件,在其中键入: 16. 部署运行程序,在地址栏里输入 http://localhost:8080/mybbs/jsp/login.jsp 打开登录页 面。 图 1-7 登录页面 17. 输入正确的用户名,密码和验证码后点击提交,提示登录成功。 专题一 Struts 翰子昂系列理论教材 - 17 - 图 1-8 提示登录成功 18. 验证码、用户名或密码输入错误将在登录页面提示用户。 图 1-9 提示用户名密码错误 Java 框架 - 18 - 翰子昂系列理论教材 3.3 使用 MyEclipse6.0 加速 Struts 开发 我们可以利用 MyEclipse 提供的工具来简化 Struts 的开发。我们现在用工具来建立 Login Actionform.java,LoginAction.java 和相关的 struts-config.xml。 1. 在建立完 MyBBS 工程,并在工程中添加了 Struts 支持后,点击菜单栏【File】Î【N ew】Î【Other】。 图 1-10 利用工具建立 struts 2. 选中【Web-Struts】Î【Struts1.2】 Î【Struts1.2 Form,Action & JSP】。 专题一 Struts 翰子昂系列理论教材 - 19 - 图 1-11 工具建立 Struts 图 1-12 工具建立 Struts Java 框架 - 20 - 翰子昂系列理论教材 图 1-13 工具建立 Struts 3. 点击打开后填写 FormBean 的信息,Name 中填写“LoginActionForm”,SuperClass 选中“org.apache.struts.action.ActionForm”,Form Type 中填写“com.mybbs.struts.actionfro m .LoginActionForm”。点击【add】为 LoginActionForm 添加“name”和“password”属性。 专题一 Struts 翰子昂系列理论教材 - 21 - 图 1-14 工具建立 Struts Java 框架 - 22 - 翰子昂系列理论教材 图 1-15 工具建立 Struts 4. 点击【Next】填写 action 的信息,path 填写“/LoginAction,superclass”,Superclass 选 中”org.apahce.struts.action.Action”,Type 填写“com.mybbs.action.LoginAction”。Name 中填 写”LoginActionForm”。Attribute 和 scope 使用工具生成的默认值,点击【Finish】,这样 Lo ginAtion,LoginActionForm 以及相关的 struts-config.xml 就算生成了。 专题一 Struts 翰子昂系列理论教材 - 23 - 图 1-16 工具建立 Struts 4. 相关理论知识 4.1 Struts 概念 Struts 的含义是“支柱,枝干”,来源于在建筑和旧式飞机中使用的金属支架,它的目的 是为了减少程序开发的时间,项目的创建者认为 JSP,Servlet 的存在虽然可以帮助用户解决 大部分的问题,但是由于它们的编码对项目的开发带来了很多的不便,可重用性也差,所以 提出 Struts 这个框架,帮助用户在最短的时间内解决这些问题。Struts 框架提供如下的服务: 作为控制器层的 Servlet。 提供大量的标签库。 提供了用于国际化的框架,利用不同的配置文件,可以帮助用户选择合适自己的语言。 提供了 JDBC 的实现,来定义数据源和数据库连接池。 XML 语法分析的工具。 文件下载机制。 Java 框架 - 24 - 翰子昂系列理论教材 开始的时候,Struts 仅被作为开发包发布,经过不断的扩充,现在的 Struts 已经是内容相 当完整的框架了。 4.2 Struts 与 Model2 的关系 在 Java Web 发展过程中的应用架构 Model1 和 Model2,而 Struts 就是基于 Model2 的架 构产生的。Struts 的 3 层模型如下: 在视图层,除了可以使用 JSP 及其标签库以外,还提供了一个强大的 Struts 标签库,来 帮助用户解决显示逻辑,并且利用 ActionForm 组件将信息递交到控制器层。 在控制器层,Struts 提供了一个控制器组件 ActionServlet,它继承自 HttpServlet,并重载 了 HttpServlet 的 doGet(),doPost()方法,可以接受 HTTP 的响应,并进行转发,同时还提供 了使用 XML 进行转发 Mapping(映射)的功能。 在模型层,Struts 提供 Action 对象,来管理业务逻辑的调用,帮助用户分离业务逻辑, 也就是说 Struts 本身不实现业务逻辑,但可以调用已完成的业务逻辑。 4.3 Web 应用中 Struts 所处 MVC 的位置 在 MVC 模型中,可以将 Struts 大致分割成如图 图 1-17 Struts MVC 图 Struts 标签库:处于视图层,用来辅助 JSP 展现页面,类似 JSP 自带标签库。 ActionForm 对象:视图层和控制器层之间的接口,可以看成是一个值对象(Value Objec t,VO)提供 get(),set()方法,在 Struts 中这个对象还提供了校验数据的方法。 ActionServlet 控制器组件:在控制层中控制页面流转,并调用模型层,继承自 HttpServl et。 Action 对象:在控制层和模型层进行交互,该对象被 ActionServlet 组件调用。可以在 Ac tion 中调用业务逻辑,并将页面回复给 ActionServlet 组件,它是控制层和模型层之间的桥梁。 Struts 通过配置文件将上面这些组件和对象关联起来一起工作。在 Web.xml 中,应该将 S truts 提供的 ActionServlet 配置成一个 Servlet,接着指定 Struts 自身的配置文件 struts-config.x ml,这个文件在 Servlet 中被调用解析。Struts 是靠 XML 的配置来完成各层之间的运行。 批注 [w1]: 删除一句话 专题一 Struts 翰子昂系列理论教材 - 25 - 4.4 Struts 的中心控制类 ActionServlet ActionServlet 来自于 org.apache.struts.action 包,它继承自 HttpServlet,作为 Struts 的 Ser vlet 控制器,是 Struts 框架控制器的核心。Web 程序初始化时,会读取 struts-config.xml 中的 内容作为它的环境上下文,所有客户端的请求都会通过 ActionServlet 开始整个流程,请求到来 时无论 doGet 还是 doPost 方法都会调用 process 方法,ActionServlet 的 process 方法十分重要, 它调用 getModuleConfig 得到配置上下文,将其传入 getRequestProcessor 方法,再由 getReque stProcessor 来返回 RequestProcessor 对象,并调用 RequestProcessor 的 process 方法来完成整个 工作流程。对于 ActionServlet 来说,程序员完全可以继承它,并覆盖其中的某些方法来完成 自己需要的全新的 ActionServlet。 下面是一段 ActionServlet 的代码: public void doGet(HttpServletRequest request,HttpServletResponse response)throws IOException,ServletException { process(request,response); } public void doPost (HttpServletRquest request,HttpServletResponse response)throws IOException,ServletException { process(request,response); } protected void process(HttpServletRequest request,HttpServletResponse response)throws IOException,ServletException { RequestUtils.selectModule(request,getServletContext()); getRequestProcessor(getModuleConfig(request)).process(request,response); } 在 web.xml 当中我们将 ActionServlet 配置成一个普通的 Servlet,通过 url-pattern 的设置 后,如果有“*.do”的请求,就会调用该 ActionServlet。 action org.apache.struts.action.ActionServlet config /WEB-INF/struts-config.xml debug 3 Java 框架 - 26 - 翰子昂系列理论教材 detail 3 0 action *.do 4.5 Struts 当中的 FormBean ActionForm 是 Struts 框架中的一个重要组成部分,它保存 request 参数。ActionForm 从本 质上讲,就是一个 JavaBean,这些 Bean 有与 HTTP request 参数相对应的属性名称,控制器 根据 struts-config.xml 文件创建 ActionForm Bean 的实例。并根据配置,把实例传给 Action 类, ActionForm Bean 的实例也被称为表单。ActionForm Bean,必须继承 org.apache.struts.action. ActionForm 类。当 ActionServlet 调用 Action 类时,它创建生成相应的 ActionForm Bean 实例, 然后把它传递个 Action 类。ActionForm Bean 中会包含许多 get/set 方法,set 方法的调用是由 Struts 框架自动完成的,请求开始处理时,Struts 框架从 request 对象中提取 url 参数,并自动 使用这些参数为 FormBean 中对应的属性赋值。 validate 方法可以对 ActionFrom Bean 中的内容进行验证,原形如下: public ActionErrors validate(ActionMapping mapping,ServletRequest request) public ActionErrors validate(ActionMapping mapping,HttpServletRequest request) 在 ActionFrom Bean 中重载 validate 方法后,并不会自动调用,要想 Struts 框架得到 request 参数后,自动调用 validate 方法,必须在 struts-config.xml 中,把使用该 ActionForm Bean 的 元素的 validate 属性设置为 true。 在 validate 方法验证出错误后,需要产生一个 ActionError 的实例,ActionErrors 中会有 0 个或多个 ActionError 实例。validate 方法返回 ActionErrors,如果 ActionErrors 中 ActionError 的实例多于 0,那么就说明 validate 方法验证到了一个错误,程序将不在进入 Action,而会根 据元素的 input 属性值,返回指定的页面。如下例我们可以用 validate 方法验证 name 属性是否为空。 public ActionErrors validate(ActionMapping mapping,ServletRequest request) { ActionErrors errors=new ActionErrors(); if(name.equals(“”) { ActionError error=new ActionError(“error.errorname”); errors.add(“noname”,error); 专题一 Struts 翰子昂系列理论教材 - 27 - } } 本例当中的 error.errorname 可在资源文件当中找到用于读取错误信息,并通过 Struts 的自 带标签显示给客户端。有关资源文件和 Struts 标签的用法本 书会在后续章节为学员介绍。 Reset 方法可以对 ActionFrom Bean 中的内容进行复位,原形如下: public void reset(ActionMapping mapping,ServletRequest request) public void reset(ActionMapping mapping,HttpServletRequest request) 使用 reset 方法,主要考虑 ActionFrom bean 的作用范围是什么,元素的 scope 属 性指定了 ActionForm 的作用范围,两种作用范围分别为“request”和“session” 当 scope 属性被指定为“request”,也即被写成“scope=request”时,每次调用 ActionFo rm Bean 都会生成一个新的实例。因此 ActionServlet 调用的 ActionForm Bean 不会受到其他 的影响,每次提交或刷新页面都会得到一个新的 ActionForm Bean。 当 scope 属性指定了为“session”,也即被写成“scope=session”的时候,由于在一次生 命周期中本 ActionForm Bean 只创建一次,其他的调用都会使用第一次创建的 Action Form Bean。也就是说,当 ActionForm Bean 中的变量存在后,如果不被改变,任何时候调用该 Ac tionForm Bean 都会得到第一次创建的值,这样的特性在跨页面收集数据时十分有效。但这样 有可能对该 ActionFrom Bean 中的部分数据值进行修改,在“scope=session”的时候,那些没 有被修改的值就会沿用之前的值,这样 ActionForm Bean 将成为一个新旧变量的混合体。 这时,就可以使用 reset 方法来解决问题。再次为 ActionForm Bean 的变量赋值前,调 用 reset 方法使其复原,这个工作是 Struts 框架自动进行的。如下例我们可以用 reset 方法复位 name 的值。 public void reset(ActionMapping mapping,HttpServletRequest request) { Name=””; } 4.6 Struts 当中的 Action 类 Struts 中的 Action 类,必须继承 Struts 提供的类 org.apache.action.Action.Actoin 类的目标 是处理一个请求,Struts 框架为每一个 Action 类只创建一个实例,因为所有的用户都使用这 一个实例,所以 Action 类被运行在一个多线程的环境中。 ActionServlet 调用 Action 类,并调用 exectue 方法, Action 类的 execute 方法声明如下: public ActionForward execute(ActionMapping mapping, ActionForm actionForm, ServletRequest request, ServletResponse response ) Java 框架 - 28 - 翰子昂系列理论教材 public ActionForward execute(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response ) exectue 方法有四个参数: 1. ServletRequest 参数与 ServletResponse 参数, Action 类可以用它读取和设置 request 和 response 属性。 2. ActionForm 参数,用于获得请求中的数据。 3.ActionMapping 参数,这个参数在 execute 方法中有很大的作。其具体类型为 org.apache.struts.action.ActionMapping。给对象封装了元素当中的所有信息,通过 ActionMapping 的各个方法,元素当中的所有属性都可以动态修改。 Action 类的 execute 方法的处理流程是:从 actionForm 中得到请求的数据信息。如果有特 殊的需求,也可以利用 request 对象。调用业务逻辑对象。分析业务逻辑对象的调用结果,按 ActionMapping 是的映射信息生成响应。 exectue 方法的返回值是一个 ActionForward 对象。ActionForward 对象可以生成影响信息, ActionMapping 对象的 findForward()方法,可以得到 ActionForward 对象: public ActionForward findForward(String name) ActionMapping 类还提供了一个方法,该方法查找所有与该操作相关的本地转发: public ActionForward[] findForward() ActionMapping 类也提供了一个给映射动态增加 ActionForward 的方法: public void addForward(ActionForward forward) execute 的返回值为 ActionForward 是用来导航的组件,它由 org.apache.struts.ActionForwa rd 来实现。它的功能是当 execute 方法运行完后,ActionServlet 根据 ActionMapping 将信息转 发到适当的地方: return mapping.findForward(“success”) 4.7 Struts 的核心配置文件 struts-config.xml 作为 Struts 框架的核心描述符,struts-config.xml 可以说是“一切尽在掌握”。它不但描 述了 MVC 模型,定义所有视图层和控制层之间的接口(ActionForm),与控制层和模型层的 接口(Action)进行结合,而且可以定义一些附加组件,如国际化信息的资源配置(Resource s)文件,标签库信息以及验证框架等。 简单的说,struts-config.xml 就是将所有动作的映射用一个 XML 反应出来。要真正理解 S 专题一 Struts 翰子昂系列理论教材 - 29 - truts 框架,就必须理解 struts-config.xml。 struts-config.xml 由许多标签组成,由于 XML 的标签通常被称作为元素,所以我们将 X ML 的标签成为元素,而这些元素都会在 ActionServlet 组件中被解析。 配置视图层和控制层的接口:form-beans 元素 在 form-beans 元素当中可以定义 0 个或 1 个以上的 form-bean 元素,每个 form-bean 被认 为是一个 ActionForm 对象,name 属性指定 form-bean 元素的名称,type 属性指定其类名和路 径。本例定义了 1 个 ActionForm 对象 LoginForm。这个对象的具体类都在 com.mybbs.actionform 路径下。 配置全局处理:global-exceptions 元素 该元素配置全局异常处理,其子元素 exception 定义了异常的详细信息。 在 global-exceptions 元素中可以定义 0 个或者 1 个以上的 exception 元素,exception 元素 的 key 属性标识了一个类似“exceptionid=’出错了’”这样的报错信息,这样的信息会被定义 在程序的资源配置文件中(资源配置文件也会被定义在 struts-config.xml 中,将在后面的章节 进行介绍),type 属性代表的是怎样的一个异常。 本实例的含义是,当发生了 java.io.IOException 的异常后,在 error.jsp 中可以显示从资源 配置文件查找到的出错信息。 声明全局转发关系:global-forwards 元素 global-forwards 元素用来声明全局的转发关系,其子元素 forward 负责将请求映射到具体 的映射。 在 global-forwards 元素中可以定义 0 个或 1 个以上的 forward 元素,每个 forward 元素接 收 name 属性定义的请求,并转发到 path 属性定义的另一个请求中去。 本实例定义的 forward 元素,将请求 success 转发到“/ Sampleaction1”。 Java 框架 - 30 - 翰子昂系列理论教材 设置映射:action-mappings 元素 action-mappings 元素用来包含零到多个 action,其子元素 action 负责具体映射的详细信息。 在 action-mapping 元素中可以定义 0 个或 1 个以上的 action 元素。每个 action 元素接受 path 属性定义的请求,并映射到 type 属性所定义的具体 action 对象。在映射过程中,将 name 属性定义的 actionform 一并传过去,它有如下的属性: Parameter,scope 两个属性指定了传送方式和范围,scope 常用的值有两个“session”和 “request”。 validate 属性指定了是否需要 actionform 的验证。 forward 元素与 global-forwards 元素中的 forward 元素的功能一样。 本例中将请求“/LoginAction”映射到是 com.mybbs.action.LoginAction 类实例的 action 对 象,将“LoginForm”作为 actionform 对象,并将其传递到自身的 action 对象。这个 actionfor m 不需要验证,action 对象以 forward 元素定义的“success” 作为标始,转发到“/success.js p 页面”。 配置控制器:controller 元素 controller 元素用于配置 struts 框架的控制器。 controller 元素有许多的属性,这些属性描述如下: className 属性指定了和元素对应的配置类,默认为 org.apache.struts.config.C ontrollerConfig。 contentType 指定响应结果的类型和字符编码,该属性为可选项,默认值为 text/html。如 果在 action 和 jsp 网页也设置了内容类型和字符编码,将会覆盖该设置。 local 属性决定是否把 local 对象保存到当前用户的 session 中,默认为 false。 processorClass 属性指定负责处理请求的 Java 类的完整类名,默认为 org.apache.struts.acti on.RequestProcessor。如果要使用自定义的类,那么自定义的类应该继承 org.apache.struts.acti on.RequestProcessor 类。 专题一 Struts 翰子昂系列理论教材 - 31 - buffersize 属性指定上传文件的输入缓冲的大小,该属性可选,默认为 4096。 tempDir 属性指定文件上传时的临时目录,该属性可选,默认采用 Servlet 容器为 Web 应 用分配的工作目录。 nocache 属性的功能是,是否为响应结果加入参数,默认情况为 false,不加入头参数。若 为 true,在响应的结果中加入特定的头参数:Pragma,Cache-Control,Expires 可以防止页面 被储存在客户浏览器的缓存中。 本例配置了一个 ActionServlet 的辅助功能,该响应结果和编码被指定为“text/html;chars et=GB2312”,并且 Locale 被保存到 session 中,这点在国际化中时常被用到。响应请求的类 已经被定义为“MyRequestProcessor”,这个类是一个自定义类,很明显该类继承自 org.apac he.struts.action.RequestProcessor。 设置资源配置文件的路径:message-resources 元素 message-resources 元素被用来设定资源配置文件的路径,其 parameter 属性指定了路径和 名称,其 key 属性可以帮助程序指定那个文件被调用,当只有一个配置文件时,可以不必指 定 key 属性。 在 message-resources 元素中定义了 key 为“resources1”的文件,该文件的具体路径在 “com.mybbs.struts”目录下,名为 ApplicationResources。在这里,因为只有一个资源配置文 件,所以 key 可以不必指定。 配置插件:plug-in 元素 plug-in 元素用来配置插件,在不需要插件的项目中,该元素可以不必指定。 className 属性提供了实现 org.apache.struts.action.PlugIn 接口的 TilesPlugin 类,表示插 件的主类。插件的参数配置使用 set-property 子元素。 本例是对于 Tiles 插件的配置。className 指定了插件的主类为“org.apache.struts.tiles.Til esPlugin”,很明显这个类实现了 org.apache.struts.action.Plugin interface 接口。该类需要 4 个 配置参数,都通过 set-property 子元素设定。 Java 框架 - 32 - 翰子昂系列理论教材 5. 试验 按照上课的顺序依次练习,主要掌握: 1. 建立 mybbs 数据库及 userinfo 表和 MyBBS 工程。(10 分钟) 2. 编写 login.jsp,success.jsp,error.jsp 页面。(15 分钟) 3. 编写用以生成验证码的 image.jsp。(15 分钟) 4. 编写用于数据访问的 JdbcDao.java,UserinfoDao.java 类。(10 分钟) 5. 编写业务逻辑类 UserinfoService。(10 分钟) 6. 编写 LoginAction 和 LoginActionForm 类。(20 分钟) 7. 配置 struts-config.xml 文件,并调试运行程序。(10 分钟) 6. 作业 利用 Struts 框架实现登录功能,并将登录者信息显示在登录成功页面当中。 专题一 Struts 翰子昂系列理论教材 - 33 - 案例二 深入 Struts 1. 教学目标 1.1 使用 Struts 内置的控制器组件 DispatchAction 1.2 了解其他相关控制器组件的应用 1.3 Struts 当中的多模块编程 2. 工作任务 2.1 使用 Struts 框架实现论坛版块的增删查改 3. 相关实践知识 3.1 本书相关实例介绍 1. 本章我们利用 Struts 内置的控制器组件,实现 MyBBS 论坛后台程序当中对论坛版块 的增删查改,本实例用到了 mybbs 数据库当中的 item 表,其结构如下: 字段名称 类型 大小 是否为空 备注 itemid int 4 否 主键,自增,代表论坛版块 id itemname varchar 50 否 代表版块名称 itemcode varchar 20 否 代表版块编码 其创建脚本如下: use mybbs; create table `mybbs`.`item`( `itemid` int not null auto_increment, `itemname` varchar(50) default '' not null, `itemcode` varchar(50) default '' not null, primary key (`itemid`) )TYPE=INNODB, default character set gbk; 2. MyBBS 后台程序页面,本书采用的是当下比较流行的 CSS+DIV 布局,以便学员课下 学习,但由于 CSS 不是本书所研究的重点,所以本实例所用到的.css 文件和静态页面,我们 会以课件的形式随本书发放到学员手中。 3.2 使用 MyEclipse6.0 实现实例功能 1. 打开 MyEclipse6.0,如上章所述建立 Web 工程,并添加 Struts 支持。 Java 框架 - 34 - 翰子昂系列理论教材 图 1-18 添加了 Struts 的 Web 工程 2. 实现这样的功能,我们首先需要一个 Web 页面,用以将论坛版块信息以表格的形式展 现到客户端,在 WebRoot 下建立 jsp 文件夹,并在其中建立 leibie.jsp: <%@ page language="java" contentType="text/html;charset=gb2312"%> <%@ page import="com.mybbs.pojo.Item"%> <%@ page import="java.util.*"%> MyBBS 后台管理页面
专题一 Struts 翰子昂系列理论教材 - 35 -

帖子类别管理

  • 编号
  • 类别名称
  • 类别编码
  • 修改
  • 删除
<% java.util.List list = (List) request.getAttribute("list"); java.util.Iterator it = list.iterator(); while (it.hasNext()) { Item item = (Item) it.next(); %>
  • <%=item.getSubid()%>
  • <%=item.getSubname()%> Java 框架 - 36 - 翰子昂系列理论教材
  • <%=item.getSubcode()%>
  • 修改
  • 删除
<% } %>
专题一 Struts 翰子昂系列理论教材 - 37 - 3. 建立用以添加论坛版块的的页面 addleibie.jsp。 <%@ page language="java" contentType="text/html;charset=gb2312"%> 商业资讯后台管理主页面

帖子类别管理

添加类别
类别名称:
Java 框架 - 38 - 翰子昂系列理论教材
类别编码:
 
专题一 Struts 翰子昂系列理论教材 - 39 - 4. 用以更新论坛版块的页面 updateleibie.jsp。 <%@ page language="java" contentType="text/html;charset=gb2312"%> <%@ page import="com.mybbs.pojo.Item"%> 商业资讯后台管理主页面

帖子类别管理

添加类别
Java 框架 - 40 - 翰子昂系列理论教材 类别名称:
<% Item item=(Item)request.getAttribute("item"); %>
类别编码:
 
5. 新建 com.mybbs.pojo 包并在包下建立叫作 Item 的 pojo 类,这个类就代表帖子的版块 描述信息,其中包括如下字段: //帖子类别 id private int subid; //帖子类别名称 private String subname; //帖子编号 private int subcode; 6. 新建 com.mybbs.dao 包并在包下建立 JdbcDao.java 和 ItemDao.java 类,JdbcDao.java 用于打开和关闭数据库连接,ItemDao.java 用于对 item 表的持久化操作,分别在类中,键入 如下代码: import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.List; import java.util.ArrayList; import com.mybbs.pojo.Item; public class ItemDao extends JdbcDao { //定义自身的一个变量 private static ItemDao itemDao = null; //定义自身私有的构造函数 private ItemDao() { }; //通过一个简单的单利模式将 ItemDao 返回到客户端 public synchronized static ItemDao getItemDao() { Java 框架 - 42 - 翰子昂系列理论教材 if (itemDao != null) { return itemDao; } itemDao = new ItemDao(); return itemDao; } //得到帖子类别信息,以 list 的形式返回到客户端 public List getItem() { //声明用以存放帖子类别信息的集合 List list = new ArrayList(); //得到数据库连接 super.getConnection(); //定义要执行的 sql 语句 String sqlString = "select * from item"; try { //预编译 sql 返回一个 PrePareStatement 对象 pstmt = conn.prepareStatement(sqlString); rs = pstmt.executeQuery(); //循环遍历结果集 while (rs.next()) { Item item = new Item(); item.setSubid(rs.getInt(1)); item.setSubname(rs.getString(2)); item.setSubcode(rs.getInt(3)); list.add(item); } } catch (Exception ex) { ex.printStackTrace(); } finally { super.closeConnction(); } return list; } //插入帖子类别信息 public void insertItem(Item item) { //得到数据库链接 super.getConnection(); //定义要执行的 sql 语句 String sqlString="insert into Item values(?,?,?)"; try { 专题一 Struts 翰子昂系列理论教材 - 43 - //预编译 sql 返回一个 PrePareStatement 对象 pstmt = conn.prepareStatement(sqlString); pstmt.setInt(1,item.getSubid()); pstmt.setString(2, item.getSubname()); pstmt.setInt(3,item.getSubcode()); //执行 sql 动作 pstmt.executeUpdate(); } catch (Exception ex) { ex.printStackTrace(); } finally { super.closeConnction(); } } //插入帖子类别信息 public int selectMaxItemId() { //得到数据库链接 super.getConnection(); //定义要执行的 sql 语句 String sqlString="select max(itemid) from Item"; try { //预编译 sql 返回一个 PrePareStatement 对象 pstmt = conn.prepareStatement(sqlString); //执行 sql 动作 ResultSet rs=pstmt.executeQuery(); //如果结果集当中有数据 if(rs.next()) { int itemid=rs.getInt(1); return itemid; } return 0; } catch (Exception ex) { ex.printStackTrace(); return 0; } finally { super.closeConnction(); } } //更新帖子类别 Java 框架 - 44 - 翰子昂系列理论教材 public void updateItem(Item item) { //得到数据库链接 super.getConnection(); //定义要执行的 sql 语句 String sqlString="update Item set itemname=? , itemcode=? where itemid=?"; try { //预编译 sql 返回一个 PrePareStatement 对象 pstmt = conn.prepareStatement(sqlString); pstmt.setString(1, item.getSubname()); pstmt.setInt(2,item.getSubcode()); pstmt.setInt(3, item.getSubid()); //执行 sql 动作 pstmt.executeUpdate(); } catch (Exception ex) { ex.printStackTrace(); } finally { super.closeConnction(); } } // 删除帖子类别 public void deleteItem(Item item) { //得到数据库链接 super.getConnection(); //定义要执行的 sql 语句 String sqlString="delete from Item where itemid=?"; try { //预编译 sql 返回一个 PrePareStatement 对象 pstmt = conn.prepareStatement(sqlString); pstmt.setInt(1,item.getSubid()); //执行 sql 动作 pstmt.executeUpdate(); } catch (Exception ex) { ex.printStackTrace(); } finally { super.closeConnction(); } } } 专题一 Struts 翰子昂系列理论教材 - 45 - 7. 新建 com.mybbs.service 包,并在包下建立 ItemService 类,用于封装持久化代码,进 而完成对论坛版块信息的增删查改,其中键入如下代码: import com.mybbs.pojo.Item; import com.mybbs.dao.ItemDao; import java.util.List; import java.util.Iterator; public class ItemService { //得到操作帖子类别信息的 DAO private ItemDao itemDao=ItemDao.getItemDao(); //插入新的帖子类别 public void insertItem(Item item) { //得到新的帖子类别 id item.setSubid(itemDao.selectMaxItemId()+1); itemDao.insertItem(item); } //更新帖子类别 public void updateItem(Item item) { itemDao.updateItem(item); } //删除帖子类别 public void deleteItem(Item item) { itemDao.deleteItem(item); } //得到所有帖子类别信息 public java.util.List getItem() { java.util.List list=itemDao.getItem(); return list; } } 8. 新建 com.mybbs.actionform 包,并在包下建立 Itemform 类,键入如下代码: import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.ActionErrors; Java 框架 - 46 - 翰子昂系列理论教材 import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; public class Itemform extends ActionForm { //帖子类别 id private int subid; //帖子类别名称 private String subname; //帖子编号 private int subcode; public int getSubid() { return subid; } public void setSubid(int subid) { this.subid = subid; } public String getSubname() { return subname; } public void setSubname(String subname) { this.subname = subname; } public int getSubcode() { return subcode; } public void setSubcode(int subcode) { this.subcode = subcode; } //actioform 特有的验证方法 public ActionErrors validate(ActionMapping mapping,HttpServletRequest request) { return null; } //actionform 特有的重置方法 public void reset(ActionMapping mapping,HttpServletRequest request) { } } 9. 新建 com.mybbs.action 包,并在包下建立 ItemAction 类,注意此类继承自 org.apache. 专题一 Struts 翰子昂系列理论教材 - 47 - struts.actions.DispatchAction,在其中键入如下代码: import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import com.mybbs.actionform.Itemform; import com.mybbs.pojo.Item; import com.mybbs.service.ItemService; import javax.servlet.http.HttpSession; import org.apache.struts.actions.DispatchAction; import org.apache.struts.action.Action; public class ItemAction extends DispatchAction { //添加帖子类别 public ActionForward addItem(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { //得到有关的 actionform 对象 Itemform itemform=(Itemform)form; //声明有关帖子类别的 pojo 对象 Item item=new Item(); item.setSubname(itemform.getSubname()); item.setSubcode(itemform.getSubcode()); //声明业务逻辑对象 ItemService itemSerivce=new ItemService(); itemSerivce.insertItem(item); return mapping.findForward("updatesuccess"); } public ActionForward deleteItem(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { String itemid=request.getParameter("id"); //声明有关帖子类别的 pojo 对象 Item item=new Item(); item.setSubid(Integer.parseInt(itemid)); //声明业务逻辑对象 ItemService itemSerivce=new ItemService(); itemSerivce.deleteItem(item); return mapping.findForward("deletesuccess"); } public ActionForward updateItem(ActionMapping mapping, ActionForm form, Java 框架 - 48 - 翰子昂系列理论教材 HttpServletRequest request, HttpServletResponse response) { //得到有关的 actionform 对象 Itemform itemform=(Itemform)form; //声明有关帖子类别的 pojo 对象 Item item=new Item(); item.setSubid(itemform.getSubid()); item.setSubcode(itemform.getSubcode()); item.setSubname(itemform.getSubname()); //声明业务逻辑对象 ItemService itemSerivce=new ItemService(); itemSerivce.updateItem(item); return mapping.findForward("updatesuccess"); } public ActionForward beforeUpdateItem(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { //声明有关帖子类别的 pojo 对象 Item item=new Item(); item.setSubid(Integer.parseInt(request.getParameter("id"))); item.setSubcode(Integer.parseInt(request.getParameter("code"))); item.setSubname(request.getParameter("name")); request.setAttribute("item",item); return mapping.findForward("update"); } public ActionForward getItem(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { //声明业务逻辑对象 ItemService itemSerivce=new ItemService(); java.util.List list=itemSerivce.getItem(); request.setAttribute("list",list); return mapping.findForward("success"); } } 10. 打开 WEB-INF 当中的,struts-config.xml 文件,在其中键入: 11. 部署运行程序,在地址栏里输入 http://localhost:8080/mybbs/ItemAction.do?method=ge tItem 打开查询页面,点击红色连接,为论坛添加版块。 图 1-19 查询论坛版块信息 12. 输入版块名称和编码后,点击保存。 图 1-20 添加论坛版块 13. 如果要修改刚才填写的信息,点击红色“修改”链接。 Java 框架 - 50 - 翰子昂系列理论教材 图 1-21 添加论坛版块 14. 填写要修改的信息。 图 1-22 修改论坛版块 15. 点击保存。 图 1-23 修改论坛版块 16. 要删除论坛版块点击红色“删除”链接便可。 图 1-24 删除论坛版块 4. 相关理论知识 4.1 Struts 当中的 DispatchAction 通常,在一个 Action 中只能完成一种业务操作,如果希望在同一个 Action 类中完成一组 相关的业务操作,可以使用 DispatchAction 类。比如对一个用户对象来说,存在增加、删除、 修改的操作,一种设计方案是为每种业务操作创建独立的 Action 类,如 addUserAction,del 专题一 Struts 翰子昂系列理论教材 - 51 - UserAction,updateUserAction。尽管这种设计方案是可行的,但是这三个 Action 在执行各自 的任务中,可能会执行一些相同的操作,比如 addUserAction 和 updateUserAction 都要进行相 同的数据验证。 为了减少重复编程,使应用更加便于维护,可以由同一个 Action 类来完成一组相关的业 务操作,DispatchAction 就提供了这种功能。 DispatchAction 是一个抽象类,它继承自 org.apache.struts.action.Action,如果要创建一个 扩展 DispatchAction 类的子类,不必覆盖 execute()方法,而是创建一些实现实际业务操作的 方法,这些业务方法都应该和 execute()方法具有同样的签名,即他们的参数和返回类型都应 该相同,此外也应该申明抛出 Execption,如下例是一个扩展 DispatchAction 的例子。 import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.actions.DispatchAction; public class UserAction extends DispatchAction { public ActionForward addUser (ActionMapping mapping,ActionForm form, HttpServletRequest request,HttpServletResponse response) throws Exception { //增加用户业务的逻辑 return mapping.findForward(“addsuccess”); } public ActionForward delUser(ActionMapping mapping,ActionForm form, HttpServletRequest request,HttpServletResponse response) throws Exception { //删除用户业务的逻辑 return mapping.findForward(“delsuccess”); } public ActionForward updateUser(ActionMapping mapping,ActionForm form, HttpServletRequest request,HttpServletResponse response) throws Exception { //更新用户业务的逻辑 return mapping.findForward(“updatesuccess”); } } Java 框架 - 52 - 翰子昂系列理论教材 如何实现这些不同方法的调用呢?那就是要在 struts-config.xml 文件中更改 action-mappi ng 的配置,如下: < action-mappings > < action attribute = "addUserForm" input = "/addUser.jsp" name = "addUserForm" parameter="method" path = "/addUser" scope = "request" type="com.mybbs.action.UserAction" > < action attribute = "delUserForm" input = "/delUser.jsp" name = "delUserForm" parameter="method" path = "/delUser" scope = "request" type=" com.mybbs.action.UserAction" /> < action attribute = "updateUserForm" input = "/updateUser.jsp" name = "updateUserForm" parameter="method" path = "/updateUser" scope = "request" type=" com.mybbs.action.UserAction" /> 可以看到每个中都增加了 parameter=“”项,这个值可以随便命名,如上面命 名为 method ,当用户请求访问 DispatchAction 时,应该提供 method 请求参数, http://localhost:8080/mybbs/addUser.do?method= addUser。 以上 method 请求参数值为“addUser”,它指定了需要调用的业务方法,因此 DispatchA ction 将调用相应的 addUser()。 4.2 Struts 当中其他控制器组件 4.2.1 org.apache.struts.actions.ForwardAction 类 在 JSP 网页中,尽管可以直接通过标签把请求转发给其他 Web 组件,但是 Struts 框架提倡先把请求转发给控制器,再由控制器来负责请求转发。 专题一 Struts 翰子昂系列理论教材 - 53 - 图 1-25 网页直接把请求发送到 Web 页面 图 1-26 Jsp 网页经过控制器把请求转发给其他 Web 组件 用控制器来负责请求转发有以下一些优点: 控制器具有预处理请求功能,能够选择正确的子模块来处理请求,并且把子模块的 Mod uleConfig 和 MessageResources 对象存放在 request 范围内。这样,请求转发的目标 Web 组件 就可以正常地访问 ModuleConfig 和 MessageResources 对象,其中 ModuleConfig 和 MessageR esources 为 Struts 内部组件,用于存放配置信息和资源信息。 如果 JSP 页面中包含 HTML 表单,那么控制器能够创建和这个表单对应的 ActionForm 对 象,把用户输入表单数据组装到 ActionForm 中。如果元素的 validate 属性为 true,那 么还会调用 ActionForm 的表单验证方法。控制器把 ActionForm 对象存放在 request 或 session 范围内,这样请求转发的目标 Web 组件也可以访问 ActionForm。 JSP 网页之间直接相互转发违背了 MVC 的分层原则,按照 MVC 设计思想,控制器负责 处理所有请求,然后选择适当的视图组件返回给用户,如果直接让 JSP 相互调用,控制器就 失去了流程控制作用。 对于用户自定义的 Action 类,既可以负责请求转发,还可以充当客户端的业务代理,如 果仅仅需要 Action 类提供请求转发功能,则可以使用 org.apache.struts.actions.ForwardAction 类。ForwardAction 类专门用于转发请求,不执行任何其他业务操作。 如下例我们可以通过“login.do”进入 login.jsp 页面: ActionServlet 把请求转发给 ForwardAction,ForwardAction 再把请求转发给元素 中 parameter 属性指定的 Web 组件。总之,在 Web 组件之间通过 ForwardAction 类来进行请求 转发,可以充分利用 Struts 控制器的预处理请求功能。此外,也可以通过元素的 forward 属性来实现请求转发。 此外,也可以通过元素的 forward 属性来实现请求转发,以下代码完成同样的功 能: Java 框架 - 54 - 翰子昂系列理论教材 4.2.2 org.apache.struts.actions.IncludeAction 类 JSP 网页中,尽管可以直接通过指令包含另一个 Web 组件,但是 Struts 框架提 倡先把请求转发给控制器,再由控制器来负责包含其他 Web 组件。IncludeAction 类提供了包 含其他 Web 组件的功能。与 ForwardAction 一样,Web 组件通过 IncludeAction 类来包含另一 个 Web 组件,可以充分利用 Struts 控制器的预处理功能。 的 paramter 属性指定需要包含的 Web 组件。此外,也可以通过元素的 i nclude 属性来包含 Web 组件。 4.2.3 org.apache.struts.actions.LookupDispatchAction 类 LookupDispatchAction 类是 DispatchAction 的子类,在 LookupDispatchAction 类中也可以 定义多个业务方法。通常 LookupDispatchAction 主要应用于在一个表单中有多个提交按钮, 而这些按钮又都有一个共同的名字,这些按钮的名字和具体的 ActionMapping 的 parameter 属 性值相对应。 直接以实例来说明,在继承 LookupDispatchAction 之后,您要重新定义 getKeyMethodMa p()方法,并定义好自己的相关处理方法,例如: import javax.servlet.http.*; import org.apache.struts.action.*; import org.apache.struts.actions.*; public class EditAction extends LookupDispatchAction { protected Map getKeyMethodMap() { Map map = new HashMap(); map.put("button.save", "save"); map.put("button.preview", "preview"); map.put("button.reset", "reset"); return map; } public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // ...... } 专题一 Struts 翰子昂系列理论教材 - 55 - public ActionForward preview(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // ...... } public ActionForward reset(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // ...... } } 在资源文件中包括以下的信息: button.save=Save button.preview=Preview button.reset=Reset 为了要使用 LookupDispatchAction,在 struts-config.xml 中定义请求参数中该有的名称: 现在假设您的表单页面包括以下的内容:
.....
当您按下任一个按钮时,请求参数中会包括 method=Save 或是 method=Preview 或是 method= Reset,假设是 method=Save,LookupDispatchAction 会根据它作为 value,在资源文 件当中找到对应的 key,然后根据 key 与 getKeyMethodMap()得知要执行的方法为 save()方法。 Java 框架 - 56 - 翰子昂系列理论教材 4.2.4 org.apache.struts.actions.SwitchAction 类 SwitchAction 类用于子应用模块之间的切换。如果使用 SwitchAction 类来进行子应用模 块之间的切换,只需在 Struts 配置文件中做如下配置: 对于请求访问 SwitchAction 的 URL,需要提供两个参数: prefix,指定子应用模块的前缀,以“/”开头,默认子应用模块的前缀为空字符串“” page,指定被请求 Web 组件的 URI,只需指定相对于被切换后的子模块的相对路径。 例如,如果要从默认模块切换到 ModuleB,并将请求转发給 ModuleB 的“/index.do”, 可以采用与以下类似的 URL: http://localhost:8080/action/toModule?prefix=/moduleB&page=/index.do 4.3 Struts 当中的多模块编程 在 Struts 框架中,还允许使用多个 struts-config.xml 文件,一个命名为“struts-config.xml”, 一个命名为“struts-config-dao.xml”,配置时就应该用“,”他们隔开: config /WEB-INF/struts-config.xml, /WEB-INF/struts-config-dao.xml 这样两个配置文件的信息都会被 Web 应用环境所识别而一起工作。不过这样的配置有一 个问题,因为每个都是全局的,若有相同路径的就会发生冲突。 为了解决这个问题,Struts 框架引入了模块的概念。将每个配置文件都视为一个模块就可 以了,在 web.xml 中配置如下: config /WEB-INF/struts-config.xml config/dao /WEB-INF/struts-config-dao.xml “config/xxx”中“/xxx”就是所指的模块,每个模块都通过进行配置。在这 里指定了一个缺省模块“config”,一个普通模块“config/dao”,这样就可以根据模块名转发 到各个模块,进行各自不同的工作。 而在各个模块间的转发则是通过元素和 ContextRelative 的属性来实现。 注意元素部分的配置。 contextRelative 用来指定是否允许在模块间转发。 path 属性指定模块名,说明配置在 struts-config-dao.xml 中的 LoginAction.do,在原来的 p ath 前加上模块名,就可以在不同的模块前进行转发。对于缺省模块,不带模块名的 path 属性 就可以了。 5. 试验 按照上课的顺序依次练习,主要掌握: 1. 在 mybbs 数据库中建立 item 表并建立 MyBBS 工程。(10 分钟) 2. 编写 leibie.jsp,addleibie.jsp,updateleibie.jsp 页面。(30 分钟) 3. 编写用于数据访问的 JdbcDao.java,ItemDao.java 类。(10 分钟) 4. 编写业务逻辑类 ItemService。(10 分钟) 5. 编写 ItemAction 和 Itemform 类。(20 分钟) 6. 配置 struts-config.xml 文件,并调试运行程序。(10 分钟) 6. 作业 利用 DispatchAction 实现论坛帖子的增删查改。 Java 框架 - 58 - 翰子昂系列理论教材 案例三 Struts 当中使用数据连接池 1. 教学目标 1.1 Struts 数据库连接池的配置 1.2 Tomcat 数据库连接池配置 1.3 两种连接池的性能简要比较 1.4 Struts 国际化 2. 工作任务 2.1 使用 Struts 数据库连接池实现论坛登录示例 2.2 使用 Tomcat 数据库连接池实现库论坛登录示例 3. 相关实践知识 3.1 本章相关实例介绍 本章我们用 Struts 和 Tomcat 的数据库连接池分别去实现第一章所作的论坛登录示例,本 例依然采用数据库当中的 userinfo 表,和第一章用到过的 login.jsp,image.jsp,error.jsp,suc cess.jsp 四个页面,由于前面已经给出创建脚本和代码,本章不再给出,数据库连接池服务采 用 APACHE 提供的 DBCP,此服务涉及到三个工具包,commons-pool-1.2.jar,commons-coll ections-3.1.jar,commons-dbcp-1.2.1.jar。 1. 打开 MyEclipse6.0,导入案例一的工程。 图 1-27 添加了 Struts 的 Web 工程 专题一 Struts 翰子昂系列理论教材 - 59 - 2. 将 DBCP 服务所需的三个工具包,和 MySql 数据库驱动加入工程。 图 1-28 添加了驱动程序 3.2 使用 Sturts 数据库连接池实现论坛登录实例 1. 修改 com.mybbs.dao 中的 UserInfoDao 代码,修改成如下代码: package com.mybbs.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.List; import java.util.ArrayList; import javax.sql.DataSource; import javax.servlet.http.HttpServletRequest; import com.mybbs.pojo.Userinfo; public class UserinfoDao extends JdbcDao { //定义自身的一个变量 private static UserinfoDao userinfoDao = null; //定义自身私有的构造函数 private UserinfoDao(DataSource dataSource) { super(dataSource); try { conn = super.dataSource.getConnection(); } catch (Exception e) { e.printStackTrace(); } } Java 框架 - 60 - 翰子昂系列理论教材 //通过一个简单的单利模式将UserinfoDao返回到客户端 public synchronized static UserinfoDao getUserinfoDao(DataSource dataSource) { if (userinfoDao != null) { return userinfoDao; } userinfoDao = new UserinfoDao(dataSource); return userinfoDao; } //得到用户信息,以list的形式返回到客户端 public List getPerson(Userinfo user) { //声明用以存放用户信息的集合 List list = new ArrayList(); //得到数据库连接 super.getConnection(); //定义要执行的sql语句 String sqlString = "select * from userinfo where username=? and userpwd=?"; try { //预编译sql返回一个PrePareStatement对象 pstmt = conn.prepareStatement(sqlString); //传入参数 pstmt.setString(1, user.getUsername()); pstmt.setString(2, user.getUserpwd()); //执行sql并返回结果集 rs = pstmt.executeQuery(); //循环遍历结果集 if (rs.next()) { Userinfo myuser = new Userinfo(); myuser.setUsername(rs.getString(2)); myuser.setUserpwd(rs.getString(3)); list.add(myuser); } } catch (Exception ex) { ex.printStackTrace(); } finally { super.closeConnction(); } return list; } } 专题一 Struts 翰子昂系列理论教材 - 61 - 2.修改 com.mybbs.service 包下的 UserinfoService 类,键入如下代码: package com.mybbs.service; import com.mybbs.pojo.Userinfo; import com.mybbs.dao.UserinfoDao; import java.util.List; import java.util.Iterator; import javax.sql.DataSource; public class UserinfoService { private DataSource dataSource=null; //得到操作用户信息的 DAO private UserinfoDao userinfoDao=null; //验证用户登录信息,用户名密码正确返回 true public boolean userLogin(Userinfo user) { boolean isLogin=false; //根据传入的 user 对象从数据库中查询结果集 List list=userinfoDao.getPerson(user); Iterator it=list.iterator(); //如果集合中有数据说明用户可以登录 if(it.hasNext()) { isLogin=true; } return isLogin; } public UserinfoService(DataSource dataSource) { this.dataSource=dataSource; userinfoDao=UserinfoDao.getUserinfoDao(dataSource); } } 3. 修改 com.mybbs.action 包下的 LoginAction 类,键入如下代码: package com.mybbs.action; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import com.mybbs.actionform.LoginActionform; Java 框架 - 62 - 翰子昂系列理论教材 import com.mybbs.pojo.Userinfo; import com.mybbs.service.UserinfoService; import javax.servlet.http.HttpSession; import javax.sql.DataSource; import javax.naming.Context; import javax.naming.InitialContext; public class LoginAction extends Action { String scriptStr=null; DataSource dataSource=null; public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { //得到 LoginActionForm 对象 LoginActionform LoginActionForm = (LoginActionform) form; //得到 session 对象 HttpSession session=request.getSession(); //判断验证码是否输入正确 if(!session.getAttribute("rand").toString().equals(LoginActionForm.getR and())) { scriptStr=""; request.setAttribute("scriptStr", scriptStr); //页面跳转 return mapping.findForward("self"); } //实例化用户 bean Userinfo user=new Userinfo(); //得到用户输入的用户名 user.setUsername(LoginActionForm.getName()); //得到用户输入密码 user.setUserpwd(LoginActionForm.getPassword()); //得到 struts 数据源 dataSource=this.getDataSource(request); //声明业务逻辑对象 UserinfoService userinfoService=new UserinfoService(dataSource); //如果用户名和密码输入正确登录成功 if(userinfoService.userLogin(user)) { return mapping.findForward("success"); 专题一 Struts 翰子昂系列理论教材 - 63 - } //如果输入错误提示错误 else { return mapping.findForward("error"); } } } 4. 打开 WEB-INF 当中的,struts-config.xml 文件,在其中键入如下代码: Java 框架 - 64 - 翰子昂系列理论教材 3.3 使用 tomcate 数据库连接池实现论坛登录实例 1. 修改 com.mybbs.action 包下的 LoginAction 类,键入如下代码: package com.mybbs.action; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import com.mybbs.actionform.LoginActionform; import com.mybbs.pojo.Userinfo; import com.mybbs.service.UserinfoService; import javax.servlet.http.HttpSession; import javax.sql.DataSource; import javax.naming.Context; import javax.naming.InitialContext; public class LoginAction extends Action { String scriptStr=null; DataSource dataSource=null; public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { //得到 LoginActionForm 对象 LoginActionform LoginActionForm = (LoginActionform) form; //得到 session 对象 HttpSession session=request.getSession(); //判断验证码是否输入正确 if(!session.getAttribute("rand").toString().equals(LoginActionForm.get Rand())) { scriptStr=""; request.setAttribute("scriptStr", scriptStr); 专题一 Struts 翰子昂系列理论教材 - 65 - //页面跳转 return mapping.findForward("self"); } //实例化用户 bean Userinfo user=new Userinfo(); //得到用户输入的用户名 user.setUsername(LoginActionForm.getName()); //得到用户输入密码 user.setUserpwd(LoginActionForm.getPassword()); //得到 tomcat 数据源 try { Context ctx = new InitialContext(); dataSource = (DataSource) ctx.lookup("java:comp/env/jdbc/TestDB"); } catch (Exception e) { e.printStackTrace(); } //声明业务逻辑对象 UserinfoService userinfoService=new UserinfoService(dataSource); //如果用户名和密码输入正确登录成功 if(userinfoService.userLogin(user)) { return mapping.findForward("success"); } //如果输入错误提示错误 else { return mapping.findForward("error"); } } } 2. 打开 WEB-INF 当中的 struts-config.xml 文件,在其中键入如下代码: 3. 在 tomcat6.0 主目录的 conf 文件夹下新建 context.xml 文件,在其中键入如下代码: WEB-INF/web.xml 8. 在地址栏里输入 http://localhost:8080/mybbs/jsp/login.jsp 打开登录页面。 图 1-29 登录页面 9. 输入正确的用户名,密码和验证码后点击提交,提示登录成功。 专题一 Struts 翰子昂系列理论教材 - 67 - 图 1-30 提示登录成功 10. 验证码、用户名或密码输入错误将在登录页面提示用户。 图 1-31 提示用户名密码错误 Java 框架 - 68 - 翰子昂系列理论教材 4. 相关理论知识 4.1 数据源介 传统的数据库连接方式(指通过 DriverManager 和基本实现 DataSource 进行连接)中, 一个数据库连接对象均对应一个物理数据库连接,数据库连接的建立以及关闭对系统而言是 耗费系统资源的操作,在多层结构的应用程序环境中这种耗费资源的动作对系统的性能影响 尤为明显。 在多层结构的应用程序中通过连接池(Connection Pooling)技术可以使系统的性能明显 得到提高,连接池意味着当应用程序需要调用一个数据库连接的时,数据库相关的接口通过 返回一个通过重用数据库连接来代替重新创建一个数据库连接。通过这种方式,应用程序可 以减少对数据库连接操作,尤其在多层环境中多个客户端可以通过共享少量的物理数据库连 接来满足系统需求。通过连接池技术 Java 应用程序不仅可以提高系统性能同时也为系统提供 了可测量性。 数据库连接池是运行在后台的而且应用程序的编码没有任何的影响。此中状况存在的前 提是应用程序必须通过 DataSource 对象(一个实现 javax.sql.DataSource 接口的实例)的方式 代替原有通过 DriverManager 类来获得数据库连接的方式。一个实现 javax.sql.DataSource 接口 的类可以支持或不支持数据库连接池,但是两者获得数据库连接的代码基本是相同的。 JDBC2.0 提供了 javax.sql.DataSource 的接口,负责与数据库建立连接,实际应用时不需 要编写连接数据库代码,直接从数据源获得数据库的连接。DataSource 中事先建立了多个数 据库连接,这些数据库连接保持在数据库连接池中,当程序访问数据库时,只需要从连接池 读取出空闲的连接,访问数据库结束,在将这些连接归还给连接池。DataSource 对象由容器 提供,不能使用创建实例的方法来生成 DataSource 对象,要采用 Java 的 JNDI(Java Namein g and Directory Interface,Java 命名和目录接口)来获得 DataSource 对象的引用。JNDI 是一 种将对象和名字绑定的技术,对象工厂负责生产出对象,这些对象都和唯一的名字相绑定。 程序中可以通过这个名字来获得对象的引用。容器把 DataSource 作为一种可配置的 JNDI 资 源来处理,生成 DataSource 对象的工厂为 org.apache.commons.dbcp.BasicDataSourceFactory。 专题一 Struts 翰子昂系列理论教材 - 69 - 图 1-32 数据库连接池 4.2 Struts 配置数据源 Struts DataSource 管理器在 Struts 配置文件(struts-config.xml)里定义。这个管理器可以 用来分发和配置任何实现了 javax.sql.DataSource 接口的数据库连接池(connection pool)。如 果你的 DBMS 或者容器内置了符合这些要求的连接池,你可以优先选用它。 下面是一段 Struts-config.xml 配置文件中的数据源配置,你可以更改相应的设置以适合你 自己的系统。 该标签指定 JDBC 资源所需的参数。 属性名 说明 driverClassName JDBC 驱动类的完整名称。通常驱动类的包放在 WEB-INF/lib 下 url 连到 JDBC 的 URL,不同的数据库不同 username 登录数据库的用户名 password 登录数据库的密码 maxActive 最大激活连接数 maxIdle 最大等待连接中的数量 maxWait 最长等待时间,代为 ms autoReconnect 当数据源断开的时候是否自动重新连接 max-connections 最大连接数 Java 框架 - 70 - 翰子昂系列理论教材 min-connections 最小连接数 inactivity-timeout 数据库连接超时 wait-timeout 数据库连接闲置最大时间值 eroDataTimeBehavior 指定数据库中的 DateTime 字段默认值查询时的处理方式 配置好 DataSource 以后,你就可以在你的应用系统中使用这些数据源了。下面这段代码 演示了怎样在 Action 类的 execute 方法中通过这些数据源来生成数据库连接。 public ActionForward execute( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { DataSource dataSource; Connection cnn; try { dataSource = getDataSource(request); cnn = dataSource.getConnection(); //数据连接已经建立了,你可以做你想做的事情了 } catch (SQLException e) { getServlet().log("处理数据库连接", e); } finally { //在 finally 块里包含这些代码 //用以保证连接最后会被关闭 try { cnn.close(); } catch (SQLException e) { getServlet().log("关闭数据库连接", e); } } } 专题一 Struts 翰子昂系列理论教材 - 71 - 如果你需要在模块中使用多于一个的数据源,你可以在配置文件的元素里 包含一个 key 属性。 你在代码里,可以通过这些 key 获得不同的数据源。代码如下: dataSourceA = getDataSource(request, "A"); dataSourceB = getDataSource(request, "B"); 4.3 Tomcat6.0 配置数据源 Tomcat 的灵活配置,允许你可以通过多种方式配置连接池。先定义 Tomcat6 的安装根目 录为${tomcat6},以方便后面的描述。 Tomcat6 的服务器配置文件放在${tomcat6}/conf 目录底下。我们可以在这里找到 server.x ml 和 context.xml。当然,还有其他一些资源文件。但我们只用得上这两个,其他的就不介绍 了。 首先,需要为数据源配置一个 JNDI 资源,在 Tomcat6 版本中,Context 元素已经从 serv er.xml 文件中独立出来了,放在一个 context.xml 文件中。因为 server.xml 是不可动态重加载 的资源,服务器一旦启动了以后,要修改这个文件,就得重启服务器才能重新加载。而 cont ext.xml 文件则不然,Tomcat 服务器会定时去扫描这个文件。一旦发现文件被修改,就会自动 重新加载这个文件,而不需要重启服务器。 由于 Context 元素的可用范围是可以控制的,我们可以根据需要为 Context 元素定义不同 级别的可用范围。 4.3.1 全局可用 全局可用的范围意味着 Tomcat 服务器下面的所有应用都可以使用这个 Context 元素定义 的资源。全局可用范围的 Context 元素在文件 ${tomcat6}/conf/context.xml 文件中描述。这个 文件在 Tomcat 刚刚被安装的时候,是没有定义任何资源的。我们可以看到,这个文件的内容: WEB-INF/web.xml 其中的 WEB-INF/web.xml 表示服务器会监视应用的 WEB-INF/web.xml 文件来知道那个 应用会引用在此处定义的资源。 4.3.2 指定的虚拟主机可用 指定的虚拟主机内可用就是说,在 Tomcat 服务器配置的虚拟主机中,只有指定的那个虚 Java 框架 - 72 - 翰子昂系列理论教材 拟主机上跑的应用才能使用。什么是虚拟主机和如何配置虚拟主机在这里就不描述了,有兴 趣的同学自己去查 Tomcat 的官方资料。 要配置一个虚拟主机可用的 Context 资源,可以在${tomcat6}/conf/目录下的文件 ${engi nename}/${hostname}/context.xml.default 中表述。 比如,一般一个 Tomcat 服务器安装好了以后,都有一个默认的叫做 Catalina 的引擎, 在这个引擎下有一个叫做 localhost 的虚拟主机。我们的应用一般都放在这个虚拟主机下。那 么,如果我们想要配置一个在 Catalina/localhost 虚拟主机下都可以使用的资源,我们需要在 目录 ${tomcat6}/conf 下建立路径 Catalina/localhost,在这个路径下创建文件 context.xml.def ault。全路径是 ${tomcat6}/conf/Catalina/localhost/context.xml.default。 4.3.3 指定的应用可用 一个指定的应用可用的 Context 元素,意味着这是一个只有指定的引擎,指定的虚拟主机, 指定的应用才可以使用的 Context 元素。 如果我们用 appname 来代表这个指定的应用的名字,那么元素的定义应该被放置在 ${to mcat}/conf/${enginename}/${hostname}/${appname}.xml 文件中。 例如,假设在 localhost 下我们有一个 Web 应用叫做 MyBBS,那么我们应该创建文件 $ {tomcat6}/conf/Catalina/localhost/MyBBS.xml。 下面是一段 context.xml 配置文件中的数据源配置,你可以更改相应的设置以适合你自己 的系统,我们将其配置为全局可用的资源。 WEB-INF/web.xml Resource 标签指定了 JDBC 资源所需的参数。 属性名 说明 name 数据源 JNDI 名称 auth 指定管理 Resource 的 Manager。Container 表示容器内资源。 type 指定 Resource 所属的 Java 类名 driverClassName JDBC 驱动类的完整名称。通常我驱动类的包放在 WEB-INF/lib 下 url 连到 JDBC 的 URL,不同的数据库不同 username 登录数据库的用户名 password 登录数据库的密码 maxActive 最大激活连接数 专题一 Struts 翰子昂系列理论教材 - 73 - maxIdle 最大等待连接中的数量 maxWait 最长等待时间,代为 ms 配置好 Resource 以后,你就可以在你的应用系统中使用这些数据源了。下面这段代码演 示了怎样在你的应用中生成数据库连接。 try { //JDBC 连接 Context ctx = new InitialContext(); DataSource ds =(DataSource)ctx.lookup("java:comp/env/jdbc/TestDB"); this.conn=ds.getConnection(); } catch(SQLException ex) { ex.printStackTrace(); } catch(NamingException ex) { ex.printStackTrace(); } 我们可以在一个 context 元素中定义多个资源。 WEB-INF/web.xmlWatchedResource> Java 框架 - 74 - 翰子昂系列理论教材 Context> 你代码里,可以通过这些 name 获得不同的数据源。代码如下: DataSource ds =(DataSource)ctx.lookup("java:comp/env/jdbc/TestDB"); DataSource ds =(DataSource)ctx.lookup("java:comp/env/jdbc/OtherTestDB "); 4.4 Struts 和 I18N 人们常用 I18N 作为“国际化”的简称,其来源是英文单词 internationalization 的首字符 i 和 n 之间的字符数为 18。随着全球经济的一体化成为一种主流趋势,软件开发者应该支持多 国语言的国际化的 Web 应用。对于 Web 应用来说,用样的页面在不同的语言环境下需要显 示不同的效果。也就是说,一个 Web 应用程序在运行时能根据用户客户端请求来自的国家和 语言的不同显示不同的用户界面。这样,当需要在应用程序添加对一种新的语言开发环境的 支持时,不需要对已有的软件返工,无需修改应用程序的程序代码。 在 Struts 框架中进行程序的国际化,支持重点在于应用程序的文本和图片表示。最重要 的工作就是准备 Resource Bundle 资源包。事实上,准备资源包的过程,就是把对应不同语言 环境的用户涉及的文本和图片保存在多个配置文件中,客户端根据不同的环境需要进行更换。 这些配置文件被称为“属性文件”,所有属性文件和在一起被成为资源包(Resource Bundle)。 我们要开发一个国际化的应用程序,使它同时支持中文和英文。 当建立完工程,并添加了 Struts 支持以后,我们编辑 ApplicationResources.properties 文件, 选择文件代码如下: Welcome=Welcome!! 创建英文资源文件 ApplicationResources_en.properties。 Welcome=Welcome!! 创建临时中文文件 ApplicationResources_temp.properties。 Welcom=欢迎您!!!! 对临时中文文件进行编码转换。在 DOS 下执行命令: 专题一 Struts 翰子昂系列理论教材 - 75 - native2ascii –encoding gb2312 ApplicationResources_temp.properties ApplicationResources_zh_CN.properties 在 JSP 文件当中我们使用读取信息。 <%@ page language="java" pageEncoding="ISO-8859-1"%> <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean"%> <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html"%> JSP for LoginActionForm form 设置 IE 浏览器的语言选项。依次选择【工具】Î【Internet 选项】Î【常规】Î【语言】, 打开 JSP 页面显示中文“欢迎您!!!!”。 图 1-33 IE 浏览器的语言环境 设置 IE 浏览器的语言选项。依次选择【工具】Î【Internet 选项】Î【常规】Î【语言】 Î【添加】,打开 JSP 页面显示英文:Welcome!!。 Java 框架 - 76 - 翰子昂系列理论教材 图 1-34 设置 IE 浏览器的语言环境为英文 图 1-35 设置 IE 浏览器的语言环境为英文 资源包是一种文件,他包含应用程序默认语言的健/值对。此文件的命名格式为 ResourceBunleName_language_COUNTRY.properties。例如: 英文(美国) ApplicationResources _en_US.properties 专题一 Struts 翰子昂系列理论教材 - 77 - 中文(简体)ApplicationResources _zh_CN.properties 中文(繁体)ApplicationResources _tw.properties 通过使用元素可以在 Struts 配置文件定义多个资源包。而每个附加资 源包可以使用 key 属性指定包的名称。 5. 试验 按照上课的顺序依次练习,主要掌握: 1. 建立 mybbs 数据库及 userinfo 表和 MyBBS 工程。(5 分钟) 2. 在 Struts 或 Tomcat 当中配置数据库连接池。(15 分钟) 3. 编写 login.jsp,success.jsp,error.jsp 页面。(10 分钟) 4. 编写用以生成验证码的 image.jsp。(15 分钟) 5. 编写用于数据访问的 JdbcDao.java,UserinfoDao.java 类。(10 分钟) 6. 编写业务逻辑类 UserinfoService。(10 分钟) 7. 编写 LoginAction 和 LoginActionForm 类。(20 分钟) 8. 配置 struts-config.xml 文件,并调试运行程序。(10 分钟) 6. 作业 利用 Struts 框架的国际化功能,修改 login.jsp 页面,完成国际化。提示: 1.修改 login.jsp 如果所示: <%@ page language="java" pageEncoding="Gb2312"%> <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean"%> MyBBS登录页面 <% if (request.getAttribute("scriptStr") != null) { Java 框架 - 78 - 翰子昂系列理论教材 %> <%=request.getAttribute("scriptStr")%> <% request.setAttribute("scriptStr", null); } %>


专题一 Struts 翰子昂系列理论教材 - 79 -
:
:
:
      Java 框架 - 80 - 翰子昂系列理论教材      
2.修改并新建工程的资源文件,ApplicationResources_zh_CN.properties 代码如下: login=\u8bba\u8c08\u767b\u5f55 name=\u7528\u6237\u540d pass=\u5bc6\u7801 code=\u9a8c\u8bc1\u7801 submit=\u63d0\u4ea4 reset=\u91cd\u7f6e ApplicationResources_en_US.properties 代码如下: login=bbs login name=user name pass=password code=code submit=submit reset=reset 3.以不同的 IE 语言环境打开登录页面。 专题一 Struts 翰子昂系列理论教材 - 81 - 案例四 Struts 标签和 Validator 框架 1. 教学目标 1.1 Struts 常用标签的使用 1.2 Validator 验证框架 2. 工作任务 4.1 使用 Validator 验证框架实现论坛注册 3. 相关实践知识 本案例,涉及到 mybbs 数据库当中的 userinfo 表,表结构如下: 字段名称 类型 大小 是否为空 备注 userid int 4 否 主键,自增,代表人员编号 username varchar 20 否 代表人员姓名 userpwd varchar 20 否 代表密码 userques varchar 50 否 代表密码找回问题 userans varchar 50 否 代表密码找回答案 integral int 4 否 代表用户名 grade varchar 20 否 代表用户等级 useremail varchar 20 否 代表邮箱 sex bit 1 否 代表性别 表 4-1 userinfo 表结构 1.新建 userinfo 表,其创建脚本如下: Create database mybbs; create table `mybbs`.`userinfo`( `userid` int not null auto_increment, `username` varchar(20) default '' not null, `userpwd` varchar(20) default '' not null, `userques` varchar(50) default '' not null, `userans` varchar(50) default '' not null, `integral` int not null, `grade` varchar(20) default '' not null, `useremail` varchar(20) default '' not null, `sex` bit, primary key (`userid`) )TYPE=INNODB, Java 框架 - 82 - 翰子昂系列理论教材 default character set gbk; 2. 打开 MyEclipse6.0,建立 MyBBS 工程,并添加 Struts 支持。 3. 新建 com.mybbs.pojo 包,在包下建立 Userinfo 类: public class Userinfo implements java.io.Serializable{ //用户编号 private int userid; //用户名 private String username; //密码 private String userpwd; //密码找回问题 private String userques; //密码找回答案 private String userans; //用户积分 private int ntegral; //用户等级 private String grade; //电子邮箱 private String useremail; //性别 private byte sex; public int getUserid() { return userid; } public void setUserid(int userid) { this.userid = userid; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUserpwd() { return userpwd; } public void setUserpwd(String userpwd) { this.userpwd = userpwd; } public String getUserques() { 专题一 Struts 翰子昂系列理论教材 - 83 - return userques; } public void setUserques(String userques) { this.userques = userques; } public String getUserans() { return userans; } public void setUserans(String userans) { this.userans = userans; } public int getNtegral() { return ntegral; } public void setNtegral(int ntegral) { this.ntegral = ntegral; } public String getGrade() { return grade; } public void setGrade(String grade) { this.grade = grade; } public String getUseremail() { return useremail; } public void setUseremail(String useremail) { this.useremail = useremail; } public byte getSex() { return sex; } public void setSex(byte sex) { this.sex = sex; } } 4. 新建 com.mybbs.dao 包,在包下建立 JdbcDao 类和 UserinfoDao 类,用于访问数据库: import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; Java 框架 - 84 - 翰子昂系列理论教材 import java.sql.SQLException; import java.sql.Statement; public class JdbcDao { //数据库连接对象 protected Connection conn=null; protected Statement stmt=null; protected PreparedStatement pstmt=null; protected ResultSet rs=null; //数据库驱动 private String driverClass_Name="com.mysql.jdbc.Driver"; //数据库链接 URL private String url="jdbc:mysql://localhost:3306/mybbs?user=root&password=sa"; public JdbcDao(){} //用于得到数据库连接 public void getConnection() { Connection conn=null; try { //加载驱动 Class.forName(driverClass_Name); //得到连接 conn=DriverManager.getConnection(url); this.conn=conn; } catch(SQLException ex) { ex.printStackTrace(); } catch(ClassNotFoundException ex) { ex.printStackTrace(); } } //释放数据库资源 public void closeConnction() { 专题一 Struts 翰子昂系列理论教材 - 85 - if(rs!=null) { try { rs.close(); } catch(SQLException ex) { ex.printStackTrace(); } } if(stmt!=null) { try { stmt.close(); } catch(SQLException ex) { ex.printStackTrace(); } } if(pstmt!=null) { try { pstmt.close(); } catch(SQLException ex) { ex.printStackTrace(); } } if(conn!=null) { try { conn.close(); } catch(SQLException ex) { ex.printStackTrace(); Java 框架 - 86 - 翰子昂系列理论教材 } } } } import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.List; import java.util.ArrayList; import com.mybbs.pojo.Userinfo; public class UserinfoDao extends JdbcDao { private static UserinfoDao userinfoDao = null; private UserinfoDao() { }; public synchronized static UserinfoDao getUserinfoDao() { if (userinfoDao != null) { return userinfoDao; } userinfoDao = new UserinfoDao(); return userinfoDao; } //得到用户的最大编号 public int getUserMaxId() { int maxid = 0; //得到数据库连接 super.getConnection(); //拼写 SQL 语句 String sqlString = "select max(userid) from userinfo "; try { pstmt = conn.prepareStatement(sqlString); rs = pstmt.executeQuery(); //返回最大编号值 if (rs.next()) { maxid = rs.getInt(1); } 专题一 Struts 翰子昂系列理论教材 - 87 - } catch (Exception ex) { ex.printStackTrace(); } finally { super.closeConnction(); } return maxid; } //插入用户信息 public void insertUserInfo(Userinfo user) { //得到数据库连接 super.getConnection(); //拼写 SQL 语句 String sqlString = "insert into mybbs.userinfo values (?,?,?,?,?,?,?,?,?)"; try { pstmt = conn.prepareStatement(sqlString); pstmt.setInt(1,user.getUserid() ); pstmt.setString(2, user.getUsername()); pstmt.setString(3, user.getUserpwd()); pstmt.setString(4, user.getUserques()); pstmt.setString(5, user.getUserans()); pstmt.setInt(6, user.getNtegral()); pstmt.setString(7, user.getGrade()); pstmt.setString(8, user.getUseremail()); pstmt.setByte(9, user.getSex()); //插入信息 pstmt.executeUpdate(); } catch (Exception ex) { ex.printStackTrace(); } finally { super.closeConnction(); } } } 5. 新建 com.mybbs.service 包,在包下建立 UserinfoService 类,用于插入用户信息: import com.mybbs.pojo.Userinfo; import com.mybbs.dao.UserinfoDao; import java.util.List; import java.util.Iterator; Java 框架 - 88 - 翰子昂系列理论教材 public class UserinfoService { private UserinfoDao userinfoDao=UserinfoDao.getUserinfoDao(); public void insertUserInfo(Userinfo user) { user.setUserid(userinfoDao.getUserMaxId()+1); userinfoDao.insertUserInfo(user); } } 6. 新建 com.mybbs.actionform 包,在包下建立 InserUserinfoActionForm 类: import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; import org.apache.struts.validator.ValidatorForm; public class InsertUserInfoActionForm extends ValidatorForm { //性别 private Byte sex=1; //电子邮箱 private String useremail; //密码找回问题 private String userques; //用户名 private String username; //密码找回答案 private String userans; //密码 private String userpwd; //确认密码 private String validateuserpwd; //等级 private String grade; //分数 private int integral; public Byte getSex() { return sex; } 专题一 Struts 翰子昂系列理论教材 - 89 - public void setSex(Byte sex) { this.sex = sex; } public String getUseremail() { return useremail; } public void setUseremail(String useremail) { this.useremail = useremail; } public String getUserques() { return userques; } public void setUserques(String userques) { this.userques = userques; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUserans() { return userans; } public void setUserans(String userans) { this.userans = userans; } public String getUserpwd() { return userpwd; } public void setUserpwd(String userpwd) { Java 框架 - 90 - 翰子昂系列理论教材 this.userpwd = userpwd; } public String getGrade() { return grade; } public void setGrade(String grade) { this.grade = grade; } public int getIntegral() { return integral; } public void setIntegral(int integral) { this.integral = integral; } public String getValidateuserpwd() { return validateuserpwd; } public void setValidateuserpwd(String validateuserpwd) { this.validateuserpwd = validateuserpwd; } } 7. 新建 com.mybbs.action 包,在包下建立 LoginAction 类: import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.actions.DispatchAction; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import com.mybbs.actionform.InsertUserInfoActionForm; import com.mybbs.pojo.Userinfo; import com.mybbs.service.UserinfoService; public class InsertUserInfoAction extends DispatchAction { 专题一 Struts 翰子昂系列理论教材 - 91 - public ActionForward add(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { InsertUserInfoActionForm InsertUserInfoActionForm = (InsertUserInfoActionForm) form; //将用户信息保存到 UserInfo 的实例当中 Userinfo user=new Userinfo(); user.setUsername(InsertUserInfoActionForm.getUsername()); user.setUserpwd(InsertUserInfoActionForm.getUserpwd()); user.setUseremail(InsertUserInfoActionForm.getUseremail()); user.setSex(InsertUserInfoActionForm.getSex()); user.setUserans(InsertUserInfoActionForm.getUserans()); user.setUserques(InsertUserInfoActionForm.getUserques()); user.setNtegral(0); user.setGrade("初级用户"); //利用 UserinfoService 类的实例将用户信息插入数据库 UserinfoService userivce=new UserinfoService(); userivce.insertUserInfo(user); return null; } } 8. 新建 com.mybbs.struts 包,在包下建立 ApplicationResources.properties: username=\u7528\u6237\u540d userpwd=\u5bc6\u7801 validateuserpwd=\u786e\u8ba4\u5bc6\u7801 useremail=EMAIL userques=\u95ee\u9898 userans=\u7b54\u6848 name=\u540d\u5b57 password=\u5bc6\u7801 rand=\u9a8c\u8bc1\u7801 email=EMAIL addr=\u5730\u5740 phonelength=11 errors.required={0} \u4e0d\u80fd\u4e3a\u7a7a. errors.email={0} \u9519\u8bef\u7684\u683c\u5f0f. errors.invalid={0}\u683c\u5f0f\u4e0d\u5bf9. phoneerror=\u76ee\u524d\u8fd8\u6ca1\u6709\u60a8\u7684\u6210\u7ee9 9. 新建和编写“register.jsp”页面: <%@ page language="java" pageEncoding="Gb2312"%> Java 框架 - 92 - 翰子昂系列理论教材 <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix="bean"%> <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix="html"%> <%@ taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix="logic"%> 登录页面


专题一 Struts 翰子昂系列理论教材 - 93 -
用户注册
用户名:
密   码:
确认密码:
性   别:
  男:       女:
EMAIL:
密码找回问 题:
密码找回答:
      提交
10. 配置 struts-config.xml 文件: Java 框架 - 94 - 翰子昂系列理论教材 11. 在 WEB-INF 下新建 validation.xml,将其配置为:
专题一 Struts 翰子昂系列理论教材 - 95 -
12. 运行效果: 图 1-36 注册页面 Java 框架 - 96 - 翰子昂系列理论教材 4. 相关理论知识 4.1 Struts 标签库 为了更容易、更快速地进行开发,Struts 提供了功能同 JSP 类似的标签库,常用的包括: HTML、Bean、Logic。 在运用 Struts 标签库前,我们需要通过 3 个步骤来配置一个 Struts 应用程序。 1. 在部署描述符(web.xml 文件)中注册 http://jakarta.apache.org/struts/tags-html /WEB-INF/struts-html.tld 上面的代码告诉 ServletContainer 有关 Struts HTML 标签库的情况,以及在那里可以找到 标签库的 TLD 文件。 2. 将 Struts-html.tld 文件复制到 WEB-INF 目录中。你不用担心标签库类文件,因为它们 已经包含在 struts.jar 文件中了。 3. 在运用标签的每一个页面中,插入下面的指示符: <%@ taglib uri=“http://jakarta.apache.org/struts/tags-html“ prefix=“html“ %> 另外,你也可以运用 struts-html.tld 文件来学习它所支持的标签及每个标签可以带有哪些 属性。 4.1.1 Struts HTML 标签库 HTML 标签提供了一组标签,它与 HTML 用于创建表单的标签集类似。使用 HTML 标 签库可以将 Form Bean 中的数据放置在表单控制器中。标签库中的部分 HTML 标签如表。 属性名 说明 html 表示一个 HTML元素 base 表示一个 HTML元素 text 表示输入类型为文本的 HTML元素 textarea 表示输入类型为文本区域的 HTML元素 radio 表示一个单选按钮 form 定义一个表单元素 button 定义一个按钮输入字段 cancel 定义一个取消按钮 checkbox 定义一个复选框字段 rewrite 定义一个 URI 专题一 Struts 翰子昂系列理论教材 - 97 - select 表示输入类型为选择的 HTML元素 HTML 标签用来生成 HTML 语言中的 HTML 元素。这个标签有 Lang 和 Xhtml 两个属性。 z Lang:表明 HTML 标签生成的 HTML 元素里有没有 lang 属性。如果取值为 true,h tml 元素将呈现 lang 属性并设置为用户在本次 Web 会话中的区域语言。如果在 Http Session 对象里没有找到区域语言信息,则使用 HTTP 标头中的 Accept-Language 字 段所指定的语言;如果在当前请求里没有找到 Accept-Language 标头字段,则使用服 务器的默认语言。 z Xhtml:如果这个属性的值设为 true,其他 HTML 标签将被强制性呈现为 XHTML 元素而不是普通的 HTML 元素。 JSP 页面里的标签将生成如下所示的 HTML 元素: Xhtml 属性将把与自己在同一个页面里的其他 HTML 标签强制性地呈现为 XHTML 元素 而不是普通的 HTML 元素。 form 标签生成 HTML 语言中的 form 元素。这个标签最重要的属性是 action,它指定了将 在用户提交表单时调用的 Struts 动作。在默认的情况下,HTML 表单都将按照 post 方法提交。 action 属性的值必须指定 Struts 配置文件里定义过的动作的路径。同时,在 Struts 配置文件里, 相应的 action 元素的 name 属性必须指向一个动作表单。 z action:指 定 HTML 表单将提交给哪个动作。这个属性的值必须是在 Struts 配置文件 里定义过的动作的路径。 z acceptCharset:服务器将接收的输入数据所使用的字符集编码清单 z enctype:用 post 方法提交的 HTML 表单的内容将使用的编码方案。在默认的情况下, 这个属性的值是 application/x-www-form- urlencoded。对于一个用来上传文件的表 单,这个属性必须设置为 multipart/form-data。 z method:用来提交这个请求的 HTTP 方法。在默认的情况下,这个属性的值是 post 一个 form 标签的示例。 text、password、hidden 和 textarea 标签将分别呈现为以下几种 HTML 元素:
3. 建立用以添加论坛版块的页面 addleibie.jsp。 <%@ page language="java" contentType="text/html;charset=gb2312"%> 商业资讯后台管理主页面
专题二 Hibernate 翰子昂系列理论教材 - 195 -

帖子类别管理

添加类别
/ItemAction.do?method=addItem" method="post" >
类别名称:
类别编码:
 
4. 新建 com.mybbs.pojo 包并在包下建立叫作 Item 的 pojo 类,这个类就代表帖子的版块 描述信息,其中包括如下字段: package com.mybbs.pojo; public class Item implements java.io.Serializable { //版块 id private Integer itemid; //版块名称 private String itemname; //版块编号 private String itemcode; public Item() { } public Item(Integer itemid, String itemname, String itemcode) { this.itemid = itemid; this.itemname = itemname; this.itemcode = itemcode; } public Integer getItemid() { return this.itemid; } public void setItemid(Integer itemid) { this.itemid = itemid; 专题二 Hibernate 翰子昂系列理论教材 - 197 - } public String getItemname() { return this.itemname; } public void setItemname(String itemname) { this.itemname = itemname; } public String getItemcode() { return this.itemcode; } public void setItemcode(String itemcode) { this.itemcode = itemcode; } } 5. 新建 Item.hbm.xml 文件: 6. 新建 com.mybbs.dao 包并在包下建立 HibernateDAO 类,用于操作数据库: package com.mybbs.dao; Java 框架 - 198 - 翰子昂系列理论教材 import java.util.List; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import com.mybbs.pojo.Item; import com.mybbs.utils.PageUtil; public class HibernateDAO { //用于单例模式 private static HibernateDAO instance = null; //Session 工厂用于生产 Session private static SessionFactory fac = null; //静态块 static { try { Configuration cfg = new Configuration().configure(); fac = cfg.buildSessionFactory(); } catch (Exception e) { e.printStackTrace(); } } private HibernateDAO() { } public static HibernateDAO getInstance() { if (instance == null) { instance = new HibernateDAO(); } return instance; } //得到版块信息 public List selectItem(int start) { //打开 Session Session session = fac.openSession(); List itemList = null; try { //开始事务 Transaction tra = session.beginTransaction(); 专题二 Hibernate 翰子昂系列理论教材 - 199 - //使用 Hibernate 的分页查询 Query query = session.createQuery("from Item"); query.setFirstResult(start); query.setMaxResults(PageUtil.pageSize); //获得数据 itemList = query.list(); //提交事务 tra.commit(); } catch (Exception e) { e.printStackTrace(); } finally { //关闭 Session session.close(); } return itemList; } // 得到所有帖子版块信息 public List selectAllItem() { //打开 Session Session session = fac.openSession(); List itemList = null; try { //开始事务 Transaction tra = session.beginTransaction(); Query query = session.createQuery("from Item"); //获得数据 itemList = query.list(); //提交事务 tra.commit(); } catch (Exception e) { e.printStackTrace(); } finally { //关闭 Session session.close(); } return itemList; } // 添加一个论坛版块 public void insertItem(Item item) { //打开 Session Java 框架 - 200 - 翰子昂系列理论教材 Session session = fac.openSession(); try { //开始事务 Transaction tra = session.beginTransaction(); //保存对象 session.save(item); //提交事务 tra.commit(); } catch (Exception e) { e.printStackTrace(); } finally { //关闭 Session session.close(); } } // 删除一个论坛版块 public void deleteItem(Item item) { //打开 Session Session session = fac.openSession(); try { //开始事务 Transaction tra = session.beginTransaction(); //删除数据 Query query = session.createQuery("delete Item i where i.itemid=?"); query.setInteger(0, item.getItemid()); query.executeUpdate(); //提交事务 tra.commit(); } catch (Exception e) { e.printStackTrace(); } finally { //关闭 Session session.close(); } } } 7. 新建 com.mybbs.service 包并在包下建立 ItemService 类,用于封装持久化代码,进而 完成对论坛版块信息的增删查,其中键入如下代码: 专题二 Hibernate 翰子昂系列理论教材 - 201 - package com.mybbs.service; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import com.mybbs.dao.HibernateDAO; import com.mybbs.pojo.Item; import com.mybbs.utils.PageUtil; public class ItemService { //分页显示版块信息 public List selectItem(HttpSession session, HttpServletRequest request) { //本页显示第一条数据在数据库中的索引 int start = 0; //当前页索引 int pageCount = PageUtil.firstPageIndex; //最大页数 int maxPageCount = 0; //获得数据库中所有数据 List list = HibernateDAO.getInstance().selectAllItem(); int count = list.size(); //计算最大页数 if (count % PageUtil.pageSize == 0) { maxPageCount = count / PageUtil.pageSize; } else { maxPageCount = count / PageUtil.pageSize + 1; } //如果最大页数大于 1,分页显示数据 if (maxPageCount > 1) { //分页按钮显示 request.setAttribute("display", "display"); //将当前页索引信息存入 session,如果为空说明第一次访问本页面,创建 session, 将 1 存入 if (session.getAttribute("pageCount") == null) { session.setAttribute("pageCount", PageUtil.firstPageIndex); } else { //如果点击首页按钮 if (request.getParameter("operater").toString().equals("first")) { //第一页 Java 框架 - 202 - 翰子昂系列理论教材 pageCount = PageUtil.firstPageIndex; session.setAttribute("pageCount", pageCount); }//点击最后一页按钮 else if (request.getParameter("operater").toString().equals( "end")) { //最后一页 pageCount = maxPageCount; session.setAttribute("pageCount", PageUtil.endPageIndex); }//点击上一页 else if (request.getParameter("operater").toString().equals( "previous")) { //如果当前页是最后一页,我们用 0 代表最后一页的页数 if (Integer.parseInt(session.getAttribute("pageCount") .toString()) == 0) { //当前页减一 pageCount = maxPageCount - 1; session.setAttribute("pageCount", pageCount); } else { session.setAttribute("pageCount", Integer .parseInt(session.getAttribute("pageCount") .toString()) - 1); pageCount = Integer.parseInt(session.getAttribute( "pageCount").toString()); } }//点击下一页 else if (request.getParameter("operater").toString().equals( "next")) { //当前页加一 session.setAttribute("pageCount", Integer.parseInt(session .getAttribute("pageCount").toString()) + 1); pageCount = Integer.parseInt(session.getAttribute( "pageCount").toString()); //如果当前页数等于最大页数,说明是最后一页。我们将 session 中的 pageCount 设为 0 if (pageCount == maxPageCount) { session.setAttribute("pageCount", PageUtil.endPageIndex); } } 专题二 Hibernate 翰子昂系列理论教材 - 203 - } } else { //数据不够两页所以不用分页 pageCount=maxPageCount; request.setAttribute("display", "notdisplay"); } start = (pageCount * PageUtil.pageSize) - PageUtil.pageSize; return HibernateDAO.getInstance().selectItem(start); } //添加一个论坛版块 public void addItem(Item item) { HibernateDAO.getInstance().insertItem(item); } //删除一个论坛版块 public void deleteItem(Item item) { HibernateDAO.getInstance().deleteItem(item); } } 8. 新建 com.mybbs.actionform 包并在包下建立 Itemform 类,键入如下代码: import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; public class Itemform extends ActionForm { //帖子类别 id private int subid; //帖子类别名称 private String subname; //帖子编号 private int subcode; public int getSubid() { return subid; } public void setSubid(int subid) { Java 框架 - 204 - 翰子昂系列理论教材 this.subid = subid; } public String getSubname() { return subname; } public void setSubname(String subname) { this.subname = subname; } public int getSubcode() { return subcode; } public void setSubcode(int subcode) { this.subcode = subcode; } //actioform 特有的验证方法 public ActionErrors validate(ActionMapping mapping,HttpServletRequest request) { return null; } //actionform 特有的重置方法 public void reset(ActionMapping mapping,HttpServletRequest request) { } } 9. 新建 com.mybbs.utils 包并在包下建立 pageUtil 类,键入如下代码: package com.mybbs.utils; public class PageUtil { //每页显示数据条数 public static final int pageSize=3; //第一页 public static final int firstPageIndex=1; //最后一页 public static final int endPageIndex=0; } 10. 新建 com.mybbs.action 包并在包下建立 ItemAction 类,注意此类继承自 org.apache.st ruts.actions.DispatchAction,在其中键入如下代码: package com.mybbs.action; 专题二 Hibernate 翰子昂系列理论教材 - 205 - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import com.mybbs.actionform.Itemform; import com.mybbs.pojo.Item; import com.mybbs.service.ItemService; import javax.servlet.http.HttpSession; import org.apache.struts.actions.DispatchAction; import org.apache.struts.action.Action; public class ItemAction extends DispatchAction { //添加帖子类别 public ActionForward addItem(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { HttpSession session = request.getSession(); //得到有关的 actionform 对象 Itemform itemform = (Itemform) form; //声明有关帖子类别的 pojo 对象 Item item = new Item(); item.setItemname(itemform.getSubname()); item.setItemcode(itemform.getSubcode()); //声明业务逻辑对象 ItemService itemSerivce = new ItemService(); itemSerivce.addItem(item); session.setAttribute("pageCount", null); return mapping.findForward("updatesuccess"); } public ActionForward deleteItem(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { HttpSession session = request.getSession(); String itemid = request.getParameter("id"); //声明有关帖子类别的 pojo 对象 Item item = new Item(); item.setItemid(Integer.parseInt(itemid)); //声明业务逻辑对象 ItemService itemSerivce = new ItemService(); itemSerivce.deleteItem(item); Java 框架 - 206 - 翰子昂系列理论教材 session.setAttribute("pageCount", null); return mapping.findForward("deletesuccess"); } public ActionForward getItem(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { //声明业务逻辑对象 ItemService itemSerivce = new ItemService(); //获得 session 对象 HttpSession session = request.getSession(); java.util.List list = itemSerivce.selectItem(session, request); request.setAttribute("list", list); return mapping.findForward("success"); } } 11. 打开 WEB-INF 当中的,struts-config.xml 文件,在其中键入如下代码: 专题二 Hibernate 翰子昂系列理论教材 - 207 - 12. hibernate.cfg.xml 配置信息如下: root jdbc:mysql://localhost:3306/mybbs org.hibernate.dialect.MySQLDialect MySQL sa com.mysql.jdbc.Driver 13. 部署运行程序,在地址栏里输入 http://localhost:8080/mybbs/ItemAction.do?method=ge tItem 打开查询页面,点击红色链接,为论坛添加版块。 图 2-42 查询论坛版块信息 Java 框架 - 208 - 翰子昂系列理论教材 14. 输入版块名称和编码后,点击“保存”。 图 2-43 添加论坛版块 15. 要删除论坛版块点击红色“删除”链接便可。 图 2-44 删除论坛版块 16. 如果数据量大的话程序会分页显示。 图 2-45 分页显示数据 4. 相关理论知识 4.1 Hibernate 的检索方式 1. OID 检索方式 OID 对象标始符,按照对象的 OID 来检索对象。Session 的 get()和 load()方法提供了这种 功能。如果在应用程序中事先知道了 OID,就可以使用这种检索对象的方式。 2. HQL 检索方式 使用面向对象的 HQL 查询语言。Session 的 find()方法用于执行 HQL 查询语句。此外,H ibernate 还提供了 Query 接口,它是 Hibernate 提供的专门的 HQL 查询接口,能够执行各种复 专题二 Hibernate 翰子昂系列理论教材 - 209 - 杂的 HQL 查询语句。 3. QBC 检索方式 使用(Query By Criteria)API 来检索对象。这种 API 封装了基于字符串形式的查询语句, 提供了更面向对象的接口。 4. 本地 SQL 检索方式 使用本地数据库的 SQL 查询语句。Hibernate 会负责把检索到的 JDBC ResultSet 结果集 映射成为对象图。 我们将在本章主要介绍 HQL 检索方式、QBC 检索方式和本地 SQL 检索方式,重点介绍 了 HQL 查询的语法,此外还介绍了各种检索方式的优缺点,指出了各自的使用场合。 4.2 HQL 检索方式 HQL(Hibernate Query Language)是面向对象的查询语言,它和 SQL 查询语言有些相 似。在 Hibernate 提供的各种检索方式中,HQL 是使用最广泛的一种检索方式。它具有以下 功能: z 在查询语句中设置各种查询条件。 z 支持投影查询,即仅检索出对象的部分属性。 z 支持分页查询。 z 支持连接查询。 z 支持分组查询,允许使用 having 和 ground by 关键字。 z 提供内置聚集函数,如 sum()、min()和 max()。 z 能够调用用户定义的 SQL 函数。 z 支持子查询,即嵌入式查询。 z 支持动态绑定参数。 Session 类的 find()方法及 Query 接口都支持 HQL 检索方式。这两者的区别在于,前者只 是执行一些简单 HQL 查询语句和便捷方法,它不具有动态绑定参数的功能,而且在将来新的 Hibernate 中,有可能淘汰 find()方法;而 Query()接口才是真正的 HQL 查询接口,它提供了以 上列出的各种查询功能。 以下程序代码用于检索姓名为“soctt”,并且密码为“tiger”的 Userinfo 对象。 //创建一个 Query 对象。 Query query=session.createQuery("from Userinfo as u where u.username=? and u.userpwd=?"); //动态绑定参数 query.setString(0, "scott"); query.setString(1,"tiger"); //执行查询语句,返回结果集 List result=query.list(); HQL 检索方式包括以下步骤: Java 框架 - 210 - 翰子昂系列理论教材 1. 通过 Session 的 createQuery()方法创建一个 Query 对象,它包含一个 HQL 查询语句。 HQL 查询语句可以包含占位符“?”。 2. 动态绑定参数。Query 接口提供各种类型的参数赋值语法,例如 setString()方法用于为 HQL 语句中的“username”参数赋值。 3. 调用 Query 的 list()方法执行查询语句。该方法返回 List 类型的查询结果。在 List 集合 中存放了符合查询条件的持久化对象。执行以上代码,当运行 Query 的 list()方法时,Hiberna te 执行以下 SQL 查询语句: Select * from Userinfo where username=”scott” and userpwd=”tiger” Query 接口支持方法链编程风格,它的 setString()方法及其他的 setXXX()方法都返回自身 实例,而不是返回 void 类型。以下是 Query 接口的实现类 setString()方法的源程序。 public Query setString(int position,String val) { setParameter(position,val,Hibernate.STRING); retrun this; } 如果采用方法链编程风格,将按以下形式访问 Query 接口: Query query=session.createQuery("from Userinfo as u where u.username=? and u.userpwd=?").setString(0, "scott").setString(1,"tiger"); 可见,方法链编程风格能使程序代码更加简洁。 4.3 QBC 检索方式 采用 HQL 检索方式时,在应用程序中需要定义基于字符串形式的 HQL 查询语句。QBC API 提供了检索对象的另一种方法,它主要由 Criteria 接口、Criterion 接口和 Expression 类组 成,它支持在运行时动态生成查询语句。 //创建一个 Criteria 对象。 Criteria criteria=session.createCriteria(Userinfo.class); //设置查询条件,然后把查询条件加入 Criteria 中 Criterion criterion1=Expression.like("username","s%"); Criterion criterion2=Expression.like("userpwd","tiger"); criteria.add(criterion1); criteria.add(criterion2); //执行查询语句,返回结果集 List result= criteria.list(); QBC 检索方式包括以下步骤: 1. 通过 Session 的 createCriteria()方法创建一个 Criteria 对象。 专题二 Hibernate 翰子昂系列理论教材 - 211 - 2. 设定查询条件。Expression 类提供了一系列用于设定查询条件的静态方法,这些静态 方法都返回 Criterion 的实例,每个 Criterion 代表一个查询条件。Criterion 的 add()方法用于加 入查询条件。 3. 调用 Criteria 的 list()方法执行查询语句。该方法返回 List 类型的查询结果,在 List 集 合中存放了符合查询条件的持久化对象。对于执行以上的代码,当运行 Criteria 的 list()方法时, Hibernate 执行的 SQL 查询语句为: Select * from Userinfo where username like ”%s” and userpwd=”tiger” Criteria 接口支持方法链编程风格,它的 add()方法返回自身实例,而不是返回 void 类型。 以下是 Criteria 接口的实现类 add()方法的源程序: public Criteria add(Criterion expression) { CriteriaImpl.this.add(rootAlias,expression); return this; } 如果采用编程链编程风格,将按以下形式访问 Criteria 接口: List result= session.createCriteria(Userinfo.class).add(Expression.like("username","s%") ).add(Expression.like("userpwd","tiger")).list(); Hibernate 还提供了 QBE(Query By Example)检索方式,它是 QBC 的子功能。QBE 允 许先创建一个对象样板,然后检索出所有和这个样板相同的对象。 Userinfo user=new Userinfo(); user.setUsername("scott"); user.setUserpwd("tiger"); //执行查询语句,返回结果集 List result= session.createCriteria(Userinfo.class).add(Example.create(user)).list(); 在 QBE API 中,Example 接口的静态方法 create()创建一个 Criterion 对象,它代表按照 样板对象的属性来比较的查询条件。对以上程序,Hibernate 执行的 SQL 查询语句为: Select * from Userinfo where username=”scott” and userpwd=”tiger” 从以上查询语句看出,在默认条件下,Hibernate 能把 Userinfo 对象中所有不为 Null 的属 性作为查询条件。 QBE 的功能不是特别强大,仅在某些场合下有用。一个典型的使用场合是在查询窗口中 让用户输入一系列的查询条件,然后返回匹配的对象。 4.4 SQL 检索方式 采用 HQL 或 QBC 检索方式时,Hibernate 会生成标准的 SQL 查询语句,适用于所有的数 Java 框架 - 212 - 翰子昂系列理论教材 据库平台,因此这两种检索方式是跨平台的。 有的应用程序可能需要根据底层数据库的 SQL 方言,来生成一些特殊的查询语句。在这 种特殊的情况下。可以利用 Hibernate 提供的 SQL 检索方式。以下程序用于检索用户名为“s octt”的用户的密码。 //创建 Query 对象 Query query= session.createSQLQuery("Select u.userpwd from Userinfo u where username=?"); //动态绑定参数 query.setString(0, "scott"); //执行 SQL select 语句,返回结果集 List list=query.list(); 从以上代码看出,SQL 检索方式与 HQL 检索方式都使用 Query 接口,区别在于 SQL 检 索方式通过 Session 的 createSQLQuery()方式创建 Query 对象,这个方法的参数指定一个 SQL 查询语句,该语句使用本地数据库的 SQL 方言。 4.5 使用别名 最简单的查询检索一个持久化类的所有实例。例如: //采用 HQL 检索方式 List result=session.createQuery("from Userinfo").list(); //采用 QBC 方式 List secondresult=session.createCriteria(Userinfo.class).list(); 通过 HQL 检索一个类的实例时,如果查询语句的其他地方需要引用它,应该为这个类指 定一个别名,例如: from Userinfo as u where u.username=? as 关键字用于设定别名,也可以将 as 关键字省略: from Userinfo u where u.username=? 在实际应用中,建议使用别名与类名相同,例如 Userinfo 类赋予别名“userinfo”,而不 是别名“u”: from Userinfo as userinfo where userinfo.username=? QBC 检索方式不需要由应用程序显示指定类的别名,Hibernate 会自动把查询语句中的根 节点实体赋予别名“this”。例如以下程序代码中 Expression 类的 eq()方法的第一个参数为“u sername”,它代表 Userinfo 类的 username 属性: List result=session.createCriteria(Userinfo.class) .add(Expression.eq("username","scott")) .list(); 专题二 Hibernate 翰子昂系列理论教材 - 213 - Hibernate 为 Userinfo 类自动赋予别名“this”,因此程序中也可以按照“this.username” 的形式引用 Userinfo 的 username 属性: List result=session.createCriteria(Userinfo.class) .add(Expression.eq("this.username","scott")) .list(); 4.6 多态查询 HQL 和 QBC 都支持多态查询,多态查询是指查询出当前类及所有子类的实例,对于以 下代码: //采用 HQL 查询 session.createQuery(“from Employee”); //采用 QBC 检索方式 session.createCriteria(Employee.class); 加入 Employee 有两个子类:HourlyEmployee 和 SalariedEmployee,那么这个查询语句会 查询出所有 Employee 类的实例,以及 HourlyEmployee 类和 SalariedEmployee 类的实例。如 果只想检索某个特定类的实例,可以使用如下方式 : //采用 HQL 查询 session.createQuery(“from HourlyEmployee”); //采用 QBC 检索方式 session.createCriteria(HourlyEmployee.class); 以下 HQL 查询语句将检索出所有的持久化对象: from java.lang.Object 多态查询对接口也适用。例如,以下查询语句查询出所有实现 Serializable 接口的实例: from java.io.Serializable Hibernate 不仅对 from 子句中显示指定的类进行多态查询,而且对其他关联的类也会进行 多态查询。 4.7 对查询结果排序 HQL 和 QBC 都支持对查询结果的排序。HQL 采用 order by 关键字对查询结果排序,而 QBC 采用 org.hibernate.expression.Order 类对查询结果排序,下面举例说明它们的用法。 1. 查询结果按照用户名升序排列: //HQL 查询方式 Query query=session.createQuery(“form Userinfo as u order by u.username”); //QBC 查询方式 Criteria criteria=session.createCriteria(Userinfo.class); criteria.addOrder(Order.asc(“username”)); Java 框架 - 214 - 翰子昂系列理论教材 2. 查询结果按用户名升序排序,并且按照密码倒序排列: //HQL 查询方式 Query query= session.createQuery(“form Userinfo as u order by u.username asc,u.userpwd desc”); //QBC 查询方式 Criteria criteria=session.createCriteria(Userinfo.class); criteria.addOrder(Order.asc(“username”)); criteria.addOrder(Order.asc(“userpwd”)); 如果应用程序中既通过 import 语句引入了 org.hibernate.expression.Order 类,又通过 impo rt 语句引入了 mypack.Order 类,在程序中引用这两个类名时,必须给出完整的包路径,以便 区分这两个 Order 类,例如: import org.hibernate.expression.Order; import mypack.Order; Criteria criteria=session.createCriteria(mypack.Orde.class); criteria.addOrder(org.hibernate.expression.Order.asc(“username”)); 4.8 分页查询 当批量查询数据库时,如果数据量很大,会导致无法在用户终端的单个页面上显示所有 的查询结果,此时需要对查询结果分页。假如 userinfo 表中有 99 条记录,可以在用户终端上 分 10 页来显示数据,每页最多显示 10 个 userinfo 对象,用户可以导航到下一页。Query 和 C riteria 接口都提供了用于分页显示查询结果的方法。 1. setFirstResult(int firstResult):设置从那个对象开始检索,参数 firstResult 表示这个对 象在查询结果中的索引位置,索引位置的起始值为 0。在默认情况下,Query 和 Criteria 接口 从查询结果中的第一个对象,也就是索引位置为 0 的对象开始检索。 2. setMaxResult(int maxResults):设定一次最多检索出的对象数目。在默认情况下,Que ry 和 Criteria 接口检索出查询结果中所有对象。 以下代码从查询结果的起始对象开始,共检索出 5 个 userinfo 对象,查询结果按照 usern ame 属性排序: //采用 HQL 检索方式 Query query= session.createQuery("from Userinfo u order by u.username"); query.setFirstResult(0); query.setMaxResults(5); List list=query.list(); //采用 QBC 检索方式 Criteria criteria=session.createCriteria(Userinfo.class); criteria.addOrder(Order.asc("username")); criteria.setFirstResult(0); criteria.setMaxResults(5); 专题二 Hibernate 翰子昂系列理论教材 - 215 - List list=criteria.list(); 如果查询结果中共有 99 个 userinfo 对象,那么在 list 中包含 5 个 userinfo 对象,第 1 个 对象在查询结果中的索引位置为 0,第 5 个对象在查询结果中的索引位置为 4。 以下代码从查询结果中索引位置为 97 的对象开始,共检索出 10 个 userinfo 对象: //采用 HQL 检索方式 Query query= session.createQuery("from Userinfo u order by u.username"); query.setFirstResult(97); query.setMaxResults(10); List list=query.list(); //采用 QBC 检索方式 Criteria criteria=session.createCriteria(Userinfo.class); criteria.addOrder(Order.asc("username")); criteria.setFirstResult(97); criteria.setMaxResults(10); List list=criteria.list(); 如果查询结果中共有 99 个对象,那么在 result 中只包含两个 userinfo 对象,它们的索引 为 97 和 98。 对于以上程序代码,也可以采用方法链编程风格: //采用 HQL 检索方式 Query query= session.createQuery("from Userinfo as u order by u.username") .setFirstResult(0) .setMaxResults(5); //采用 QBC 检索方式 Criteria criteria=session.createCriteria(Userinfo.class) .addOrder(Order.asc("username")) .setFirstResult(0) .setMaxResults(5); 4.9 检索单个对象 Query 和 Criteria 接口都提供了以下用于执行查询语句并返回查询结果的方法。 1. list()方法:返回一个 List 类型的查询结果,在 List 集合中存放了所有满足查询条件的 持久化对象。 2. uniqueResult()方法:返回单个对象。 在某种情况下,如果希望检索出一个对象,可与先调用 Query 或 Criteria 接口的 setMaxR esult(1)方法,把最大检索数目设为 1: setMaxResult(1); 接下来调用 uniqueResult 方法,该方法返回一个 Object 类型的对象: Java 框架 - 216 - 翰子昂系列理论教材 //采用 HQL 检索方式 Userinfo user= (Userinfo)session.createQuery("from Userinfo as u order by u.username") .setMaxResults(1) .uniqueResult(); //采用 QBC 检索方式 Userinfo user=(Userinfo)session.createCriteria(Userinfo.class) .addOrder(Order.asc("username")) .setMaxResults(1) .uniqueResult(); 如果明确知道查询结果只会包含一个对象,可以不调用 setMaxResults(1)方法,例如: Userinfo user= (Userinfo)session.createQuery("from Userinfo as u where u.userid=1").uniqueResult(); 以下查询结果包含多个 Customer 对象,但是没有调用 setMaxResults(1)方法: Userinfo user= (Userinfo)session.createQuery("from Userinfo").uniqueResult(); 执行以上 uniqueResult()方法时,会抛出 NonUniqueResultException()异常: org.hibernate.NonUniqueResultException: query did not return a unique result: 13 5. 试验 按照上课的顺序依次练习,主要掌握: 1. 在 mybbs 数据库中建立 item 表并建立 MyBBS 工程。(10 分钟) 2. 编写 leibie.jsp, addleibie.jsp 页面。(30 分钟) 3. 编写用于数据访问的 HibernateDAO 类。(10 分钟) 4. 编写业务逻辑类 ItemService 和 pageUtil 工具类。(10 分钟) 5. 编写 ItemAction 和 Itemform 类。(20 分钟) 6. 配置 struts-config.xml,hibernate.cfg.xml 文件,并调试运行程序。(10 分钟) 6. 课后作业 完成 mybbs 论坛版块管理中的修改功能。 专题三 Spring 翰子昂系列理论教材 - 217 - 专题三 Spring 教学目标 掌握 Spring IoC 原理 掌握 Spring AOP 原理 Java 框架 - 218 - 翰子昂系列理论教材 案例一 Spring IoC 基础 1. 教学目标 1.1 使用工具如何建立一个 Spring 工程 1.2 Spring 的 IoC 2. 工作任务 2.1 使用 MyEclipse 添加 Spring 开发环境 2.2 Spring 第一个“Hello World”程序 2.3 Spring IoC 实现简单员工管理程序 3. 相关实践知识 3.1 使用 MyEclipse 添加 Spring 开发环境 MyEclipse 为我们提供了 Spring 支持环境的构建向导,打开 MyEclipse6.0,创建步骤具体 如下: 1. 首先建立一个 Web 项目,点击菜单栏【File】Î【New】Î【Web Project】,在 Pro- ject Name 当中输入项目名称“SpringTest”,然后点击【Finish】。 图 3-1 建立 Web 工程 专题三 Spring 翰子昂系列理论教材 - 219 - 图 3-2 命名 Web 工程 图 3-3 Web 项目结构 2. 然后在项目中加入 Spring 支持,右键点击项目根节点【Spring】Î【MyEclipse】Î【A dd Spring Capabilities】。 Java 框架 - 220 - 翰子昂系列理论教材 图 3-4 添加 Spring 支持 3. 弹出如图 1-5 所示的设置窗口,在窗口中依次包括以下内容: z Spring version:选择 Spring 2; z Select the libraries to add the buildpath:选择 MyEclipse 提供的 Spring 的库,默认 只有 Spring 2.0 core libraries 被选中; z JAR Library Installation:指定将 Spring 的 jar 文件复制到的目录,默认使用“Add checked Libraries to project build-path”; z Tag Library Installation:指定 Spring 的 Tag 标签文件复制到的目录,默认为不采用 Spring Tag。 设置后的结果如图 9-5 所示: 专题三 Spring 翰子昂系列理论教材 - 221 - 图 3-5 添加 Spring 支持 4. 单击图 9-5 中的【Next】按钮,进入如图 9-6 所示的窗口,此时选中图中的复选框, 并进行如下的设置。 z Folder:选择 Bean 配置文件存放的路径,点击【Browse】按钮,选择路径,默认路 径为 src; z File:设置 Bean 配置文件的名称,默认为“applicationContext.xml”; 设置后的结果如图 9-6 所示。 Java 框架 - 222 - 翰子昂系列理论教材 图 3-6 设置 Spring 配置文件 配置完成后,单击【Finish】按钮即可完成 Spring 支持环境的添加。 5. 现在项目“SpringTest”已经能够支持 Spring 了,也就是说已经可以添加 Spring 的相 关的功能代码了。 专题三 Spring 翰子昂系列理论教材 - 223 - 图 3-7 添加 Spring 支持 3.2 Spring 第一个“Hello Word”程序 1. 新建“HelloWorld”工程,并添加 Spring 支持。如图: 图 3-8 添加了 Spring 支持的 HelloWord 工程 2. 在跟目录下新建 HelloWord.java,代码如下: public class HelloWorldBean { //定义 sayHello 方法,在控制台打印 HELLO WORLD public void sayHello() { System.out.println("HELLO WORLD"); } } 3. 在跟目录下新建 BeanTest.java,作为运行的主程序,代码如下: import org.springframework.context.ApplicationContext; import org.springframework.context.support. ClassPathXmlApplicationContext; public class BeanTest { //程序入口 public static void main(String args []) { //创建 Spring 的 ApplicationContext,用于测试 ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); //使用 Spring 上下文获得 HelloWorldBean 实例 HelloWorldBean bean=(HelloWorldBean)context.getBean("helloWorldBean"); //调用 HelloWorldBean 的 sayHello 方法 bean.sayHello(); } } Java 框架 - 224 - 翰子昂系列理论教材 4. 编写简单的 Spring 配置文件,配置文件内容如下: 5. 运行程序在控制台输出“HELLO WORLD”,运行结果: HELLO WORLD 3.3 Spring IoC 实现简单员工管理程序 1. 新建“EmployeeManager”工程,并添加 Spring 支持。如图: 图 3-9 添加了 Spring 支持的 EmployeeManager 工程 2. 新建 Computer.java 接口,代码如下: //定义 Computer 接口 public interface Computer { //Computer 接口里有个打字方法 public void codeing(); } 3. 新建 Employee.java 接口,代码如下: //定义 Employee 接口 public interface Employee { //Employee 接口里定义一个使用电脑的方法 专题三 Spring 翰子昂系列理论教材 - 225 - public void userComputer(); } 4. 新建 Age.java 类用于提供一个方法返回整型值,代码如下: public class Age { public int getValue() { return 23; } } 5. 新建 NotebookPC.java 类继承 Computer 接口,代码如下 //Computer 的第一个实现类 NotebookPC public class NotebookPC implements Computer { //默认构造函数 public NotebookPC() { } //实现 Computer 接口的 codeing 方法 public void codeing() { System.out.println("笔记本电脑打字"); } } 6. 新建 Programmer.java 类用于用于描述员工信息,代码如下: import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; public class Programmer implements Employee { //姓名 private String name; //年龄 private int age; //下面是系列集合属性,分别表示此人的学校,成绩,健康情况 private List schools=new ArrayList(); private Map score=new HashMap(); Java 框架 - 226 - 翰子昂系列理论教材 private Properties health=new Properties(); //面向 Computer 接口编程,而不是具体的实现类 private Computer computer; //默认构造函数 public Programmer() { } public Computer getComputer() { return computer; } //设置注入需要的 setter 方法 public void setComputer(Computer computer) { this.computer = computer; } //实现 Employee 接口的 userComputer 方法 public void userComputer() { computer.codeing(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public List getSchools() { return schools; } public void setSchools(List schools) { this.schools = schools; } public Map getScore() { return score; } public void setScore(Map score) { this.score = score; 专题三 Spring 翰子昂系列理论教材 - 227 - } public Properties getHealth() { return health; } public void setHealth(Properties health) { this.health = health; } } 7. Spring 配置文件: 李明 getValue Java 框架 - 228 - 翰子昂系列理论教材 小学 中学 大学 87 89 82 正常 175 8. 编写主程序,用于测试,代码如下: import org.springframework.context.ApplicationContext; 专题三 Spring 翰子昂系列理论教材 - 229 - import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestBean { public static void main(String args []) { //因为是独立的应用程序,显示地实例化 Spring 的上下文 ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml"); //利用 Spring 上下文得到 Programmer 类的实例 Programmer programmer=(Programmer)context.getBean("programmer"); //打印姓名 System.out.println(programmer.getName()); //打印年龄 System.out.println(programmer.getAge()); //打印学校信息 System.out.println(programmer.getSchools()); //打印成绩信息 System.out.println(programmer.getScore()); //打印健康情况 System.out.println(programmer.getHealth()); //使用电脑 programmer.userComputer(); } } 9. 运行程序,运行结果如下: 李明 23 [小学, 中学, 大学] {数学=87, 英语=89, 语文=82} {血压=正常, 身高=175} 笔记本电脑打字 4. 相关理论知识 4.1 Spring 概念 Spring 为企业应用的开发提供一个轻量级的解决方案。该解决方案包括基于依赖注入的 核心机制,基于 AOP 的申明式事务管理,与多种持久层技术的整合,以及优秀的 Web Mvc 框架等。 Spring 的设计哲学是:在尽量不影响 Java 对象的情况下,将 Java 对象加入框架的管理中。 Spring 是一种低侵入式的框架。 Java 框架 - 230 - 翰子昂系列理论教材 Spring 支持对 POJO(plain old Java Object,指最传统的 Java 对象,和任何模式都无关) 的管理,能将 J2EE 应用各层的对象“组装”在一起,甚至这些对象无须标准的 JavaBean。 Spring 也是高度组件化的框架。它允许开发者选用该框架的部分或全部,并不强制开发 者选择 Spring 的全部。无论怎样,绝不会影响 Spring 的独特魅力。 4.2 Spring 历史 Spring 的基础架构起源于 2000 年早期,它是 Rod Johnson 在一些成功的商业项目中构建 的基础设施。 在 2002 后期,Rod Johnson 发布了《Expert One-on-One J2EE Design and Development》 一书,并随书提供了一个初步的开发框架实现——interface21 开发包,interface21 就是书中阐 述的思想的具体实现。后来,Rod Johnson 在 interface21 开发包的基础之上,进行了进一步 的改造和扩充,使其发展为一个更加开放、清晰、全面、高效的开发框架——Spring。 2003 年 2 月 Spring 框架正式成为一个开源项目,并发布于 SourceForge。 4.3 Spring 包含的模块 Spring 框架由七个定义明确的模块组成。 图 3-10 Spring 模块 组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联 合实现。每个模块的功能如下: 1. 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFacto ry,它是工厂模式的实现。BeanFactory 使用控制反转(IoC)模式将应用程序的配置和依赖性 规范与实际的应用程序代码分开。 2. Spring Context(上下文):Spring 上下文是一个配置文件,向 Spring 框架提供上下 文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功 能。 4. Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理 专题三 Spring 翰子昂系列理论教材 - 231 - 异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地 降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常 遵从通用的 DAO 异常层次结构。 5. Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系 工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。 6. Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化 了处理多部分请求以及将请求参数绑定到域对象的工作。 7. Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通 过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、V elocity、Tiles、iText 和 POI。 Spring 框架的功能可以用在任何 J2EE 服务器中。Spring 的核心要点是:支持不绑定到特 定 J2EE 服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同 J2EE 环境(W eb 或 EJB)、独立应用程序、测试环境之间重用。 4.4 Spring 的 Ioc 容器 IoC,Inversion of Control,控制反转又名依赖注入,其原理是基于 OO 设计原则的 The Hollywood Principle:Don't call us, we'll call you。也就是说,所有的组件都是被动的(Pas sive),所有的组件初始化和调用都由容器负责。组件处在一个容器当中,由容器负责管理。 Spring 以 bean 的方式组织,管理 Java 应用中各组件,组件之间的依赖关系是松耦合运行 良好。Spring 使用 BeanFactory 作为应用中负责产生和管理各组件的工厂,同时也是组件运行 时的容器。BeanFactory 根据配置文件确定容器中 bean 的实现,管理 bean 之间的依赖关系。 bean 之间的依赖关系由 BeanFactory 管理,bean 的具体实例化过程也由 BeanFactory 来管 理。将 bean 和 bean 实现类的依赖解耦,变成对接口的依赖。程序从面向具体类的编程,转 向编程面向接口编程。极大地降低应用中组件的耦合。 ApplicationContext 是 BeanFactory 的加强。ApplicationContext 接口提供在 J2EE 应用中的 大量增强功能,比如随 Web 应用的自动创建,程序国际化等。 ApplicationContext 通常使用如下两个实现类: z FileSystemXmlApplicationContext:以指定路径的 XML 配置文件创建 ApplicationCo ntext。 z ClassPathXmlApplicationContext:以 CLASSPATH 路径下的 XML 配置文件创建 Ap plicationContext。 依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念,具 体的含义:当某个角色(可能是一个 Java 实例,调用者)需要另一个角色(另一个 Java 实例, 被调用者)的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。但 在 Spring 里,创建被调用者的工作不再由调用者来完成,因此成为控制反转,创建被调用者 的实例的工作通常由 Spring 容器来完成,然后注入调用者,因此也称为依赖注入。 不管是依赖注入,还是控制反转,都说明 Spring 采用动态,灵活的方式来管理各种对象。 Java 框架 - 232 - 翰子昂系列理论教材 对象与对象之间的具体实现互相透明。依赖注入通常有两种: z 设值注入 z 构造注入 4.4.1 设值注入 设值注入是指通过 setter 方法传入被调用者的实例。这种注入方式简单,直观,因而在 S pring 的依赖注入里大量使用。看下面的代码,这是 Employee 的接口: //定义 Employee 接口 public interface Employee { //Employee 接口里定义一个使用电脑的方法 public void userComputer(); } 然后是 Computer 的接口: //定义 Computer 接口 public interface Computer { //Computer 接口里有个打字方法 public void codeing(); } Employee 的实现类如下: Public class Programmer implements Employee { //面向 Computer 接口编程,而不是具体的实现类 private Computer computer; //默认构造函数 public Programmer() { } public Computer getComputer() { return computer; } //设置注入需要的 setter 方法 public void setComputer(Computer computer) { this.computer = computer; } //实现 Employee 接口的 userComputer 方法 public void userComputer() { 专题三 Spring 翰子昂系列理论教材 - 233 - computer.codeing(); } } Computer 的一个实现类 NotebookPC: //Computer 的第一个实现类 NotebookPC public class NotebookPC implements Computer { //默认构造函数 public NotebookPC() { } //实现 Computer 接口的 codeing 方法 public void codeing() { System.out.println("笔记本电脑打字"); } } 下面采用 Spring 的配置文件将 Employee 和 Computer 实例组织在一起。配置文件如下所 示: Java 框架 - 234 - 翰子昂系列理论教材 从配置文件中,可以看到 Spring 管理 bean 的灵巧性,bean 与 bean 之间的依赖关系放在 配置文件里组织,而不是写在代码里。通过配置文件的指定,Spring 能够精确地为每个 bean 注入属性。因此,配置文件里的 bean 的 class 元素,必须是真正的实现类。 Spring 会自动接管每个 bean 定义里的 property 元素定义。Spring 会在执行无参的构造器 和创建默认的 bean 实例后,调用对应的 setter 方法为程序注入属性值。Property 定义的属性 值将不再由该 bean 主动创建,管理,而改为被动接收 Spring 的注入。 每个 bean 的 id 属性是该 bean 的唯一标识。程序通过 id 属性访问 bean。bean 与 bean 的 依赖关系也是通过 id 属性完成。 下面看主程序部分: Import org.springframework.context.ApplicationContext; import org.springframework.context.support. ClassPathXmlApplicationContext; public class BeanTest { //主方法,程序入口 public static void main(String args []) { //因为是独立的应用程序,显示地实例化 Spring 的上下文 ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml"); //Employee bean 的 id 来获取 bean 的实例,面向接口编程,因此 //此处强制类型转换为接口类型 Employee em=(Employee)context.getBean("programmer"); //直接执行 Employee 的 userComputer()方法 em.userComputer(); } } 程序执行结果: 笔记本电脑打字 主程序调用 Employee 的 userComputer()方法时,该方法的方法体内需要使用 Computer 的实例,但程序里没有任何地方将特定的 Employee 实例和 Computer 实例耦合在一起。或者 说,程序里没有为 Employee 实例传入 Computer 的实例,Computer 实例由 Spring 在运行期间 动态注入。 Employee 实例不仅不需要了解 Computer 实例的具体实现,甚至无须了解 Computer 的创 建过程。程序在运行到需要 Computer 时,Spring 创建了 Computer 实例,然后注入给需要的 C omputer 的调用者。Employee 实例运行到需要 Computer 实例的地方,自然就产生了 Compute r 实例,用来供 Employee 实例使用。 专题三 Spring 翰子昂系列理论教材 - 235 - 4.4.2 构造注入 所谓构造注入,指通过构造函数来完成依赖关系的,而不是通过 setter 方法。对前面代码 Programmer 类作简单的修改,修改后的代码如下: Public class Programmer implements Employee { //面向 Computer 接口编程,而不是具体的实现类 private Computer computer; //默认构造函数 public Programmer(Computer computer) { this.computer=computer; } //实现 Employee 接口的 userComputer 方法 public void userComputer() { computer.codeing(); } } 此时无须 Programmer 类里的 setComputer 方法,构造 Programmer 实例的时候,Spring 为 Programmer 实例注入所依赖的 computer 实例。构造注入的配置文件也需做简单的修改,修改 后的配置文件如下: 执行效果与使用设值注入时的执行效果完全一样,区别在于:创建 Programmer 实例中 computer 属性的时机不同,设值注入是先创建一个默认的实例,然后调用对应的 setter 方法注 入依赖关系。而构造注入则在创建 bean 实例时,已经完成依赖关系的注入。 设值注入和构造注入,都是 Spring 支持的依赖注入模式。也是目前主流的依赖注入模式。 Java 框架 - 236 - 翰子昂系列理论教材 两种注入各有优点。建议采用以设值注入为主,构造注入为辅的注入策略。对于依赖关系无 须变化的注入,尽量采用构造注入;而其他的依赖关系的注入,则考虑采用设置注入。 4.4.3 bean 的基本行为 bean 在 Spring 的容器中有两种基本行为: z singleton:单态 z non-singleton 或 prototype:原形 如果一个 bean 被设置成 singleton 的,整个 Spring 容器里只有一个共享实例存在,程序每 次请求该 id 的 bean,Spring 都会返回该 bean 的共享实例。 如果一个 bean 被设置成 non-singleton 行为,程序每次请求该 id 的 bean,Spring 都会创建 一个 bean 实例,然后返回给程序。通常,要求 Web 应用程序的控制器 bean 被配置成 non-si ngleton 行为,因为每次 HttpServletRequest 都需要系统启动一个新 Action 来处理用户请求。 如果不指定 bean 的行为,Spring 默认使用 singleton 行为。Java 在创建 Java 实例时,需 要进行内存申请;销毁实例时,需要完成垃圾回收。这些工作都会导致系统开销的增加。因 此,non-single 行为的 bean 创建、销毁时代价较大。而 singleton 行为的 bean 在实例后,可以 重复使用。因此,除非必要,尽量避免将 bean 设置成 non-singleton 行为。 设置 bean 的基本行为,通过 scope 属性来指定,scope 属性值只接受 singleton 或 prototy pe。下面配置 singleton 和 non-singleton bean 各一个。 主程序通过如下代码来测试两个 bean 的区别: Import org.springframework.context.ApplicationContext; import org.springframework.context.support. ClassPathXmlApplicationContext; public class BeanTest { //主方法,程序入口 public static void main(String args []) 专题三 Spring 翰子昂系列理论教材 - 237 - { //因为是独立的应用程序,显示地实例化 Spring 的上下文 ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml"); //判断两次请求 singleton 行为的 bean 实例是否相等 System.out.println(context.getBean(“programmer”) ==context.getBean(“programmer”)); //判断两次请求 non-singleton 行为的 bean 实例是否相等 System.out.println(context.getBean(“anthorprogrammer”) ==context.getBean(“anthorprogrammer”)); } } 程序运行结果: true flase 4.4.4 bean 依赖关系可接受的元素 z value z ref z bean z list,set,map 以及 props 4.3.4.1 value 元素 value 元素用于确定字符串参数。这些参数由 PropertyEditors 完成转换:从 java.lang.Strin g 类型转化为所需的参数类型。基本数据类型,通常可以正确转换。下面代码演示 value 元素 正确属性值的情况: < 1 2.3 Java 框架 - 238 - 翰子昂系列理论教材 下面是该 bean 的实现类,接口不再列出: Public class ExampleBean { private int integerProperty; private double doubleProperty; //默认构造函数 public void setIntegerProperty(int i) { this.integerProperty=i; } //默认构造函数 public void setDoubleProperty (double d) { this.doubleProperty =d; } } 测试本程序与之前的程序差别不大,在此不再赘述。执行结果是: 1 2.3 value 元素主要传入字符串参数,基本数据类型参数。也可以传入合作者 bean,但不推荐 使用 value 元素。可以指定为空,指定空属性值通过元素,代码如下: 它采用如下配置文件的效果相同: 4.4.4.2 ref 元素 ref 元素:用于指定属性值的 Spring 容器中的其他 bean。ref 元素是一种简写,并可防止 专题三 Spring 翰子昂系列理论教材 - 239 - 出现引用错误,用于设置属性值为容器中其他 bean。通常,对属性值为容器中其他 bean 的情 况,推荐采用 ref 元素指定,而不是 value 元素。看下面的配置片段: 于下面的配置片段效果完全一样: 第一种形式比第二种形式更好的原因:使用 ref 标记,可以让 Spring 容器在部署时验证 依赖的 bean 是否真正存在;在第二种形式中,notebookPC 属性值仅在创建 bean 时作验证, 会导致错误的延迟出现。而且,第二种还有额外的类型转换开销。因此,合作者 bean 属性值 的传入,推荐采用 ref 元素指定。ref 元素通常有两个属性: z bean z local bean 用于确定不在同一个 XML 配置文件中的 bean。而 local 用于确定同一个 XML 配置 文件中的其他 bean,并且 local 属性值只能是其他 bean 的 id 属性。 local 属性让 Spring 在解析 XML 时,验证 bean 的名称。 4.3.4.3 bean 元素 bean 元素用来定义 bean 实例属性是个嵌套 bean,而不是在 Spring 容器中已经存在的 be an。嵌套 bean 只对它的外部 bean 有效,因此嵌套 bean 没有 id 属性。修改上面的配置文件, 使之变成嵌套 bean 的形式,示例如下: 嵌套 bean 的配置形式,保证嵌套 bean 不能被容器访问,因此不用担心其他程序修改嵌 套 bean。外部 bean 的用法与之前的用法完全一样,使用结果也没有区别。 嵌套 bean 损失了部分性,提高了程序的内聚性。 Java 框架 - 240 - 翰子昂系列理论教材 4.3.4.4 list,set,map 以及 props 元素 list,set,map 和 props 元素分别用来设置类型为 List,Set,Map 和 Properties 的属性值。 分别用来为 bean 传入集合值。看如下示例代码: import java.util.*; public class Programmer implements Employee { //下面是系列集合属性,分别表示此人的学校,成绩,健康和电脑 private List schools=new ArrayList(); private Map score=new HashMap(); private Properties health=new Properties(); private Set Computer=new HashSet(); public Programmer() { System.out.println("Spring 实例化主调 bean:Programmer 实例..."); } public List getSchools() { return schools; } public void setSchools(List schools) { this.schools = schools; } public Map getScore() { return score; } public void setScore(Map score) { this.score = score; } public Properties getHealth() { return health; } public void setHealth(Properties health) { this.health = health; } public Set getComputer() { 专题三 Spring 翰子昂系列理论教材 - 241 - return computer; } public void setComputer(Set computer) { this.computer = computer; } public void test() { System.out.println(schools); System.out.println(score); System.out.println(health); System.out.println(computer); } } 下面是 Spring 的配置文件: 小学 中学 大学 87 Java 框架 - 242 - 翰子昂系列理论教材 89 82 正常 175 字符串电脑 如上配置文件所示:set 元素的值可以通过 value,bean,ref 确定值。map 元素 entry 的值、 set 元素的值都可以使用如下元素: z value:确定基本数据类型值,或字符串类型值。 z local:确定另一个 bean 为属性值。 z bean:确定一个嵌套 bean 为属性值。 z list,set,map 以及 props:确定集合值为属性值。 4.4.5 高级依赖注入 前面介绍的依赖注入,要么是 JavaBean 式的值,要么是直接依赖于其他 bean。实际应用 当中,某个实例的属性可能是某个方法的返回值、类的 field 值,或是属性值。这种常见的注 入方式,Spring 同样提供支持。Spring 甚至支持将 bean 实例的属性值、方法返回值、field 值, 专题三 Spring 翰子昂系列理论教材 - 243 - 直接赋给一个变量。 4.4.5.1 属性值的依赖注入 属性值的注入,是通过 PropertyPathFactoryBean 类完成的。PropertyPathFactoryBean 用来 获得目标 bean 的属性值。获得的值可注入其他 bean,也可以直接定义成新的 bean。看如下配 置文件: 12 主程序程序如下: Public static void main(String args []) { //因为是独立的应用程序,显示地实例化 Spring 的上下文 ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml"); //打印出 teacher 的 age 值 System.out.println(((Teacher)context.getBean("teacher")) .getAge()); } 执行结果如下: 12 Java 框架 - 244 - 翰子昂系列理论教材 teacher 这个 bean 的 age 属性来自于 programmer bean 的 age 属性。一个 bean 实例的属性, 可以注入另一个 bean。bean 实例的属性值可以直接定义成 bean 实例,也可以通过 Property- pathFactoryBean 完成的。对上面的配置文件增加如下代码: programmer age 主程序部分增加如下输出: System.out.println(context.getBean("age")); 此语句的执行效果如下: 12 使用 PropertyPathFactoryBean 必须指定如下两个属性: z tragetBeanName:用于指定目标 bean,确定获取哪个 bean 的属性值。 z propertyPath:用于指定属性,确定获取目标 bean 的哪个属性值。 4.4.5.2 field 值的依赖注入 field 值的注入,是通过 FieldRetrievingFactoryBean 类完成的。FieldRetrievingFactoryBean 用来获得目标 bean 的 field 值。获得的值可注入其他 bean,也可以直接定义成新的 bean。看 如下配置文件。 专题三 Spring 翰子昂系列理论教材 - 245 - Age 类代码如下: Public class Age { public static final int age=23; } 主程序如下: Public class BeanTest { //主方法,程序入口 public static void main(String args []) { //因为是独立的应用程序,显示的实例化 Spring 的上下文 ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml"); //打印出 teacher 的 age 值 System.out.println(((Teacher)context.getBean("teacher")).getAge()); } } 程序的执行结果如下: 23 field 值也可以配置成 bean 实例。在配置文件中增加如下一段: Age.age 主程序部分增加如下输出: System.out.println(context.getBean("age")); 此语句的执行效果如下: 23 4.4.5.3 方法返回值的依赖注入 方法返回值的注入是通过 MethodInvokingFacotyBean 类完成的。MethodInvokingFacoty- Java 框架 - 246 - 翰子昂系列理论教材 Bean 用来获得某个方法的返回值,该方法即可以是静态方法,也可以是实例方法。方法的返 回值可以注入 bean 实例属性,也可以直接定义成 bean 实例。看如下配置文件: getValue 下面给出 axe 的代码部分。axe 包含一个实例方法,返回整型值,代码如下: Public class Age { public int getValue() { return 23; } public static int getStaticValue() { return 15; } } 主程序如下: Public class BeanTest { //主方法,程序入口 public static void main(String args []) { 专题三 Spring 翰子昂系列理论教材 - 247 - //因为是独立的应用程序,显示的实例化 Spring 的上下文 ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml"); //打印出 teacher 的 age 值 System.out.println(((Teacher)context.getBean("teacher")).getAge()); } } 执行结果如下: 23 bean teacher 的 age 属性值来自于 age 类的实例方法返回值。使用 bean 实例的方法返回值 注入,通过 MethodInvokingFactoryBean 完成,必须指定如下两个属性。 z targetObject:确定目标 bean,该 bean 可以是容器中已有的 bean,也可以是嵌套 bea n。 z targetMethod:确定目标方法,确定通过目标 bean 的那个方法返回值注入。 如果使用静态方法返回值注入,则无须指定 targetObject,但需要指定目标的 class,指定目 标 class 的属性通过通过 targetClass 属性。使用静态方法注入,需指定如下两个属性。 z targetClass:确定目标 class z targetMethod:确定目标方法,确定通过目标 bean 的那个方法返回值注入。 在配置文件中增加如下一段: Age getStaticValue 主程序中增加如下输出: //打印出 anotherteacher 的 age 值 System.out.println(((Teacher)context.getBean("anotherteacher")).getAge()); 程序执行结果如下: Java 框架 - 248 - 翰子昂系列理论教材 15 当然,也可以将方法返回值直接定义成 bean,配置文件中增加如下一段: getValue 主程序部分增加如下输出: System.out.println(context.getBean("agevalue")); 此语句的执行效果如下: 23 5. 试验 按照上课的顺序依次练习,主要掌握: 1. 建立 HelloWorld 工程,添加 Spring 的支持。(10 分钟) 2. 编写 HelloWorldBean,BeanTest 和 applicationContext.xml 配置文件并运行程序。 (2 0 分钟) 3. 建立 EmployeeManager 工程,添加 Spring 的支持。(10 分钟) 4. 编写 Computer 接口和 NotebookPC 类。(10 分钟) 5. 编写 Employee 接口、Age 和 Programmer 类。(15 分钟) 6. 编写 TestBean 类和 applicationContext.xml 配置文件并运行程序。(25 分钟) 6. 作业 利用 Spring IoC 实现员工管理程序。 专题三 Spring 翰子昂系列理论教材 - 249 - 案例二 Spring AOP 基础 1. 教学目标 1.1 AOP 编程 2. 工作任务 使用 Spring AOP 打印程序日志。 3. 相关实践知识 使用 Spring AOP 打印程序日志 本示例我们操作 mybbs 数据库中的 userinfo 表,模拟增删改论坛用户,并在控制台打印 出操作日志。 1. 首先建立一个 Web 项目 UserInfoManager,添加 Spring 支持。 图 3-11 建立 Spring 工程 Java 框架 - 250 - 翰子昂系列理论教材 图 3-12 添加完 Spring 支持的 UserInfoManager 工程 2. 新建 com.pojo 包,并在包下建立 Userinfo.java 类和 Userinfo.hbm.xml 配置文件。 package com.pojo; public class Userinfo implements java.io.Serializable { //用户 id private Integer userid; //用户名 private String username; //密码 private String userpwd; //密码找回问题 private String userques; //密码找回答案 private String userans; //积分 private Integer integral; //等级 private String grade; //email private String useremail; //性别 private Byte sex; public Userinfo() { } public Userinfo(Integer userid, String username, String userpwd, String userques, String userans, Integer integral, String grade, String useremail, Byte sex) { this.userid = userid; this.username = username; this.userpwd = userpwd; 专题三 Spring 翰子昂系列理论教材 - 251 - this.userques = userques; this.userans = userans; this.integral = integral; this.grade = grade; this.useremail = useremail; this.sex = sex; } public Integer getUserid() { return this.userid; } public void setUserid(Integer userid) { this.userid = userid; } public String getUsername() { return this.username; } public void setUsername(String username) { this.username = username; } public String getUserpwd() { return this.userpwd; } public void setUserpwd(String userpwd) { this.userpwd = userpwd; } public String getUserques() { return this.userques; } public void setUserques(String userques) { this.userques = userques; } public String getUserans() { return this.userans; Java 框架 - 252 - 翰子昂系列理论教材 } public void setUserans(String userans) { this.userans = userans; } public Integer getIntegral() { return this.integral; } public void setIntegral(Integer integral) { this.integral = integral; } public String getGrade() { return this.grade; } public void setGrade(String grade) { this.grade = grade; } public String getUseremail() { return this.useremail; } public void setUseremail(String useremail) { this.useremail = useremail; } public Byte getSex() { return this.sex; } public void setSex(Byte sex) { this.sex = sex; } } 3. 新建 com.dao 包,并在包下建立 UserinfoDao.java 用以操作数据库。 package com.dao; import org.hibernate.Session; import org.hibernate.cfg.Configuration; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import com.pojo.Userinfo; import org.hibernate.Query; Java 框架 - 254 - 翰子昂系列理论教材 public class UserInfoDao { //定义 session 用以操作数据库 private Session session; public UserInfoDao() { //打开一个 session 连接 Configuration config=new Configuration().configure(); SessionFactory factory=config.buildSessionFactory(); session=factory.openSession(); } //添加一个用户 public void add(Userinfo userinfo) { try { Transaction tra=session.beginTransaction(); session.save(userinfo); tra.commit(); }catch(Exception e) { e.printStackTrace(); } } //修改一个用户 public void update(Userinfo userinfo) { try { Transaction tra=session.beginTransaction(); session.update(userinfo); tra.commit(); }catch(Exception e) { e.printStackTrace(); } } //查询一个用户 public Userinfo select(int id) { Userinfo user=null; 专题三 Spring 翰子昂系列理论教材 - 255 - try { Transaction tra=session.beginTransaction(); user=(Userinfo)session.load(Userinfo.class, id); tra.commit(); }catch(Exception e) { e.printStackTrace(); } return user; } //删除一个用户 public void delete(int id) { try { Transaction tra=session.beginTransaction(); Query query=session.createQuery("delete Userinfo as u where u.userid=?"); query.setInteger(0, id); query.executeUpdate(); tra.commit(); }catch(Exception e) { e.printStackTrace(); } } } 4. 新建 com.serivce 包,在包下建立 UserServiceInterfaces.java 接口和 UserService.java 类 封装业务逻辑。 package com.service; import com.pojo.Userinfo; public interface UserServiceInterface { //添加用户 public void addUser(Userinfo user); //修改用户 public void updateUser(Userinfo user); //查询用户 public Userinfo getUser(int id); //删除用户 Java 框架 - 256 - 翰子昂系列理论教材 public void deleteUser(int id); } package com.service; import com.dao.UserInfoDao; import com.pojo.Userinfo; public class UserService implements UserServiceInterface{ private UserInfoDao userInfoDao=null; public UserService() { userInfoDao=new UserInfoDao(); } //添加用户 public void addUser(Userinfo user) { userInfoDao.add(user); } //修改用户 public void updateUser(Userinfo user) { userInfoDao.update(user); } //查询用户 public Userinfo getUser(int id) { Userinfo user=userInfoDao.select(id); return user; } //删除用户 public void deleteUser(int id) { userInfoDao.delete(id); } } 5. 根目录下建立 MyBeforeAdvisor.java 类,用于打印日志。 import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; //before 处理实现类 public class MyBeforeAdvisor implements MethodBeforeAdvice { //实现 MethodBeforeAdvice 接口必须实现的方法,该方法在连接点之前执行 专题三 Spring 翰子昂系列理论教材 - 257 - public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { if(arg0.getName().equals("addUser")) { System.out.println("添加了一个用户"); } else if(arg0.getName().equals("updateUser")) { System.out.println("更新了一个用户"); } else if(arg0.getName().equals("getUser")) { System.out.println("查询了一个用户"); } else if(arg0.getName().equals("deleteUser")) { System.out.println("删除了一个用户"); } } } 6. 编写 Spring 的配置文件 applicationContext.xml。 com.service.UserServiceInterface Java 框架 - 258 - 翰子昂系列理论教材 myBeforeAdvisor 7. 编写 Hibernate 的配置文件 hibernate.cfg.xml。 root jdbc:mysql://localhost:3306/mybbs org.hibernate.dialect.MySQLDialect MySQL sa com.mysql.jdbc.Driver 8. 编写测试程序。 import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.pojo.Userinfo; import com.service.UserServiceInterface; public class TestBean { public static void main(String args []) { ApplicationContext context=new 专题三 Spring 翰子昂系列理论教材 - 259 - ClassPathXmlApplicationContext("applicationContext.xml"); //拿到代理对象 UserServiceInterface userService=(UserServiceInterface)context.getBean("user"); //生成一则用户信息 Userinfo user=new Userinfo(); user.setUserid(11); user.setUsername("scott"); user.setUserpwd("soctt"); user.setUserques("how old are you"); user.setUserans("32"); user.setUseremail("hao123@.com"); user.setIntegral(50); user.setGrade("初级用户"); user.setSex((byte)1); //添加一个用户 userService.addUser(user); //查询用户 user=userService.getUser(11); //修改用户 user.setUsername("anthorscott"); userService.updateUser(user); //删除用户 userService.deleteUser(11); } } 9. 运行测试程序会在控制台打印: 添加了一个用户 查询了一个用户 更新了一个用户 删除了一个用户 4. 相关理论知识 4.1 Spring AOP 简介 AOP 是 OOP 的延续,是 Aspect Oriented Programming 的缩写,意思是面向切面编程。 AOP 实际是 GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦, AOP 可以说也是这种目标的一种实现。 面向切面编程并不会取代面向对象编程,而是作为面向对象编程的补充。AOP 从动态的 Java 框架 - 260 - 翰子昂系列理论教材 角度考虑程序的结构,从而使 OOP 更加完善。面向对象编程将程序分解成各个层次的对象, 而面向切面编程将程序运行过程分解成各个切面。 AOP 从程序动态运行角度考虑程序的结构,提取业务处理过程的切面。AOP 面对程序运 行中各个步骤,以降低各步骤之间的耦合,从而提高步骤之间的隔离。 OOP 是静态的抽象,它对应用中的实体及属性、行为进行抽象,从而获得清晰高效的单 元划分;而 AOP 是动态的抽象,它对应用的执行过程的步骤进行抽象,从而获得步骤之间的 逻辑划分。AOP 框架并不与特定的代码耦合,能处理程序执行中的特定点,而不是某个具体 的程序。AOP 框架具有以下两个特征: z 各步骤之间的良好隔离。 z 源代码无关性。 AOP 框架是 Spring 的一个关键组件。AOP 框架是 Spring IoC 的完善和补充,使之成为 更加有效的中间件。当然,IoC 容器(BeanFactory 和 ApplicationContext)并不依赖于 AOP 框架,因此,AOP 框架并不是必须的。 Spring AOP 的目标是提供与 Spring IoC 容器紧密结合的 AOP 框架,Spring AOP 并不致 力于提供完善的 AOP 实现,而是以“实用主义”为目标,提供 AOP 框架与 IoC 容器的完美 结合。 4.2 AOP 中的概念 下面是关于 AOP 一些术语解释。 1. 切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。事务管理 是 J2EE 应用中一个关于横切关注点的很好的例子。 2. 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出。S pring AOP 中连节点总是方法的调用,Spring 并没有显示地使用连接点。 3. 处理(Porcess):AOP 框架在特定的的连接点执行的动作。处理包括“around”、“b efore”、“throws”等类型。大部分框架都以拦截器作为处理模型。 4. 切入点(Pointcut):系列连接点的集合,它确定处理触发的时机。AOP 框架允许开 发者自己定义切入点:例如使用正则表达式。 5. 引入(Introduction):添加方法或字段到被处理的类。Spring 允许引入新的接口到任 何被处理的对象。例如,可以使用一个引入,使任何对象实现 IsModified 接口,以此来简化 缓存的应用。 6. 目标对象(Target Object):包含连接点的对象,也称为被处理对象或者被代理对象。 7. AOP 代理(AOP Proxy):AOP 框架创建的对象,包含处理。Spring 中的 AOP 代理 可以是 JDK 动态代理,也可以是 CGLIB 代理。前者为实现接口的目标对象代理,后者为不 实现接口的目标对象代理。 4.3 AOP 代理 AOP 代理是 AOP 框架创建的对象。通常,AOP 代理可以作为目标对象的替代品,而 A OP 代理提供比目标对象更加强大的功能。一个典型的例子就是 Spring 中的事务代理 bean。 通常,目标 bean 的方法不是事务性的,而 AOP 代理包含目标 bean 的全部方法,而且这些方 专题三 Spring 翰子昂系列理论教材 - 261 - 法经过加强,变成了事务性方法。简单的说,目标对象是蓝本,AOP 代理是目标对象的加强, 在目标对象的基础上,增加属性和方法,提供更强大的功能。 目标对象包含系列的切入点。切入点是可以触发处理连接点的集合。用户可以自定义切 入点,比如使用正则表达式。AOP 代理包装目标对象,在切入点加入的处理,使目标对象的 方法功能更强。 Spring 默认使用 JDK 动态代理实现 AOP 代理,主要用于代理接口,也可以使用 CGLIB 代理实现。实现类的代理,而不是接口。如果业务对象没有实现接口,默认使用 CGLIB 代理。 面向接口编程是良好的习惯,尽量不要面向具体的类编程。因此,业务对象通常应实现一个 或多个接口。下面是一个动态代理模式的示例,首先有个 Employee 的接口,接口如下: //定义 Employee 接口 public interface Employee { //info 方法声明 public void info(); //codeing 方法声明 public void codeing(); } 然后给出该接口的实现的实现类,实现类必须实现两个方法,代码如下: public class Programmer implements Employee { //info 方法实现,仅仅打印一个字符串 public void info() { System.out.println("我是一名程序员"); } //codeing 方法实现,仅仅打印一个字符串 public void codeing() { System.out.println("我快乐的敲打着键盘"); } } 上面的代码没有丝毫独特之处,是典型的面向接口编程的模型,为了更好的解耦,采用 工厂来创建 Programmer 实例。工厂源代码如下: public class ProgrammerFactory { //工厂本身是单态模式,因此将 ProgrammerFactory 作为静态成员变量保存 private static ProgrammerFactory instance; //将 Programmer 实例缓存 private Programmer programmer; //默认的构造函数,单态模式需要构造器是 private Java 框架 - 262 - 翰子昂系列理论教材 private ProgrammerFactory() {} //单态模式所需的静态方法,该方法是创建本类实例的唯一方法点 public static ProgrammerFactory getInstance() { if(instance==null) { instance=new ProgrammerFactory(); } return instance; } //获得 Programmer 类的实例 public Employee getProgrammer(String employeeName) { //根据字符串参数决定返回的实例 if(employeeName.equalsIgnoreCase("programmer")) { //返回 Programmer 实例前,先判断缓存的 Programmer 是否存在,如果不存在创建, //否则直接返回缓存的 Programmer 实例 if(programmer==null) { programmer=new Programmer(); } return programmer; } return null; } } 下面是一个普通的处理类。该处理类没有与任何特定的类耦合,它可以处理所有的目标 对象。从 JDK1.3 起,Java 的 import java.lang.reflect 下增加 InvocationHandler 接口,该接口是 所有处理类的根接口。该处理类的源代码如下: import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class ProxyHandler implements InvocationHandler { //需被代理的对象 private Object target; //执行代理的目标方法时,该 invoke 方法会被自动调用 public Object invoke(Object proxy,Method method,Object [] args) throws Exception { 专题三 Spring 翰子昂系列理论教材 - 263 - Object result=null; if(method.getName().equals("info")) { System.out.println("事务开始"); result=method.invoke(target, args); System.out.println("事务提交"); } else { result=method.invoke(target, args); } return result; } //通过该方法,设置目标对象 public void setTarget(Object o) { this.target=o; } } 该处理类实现了 InvocationHandler 接口,实现该接口必须实现 invoke(Object proxy,Meth od method,Object [] args)方法,程序调用代理的目标方法,自动变成调用 invoke 方法。 该处理类并未与任何接口或类耦合,它完全是通用的,它的目标实例是 Object 类型的, 可以是任何类型。在 invoke 方法内,对目标方法对象的 info 方法进行简单加强,在开始执行 目标方法之前,先打印事务开始,执行方法之后,打印事务提交。 通过 method 对象的 invoke 方法,可以完成目标对象的方法调用,执行代码如下: result=method.invoke(target, args); 下面是代理工厂: import java.lang.reflect.Proxy; public class ProxyFactory { public static Object getProxy(Object object) { //代理的处理类 ProxyHandler handler=new ProxyHandler(); //把该 programmer 类的实例委托给代理操作 handler.setTarget(object); //第一个参数用来创建动态代理的 ClassLoader 对象,只要该对象能访问 Employee 接口 即可 //第二个参数是接口数组,正是代理该接口数组 Java 框架 - 264 - 翰子昂系列理论教材 //第三个参数是代理包含的处理实例 return Proxy.newProxyInstance(Programmer.class.getClassLoader(), object.getClass().getInterfaces(), handler); } } 代理工厂里有行代码: Proxy.newProxyInstance(Programmer.class.getClassLoader(), object.getClass().getInterfaces(), handler); Proxy.newProxyInstance 方法根据接口数组动态创建代理类的实例,接口数组通过 object.getClass().getInterfaces()方法获得,创建的代理类是 JVM 在内存中动态创建,该类实现 传入的接口数组的全部接口。 Dynamic Proxy 要求被代理的必须是接口的实现类,否则无法为其构造相应的动态类。 因此,Spring 对接口实现类采用 Dynamic Proxy 实现 AOP,而对没有实现任何接口的类,则 通过 CGLIB 实现 AOP 代理。下面是主程序: public class BeanTest { //主方法,程序入口 public static void main(String args []) { Employee employee=null; //创建 Employee 实例,该实例将被作为代理对象 Employee targetObject=EmployeeFactory.getInstance().getEmployee("programmer"); //以目标对象创建代理 Object proxy=ProxyFactory.getProxy(targetObject); if(proxy instanceof Employee) { employee=(Employee)proxy; } //测试代理方法 employee.info(); employee.codeing(); } } 代理实例会实现目标对象实现的全部接口。因此,代理实例也实现了 Employee 接口,程 序运行结果如下: 事务开始 我是一名程序员 事务提交 专题三 Spring 翰子昂系列理论教材 - 265 - 我快乐的敲打着键盘 代理实例加强了目标对象的方法仅仅打印两行字符串。当然,此种加强没有意义。试想 一下程序中打印字符串的地方,换成真实的事务开始,事务提交,则代理实例的方法为目标 对象的方法增加了事务性。 4.4 Spring 对 AOP 的支持 Spring AOP 采用纯 Java 实现,因此不需要额外编译。Spring AOP 不需要控制 Class-Loa der 层次,因此适用于 J2EE Web 容器或应用服务器。Spring 目前支持拦截方法调用。成员变 量拦截器没有实现。 Spring AOP 是对 Spring IoC 的补充,为 Spring 的声明式事务管理提供基础,或者使用 Spring AOP 框架的全部功能来实现自定义切面。Spring AOP 并不想提供完善的 AOP 实现, 而是结合 Spring IoC 容器,提供实用的 AOP 实现,从而解决企业应用开发中的常见问题。 因此,Spring AOP 框架通常和 Spring IoC 容器整合使用。AOP 处理采用普通 bean 法来 定义,处理和切入点由 Spring IoC 容器管理,这是和其他 AOP 实现的重要区别。 Spring 提供切入点和处理类型抽象的大量类和接口,使用术语 Advisor 来表示代表切面的 对象,它包含一个处理和一个特定连接点的切入点。 各种处理类型有 MethodInterceptor,org.springframework.aop 中的处理接口。MethodInter ceptor 接口来自 AOP 联盟,org.aoppalliance.aop.Advice 标签接口是所有处理的根接口。Sprin g 还包括如下的处理类: z MethodInterceptor z ThrowsAdvice z BeforeAdvice z AfterReturningAdvice 同一个处理,可以处理多个目标对象,这种处理成为 per-class 处理;也可以每个目标对 象都有自己的处理,这种处理成为 per-instance 处理。 per-class 处理在应用中广泛的使用。它适合于通用处理,例如事务 Advistor。per-class 处 理不关心目标对象的状态,也不会修改目标对象的状态,这种处理仅仅对方法和方法参数起 作用,per-instance 处理关心目标对象的状态,处理可能改变目标对象的状态。per-class 处理 和 per-instance 处理,可以在同一个 AOP 代理中混合使用。Spring 中实用的处理类型有如下 5 种: z Around 处理 z Before 处理 z Throws 处理 z After Returing 处理 z Introduction 处理 4.4.1 Around 处理 Around 处理是 Spring 中最基本的处理类型。Spring 的 Interceptor Around 处理和 AOP 联 Java 框架 - 266 - 翰子昂系列理论教材 盟兼容。实现 Around 处理需要实现 MethodInterceptor 接口。MethodInterceptor 由 AOP 联盟 提供,MethodInterceptor 接口的源代码如下: public interface MethodInterceptor extends Interceptor{ Object invoke(MethodInvocation invocation) throws Throwable; } 程序调用代理实例的方法,invoke 方法完成实际的方法体。该方法包含 MethodInvocatio n 参数,MethodInvocation 也是 AOP 联盟提供的。该参数当中包含被调用的方法、目标连接 点、AOP 代理和调用方法的参数,通过 MethodInvocation 可以执行目标方法。invoke()方法返 回调用的结果,即连接点的返回值。下面是一个简单的 Around 处理源代码: import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; //创建 Around 处理应该实现 MethodInterceptor 接口 public class MyAroundInterceptor implements MethodInterceptor{ //实现 MethodInterceptor 接口必须实现的方法,该方法处理完成的动作 public Object invoke(MethodInvocation invocation) throws Throwable { //执行目标方法之前的方法 System.out.println("调用方法前:invocation 对象["+invocation+"]"); //执行目标方法 Object rval=invocation.proceed(); //执行目标方法后的处理 System.out.println("调用结束...."); return rval; } } 目标类和所实现的接口如下: //目标类所实现的接口 public interface Person { //info 方法声明 void info(); } //目标类,实现接口 public class PersonImpl implements Person { //两个成员属性 private String name; private int age; //name 属性依赖注入所需的 setter 方法 专题三 Spring 翰子昂系列理论教材 - 267 - public void setName(String name) { this.name = name; } //age 属性依赖注入所需的 setter 方法 public void setAge(int age) { this.age = age; } //测试用的 info 方法,该方法仅仅打印 name 属性和 age 属性的值 public void info() { System.out.println("我的名字是:"+name+",我今年的年龄为:"+age); } } 下面是完整的配置文件(配置文件配置了 bean、处理 bean、代理工厂 bean、Spring Ioc 容器负责管理 bean 之间的依赖关系): 李明 51 Person Java 框架 - 268 - 翰子昂系列理论教材 myAroundInterceptor 注意工厂 bean 的配置,工厂 bean 采用 ProxyFactoryBean。如果程序中请求获得工厂 bea n ID,实际将不会获得 bean 的实例,而是获得工厂 bean 的 getObject()返回对象。ProxyFacto ryBean 工厂 bean 将返回目标对象的 AOP 代理,主程序如下: import org.springframework.context.ApplicationContext; import org.springframework.context.support. ClassPathXmlApplicationContext; public class BeanTest { public static void main(String args []) { //创建 Spring 容器实例 ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml"); //获取 AOP 代理实例 Person p=(Person)context.getBean("person"); //执行 AOP 代理方法 p.info(); } } 主程序没有太多的变化,与之前的区别在于:原来直接请求目标 bean ID;使用 AOP 代 理后,改为请求代理工厂 bean ID,IoC 不会返回工厂实例,而是返回工厂生成的代理实例, 程序的执行结果如下: 调用方法前:invocation 对象[ReflectiveMethodInvocation: public abstract void Person.inf o(); target is of class [PersonImpl]] 我的名字是:李明,我今年的年龄为:51 调用结束.... 将该示例的执行结果与 4.3 节的示例的执行结果相比,效果相似。代理在调用方法之前, 调用方法之后,加入了处理。这种处理,可以是相当简单的,也可以是相当复杂。比如增加 事务管理,或执行权限检查。 4.4.2 Before 处理 Before 处理是一种简单的处理,这种处理不需要 MethodInvocation 对象,因为它仅在方 专题三 Spring 翰子昂系列理论教材 - 269 - 法调用之前执行处理,而目标方法的继续执行无须处理关心。 Before 处理不需要调用 Proceed()方法,不会产生忘记调用目标方法的错误,不会停止 拦截器链的继续执行。实现 Before 处理必须实现 MethodBeforeAdvice 接口,该接口的源代码 如下: //before 处理实现类 public interface MethodBeforeAdvice extends BeforeAdvice { //该方法在连接点之前被执行 void before(Method m,Object[] args,Object target) throws Throwable; } before 方法的返回值是 void,意味着 Before 处理在连接点之前执行处理,但不能改变方 法的返回值。如果 Before 处理抛出异常,拦截器链的执行被中断,该异常将沿着拦截器链向 上传播。如果异常是 unchecked 的,或者出现在目标方法声明中,异常直接返回给客户端; 否则,异常由 AOP 代理包装成 unchecked。Before 处理可以被用于任何类型的切入点。下面 是一个简单的 Before 处理实现类: import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; //before 处理实现类 public class MyBeforeAdvisor implements MethodBeforeAdvice { //实现 MethodBeforeAdvice 接口必须实现的方法,该方法在连接点之前执行 public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("方法调用前..."); System.out.println("下面是方法调用的信息:"); System.out.println("所执行的方法是:"+arg0); System.out.println("调用方法的参数:"+arg1); System.out.println("目标对象是:"+arg2); } } 程序所有的接口和目标类,与之前的实例非常相似: //目标类所实现的接口 public interface Person { //run 方法声明 void run(); } //目标类,实现接口 public class PersonImpl implements Person { //两个成员属性 Java 框架 - 270 - 翰子昂系列理论教材 private String name; private int age; //name 属性依赖注入所需的 setter 方法 public void setName(String name) { this.name = name; } //age 属性依赖注入所需的 setter 方法 public void setAge(int age) { this.age = age; } //实现 Person 接口必须实现的 run 方法 public void run() { if(age<45) { System.out.println("我还年轻,奔跑迅速..."); } else { System.out.println("我年老体弱,只能慢跑..."); } } } 将目标 bean 和处理 bean 部署在容器中,同时也部署代理工厂 bean。完整的配置文件如 下: 李明 28 专题三 Spring 翰子昂系列理论教材 - 271 - Person myBeforeAdvisor 程序和上例主程序的程序差别不大,只是上示例中的执行 info 方法,本示例中换成执行 run 方法,此处不在给出主程序,程序运行结果如下: 方法调用前... 下面是方法调用的信息: 所执行的方法是:public abstract void Person.run() 调用方法的参数:[Ljava.lang.Object;@bad8a8 目标对象是:PersonImpl@5e13ad 我还年轻,奔跑迅速... 虽然 Before 处理无须理会目标方法的执行,但程序中依然可以获得目标方法的相关信息, 如目标方法的方法名、方法调用的参数、目标对象。 4.4.3 After Returning 处理 After Returning 处理和 Before 处理非常的相似,不需要 MethodInvocation 对象,目标方 法的继续执行无须处理关心。实现 After Returning 处理,必须实现 org.springframework.aop. AfterReturningAdvice 接口,该接口的源代码如下所示: public interface AfterReturningAdvice extends Advice { void afterReturning(Object returnvalue,Method m,Object[] args,Object target) throws Throwable; } Java 框架 - 272 - 翰子昂系列理论教材 看上面的源代码,afterReturning 方法在目标方法返回之后自动调用,afterReturning 可以 访问目标方法的返回值,但不能修改。afterReturning 也可以访问目标方法、方法参数和目标 对象。 After Returning 处理可被用于任何类型的切入点。下面是一个简单的 After Returning 处 理实现类: import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; //实现自定义处理类,必须实现 AfterReturningAdvice 接口 public class MyAfterAdvisor implements AfterReturningAdvice { //实现 AfterReturningAdvice 接口必须实现的 afterReturning 方法 public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { System.out.println("方法调用结束......"); System.out.println("目标方法的返回值是:"+arg0); System.out.println("目标方法是:"+arg1); System.out.println("目标方法的参数是:"+arg2); System.out.println("目标对象是:"+arg3); } } 程序所有的接口和目标类也非常简单: //目标类所实现的接口 public interface Person { //test 方法声明 String test(); } //目标类,实现接口 public class PersonImpl implements Person { //两个成员属性 private String name; private int age; //name 属性依赖注入所需的 setter 方法 public void setName(String name) { this.name = name; } //age 属性依赖注入所需的 setter 方法 public void setAge(int age) { this.age = age; } //实现 Person 接口必须实现的 test 方法 专题三 Spring 翰子昂系列理论教材 - 273 - public String test() { System.out.println("我的名字是:"+name+",今年的年龄为:"+age); return "我的名字是:"+name+",今年的年龄为:"+age; } } 将目标 bean 和处理 bean 部署到容器中,也部署代理工厂 bean。本实例的处理与切入点 配置在同一个 bean 之中,切入点采用正则表达式切入点配置: 李明 51 .*test.* Person Java 框架 - 274 - 翰子昂系列理论教材 testAdvisor 主程序与前面示例的主程序差别不大,本示例换成执行 test 方法,此处不在给出主程序, 程序运行结果如下: 我的名字是:李明,今年的年龄为:51 方法调用结束...... 目标方法的返回值是:我的名字是:李明,今年的年龄为:51 目标方法是:public abstract java.lang.String Person.test() 目标方法的参数是:null 目标对象是:PersonImpl@156b6b9 4.4.4 Throws 处理 当连接点抛出异常,Throws 处理被调用。实现 Throws 异常,必须实现 org.springframew ork.aop.ThrowsAdvice 接口,该接口不包含任何方法,但实现该接口必须实现如下形式的方法。 afterThrowing([Method],[args],[target],Throwable subclass) 可以实现一个或多个这样的方法。这些方法中,只有第四个参数是必需的,前三个参数 可选,取决于应用是否关心抛出异常的宿主。最后一个参数确定连接点抛出哪种异常,处理 被调用。Throws 处理可被用于任何类型的切入点。下面的示例代理中,当连接点抛出 Remot eException 异常时,该处理被调用: public class RemoteThrowsAdvice implements ThrowsAdvice { Public void afterThrowing(RemoteException ex) throws Throwable {} } 下面的示例,当连接点抛出 RemoteException 异常时,该处理被调用,与上面的处理不同 的是,该处理声明四个参数。因此,该处理可以访问被调用的方法,方法的参数和目标对象: public class RemoteThrowsAdvice implements ThrowsAdvice 专题三 Spring 翰子昂系列理论教材 - 275 - { Public void afterThrowing(Method m,Object[] args,Object target,RemoteException ex) throws Throwable {} } 一个完整的示例,该实例中的处理组合了两个 afterThrowing 方法,该处理能同时处理两 类异常。事实上,一个处理中,可以组合任意多个 afterThrowing 方法。该处理的源代码如下: import org.springframework.aop.ThrowsAdvice; import java.lang.reflect.Method; import java.lang.ArrayIndexOutOfBoundsException; //Throws 异常应该实现 ThrowsAdvice 接口 public class MyExceptionAdvice implements ThrowsAdvice { //该方法处理 ArrayIndexOutOfBoundsException 异常 public void afterThrowing(ArrayIndexOutOfBoundsException ex) throws Throwable { System.out.println("系统抛出 ArrayIndexOutOfBoundsException 异常,异常提 示为:"+ex.getMessage()); } //该方法处理 ArithmeticException 异常。 //而且访问抛出异常的方法,方法参数,目标对象 public void afterThrowing(Method m,Object[] args,Object target, ArithmeticException ex) { System.out.println(" 系统抛出 ArithmeticException 异常,异常提示为: "+ex.getMessage()); System.out.println("抛出异常的方法为:"+m); System.out.println("抛出异常的方法参数为:"+args); System.out.println("抛出异常的目标对象为:"+target); } } 程序所有的接口和目标类也非常简单: //目标类所实现的接口 public interface TestBean { void test(); void anthortest(); } //目标类,实现接口 Java 框架 - 276 - 翰子昂系列理论教材 public class TestBeanImpl implements TestBean{ //该方法抛出 ArithmeticException 异常 public void test() { int i=5/0; } //该方法抛出 ArrayIndexOutOfBoundsException 异常 public void anthortest() { Integer [] arr=new Integer[5]; for(int i=0;i<7;i++) { arr[i]=i; } } } 配置文件如下: TestBean myExceptionAdvice 专题三 Spring 翰子昂系列理论教材 - 277 - 当主程序调用 test 方法时的执行结果: 系统抛出 ArithmeticException 异常,异常提示为:/ by zero 抛出异常的方法为:public abstract void TestBean.test() 抛出异常的方法参数为:null 抛出异常的目标对象为:TestBeanImpl@b5dac4 当主程序调用 anthortest 方法时的执行结果: 系统抛出 ArrayIndexOutOfBoundsException 异常,异常提示为:5 4.4.5 Introduction 处理 Introduction 处理是一种特殊类型的拦截处理。它不能作用于任何切入点,因为它只作用 于类级别方法。实现 Introduction 处理,需要实现 IntroductionAdvisor 和 IntroductionIntercep- tor 接口。IntroductionInterceptor 接口的源代码如下: public interface IntroductionInterceptor extends MethodInterceptor { Boolean implmentsInterfaces(Class intf); } // IntroductionInterceptor 继承 AOP 联盟 MethodInterceptor 接口,接口源代码如下: public interface MethodInterceptor { public java.lang.Object invoke(MethodInvocation invocation) throws java.lang.Throwable } 实现 Introduction 处理必须实现 invoke()方法。Introduction 处理不能调用 proceed() 方法。Introduction 处理不能作用于任何切入点,通常建议使用 IntroductionAdvisor 包装该处 理。IntroductionAdvisor 的源代码如下: //包装 IntroductionInterceptor 的 IntroductionAdvisor public interface IntroductionAdvisor extends Advisor,IntroductionInfo { ClassFilter getClassFilter(): void validateInterfaces() throws IllegalArgumentException; } 其父接口 IntroductionInfo 的源代码如下: public interfaces IntroductionInfo { //该方法返回 Introduction 的接口 Class [] getInterfaces(); Java 框架 - 278 - 翰子昂系列理论教材 } getInterfaces()方法返回 Advistor 导入的接口。Introduction 处理能将目标对象转换成代 理接口,不管目标对象原本的类型,Introduction 处理后的代理能够直接调用代理接口中的方 法。如下是代理将实现的接口的源代码: public interface Lockable { //锁定目标 bean 的属性 void lock(); //解锁 void unlock(); //返回目标 bean 的属性是否被锁定 boolean locked(); } 实例程序的代理将实现该接口,目标 bean 的代理可执行该接口里的方法。一旦执行 lock 方法,目标 bean 的属性被锁定,将在控制台显示错误信息。 下面是 Introduction 处理的实现类,让其继承 DelegatingIntroductionInterceptor 类。实现 Introduction 处理可直接实现 IntroductionInterceptor 接口,但实现 DelegatingIntroductionInter- ceptor 类更简单、方便,并让其实现代理的全部接口。Introduction 处理的源代码如下: import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.DelegatingIntroductionInterceptor; //Introduction 处理,继承 DelegatingIntroductionInterceptor,实现代理接口 public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable { //locked 是个标始,该标始表示目标 bean 的属性是否可修改 //该标始会作为代理的状态,因此 Introduction 处理是代理的一部分 private boolean locked; //锁定目标 bean 的属性 public void lock() { this.locked=true; } public boolean locked() { return this.locked; } public void unlock() { 专题三 Spring 翰子昂系列理论教材 - 279 - this.locked=false; } //不能调用 peoceed 方法,必须实现 invoke 方法 public Object invoke(MethodInvocation invocation) throws Throwable { if(locked() && invocation.getMethod().getName().indexOf("set")==0) { System.out.println("属性加锁,无法修改"); } return super.invoke(invocation); } } LockMixin 继承 DelegatingIntroductionInterceptor 并实现 Lockable。通过这种方式,可以 导入任意数量的接口。通常不需要改写 invoke()方法,只需实现 DelegatingIntroductionInterce ptor。如果是导入的方法,DelegatingIntroductionInterceptor 实现委托方法,否则沿着连接点继 续处理。在示例程序中增加检查,如果属性被锁定,且客户端代码调用 setter 方法,则打印错 误。 Introduction Advistor 必须有一个独立的 LockMixin 实例,并指定导入的接口 Lockable。 本示例中通过 new 直接创建实例。实际应用中,通过 IoC 容器为其注入 Introduction 拦截器。 IntroductionAdvisor 的源代码如下: import org.springframework.aop.support.DefaultIntroductionAdvisor; public class LockMixinAdvisor extends DefaultIntroductionAdvisor { public LockMixinAdvisor() { super(new LockMixin(),Lockable.class); } } 将 Introduction Advisor 部署子容器中,不需要额外的属性注入。Advistor 必须针对每个实 例,并且是有状态的。实例代码中,每个目标 bean 都有对应的 LockMixinAdvisor,Advisor 是目标对象的一部分,因此该 Advisor 应部署成 prototype。 下面是完整的配置文件: Java 框架 - 280 - 翰子昂系列理论教材 李明 51 Person lockMixinAdvisor 程序所有的接口和目标类也非常简单: //目标类所实现的接口 public interface Person { //test 方法声明 String test(); void setAge(int age); } //目标类,实现接口 public class PersonImpl implements Person { //两个成员属性 private String name; private int age; //name 属性依赖注入所需的 setter 方法 专题三 Spring 翰子昂系列理论教材 - 281 - public void setName(String name) { this.name = name; } //age 属性依赖注入所需的 setter 方法 public void setAge(int age) { this.age = age; } //实现 Person 接口必须实现的 test 方法 public String test() { System.out.println("我的名字是:"+name+",今年的年龄为:"+age); return "我的名字是:"+name+",今年的年龄为:"+age; } } 主程序中,让代理执行 lock()方法锁定目标 bean 的属性,然后调用 setter 方法修改属性。 主程序源代码如下: import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class BeanTest { public static void main(String args []) { //创建 Spring 容器实例 ApplicationContext context= new ClassPathXmlApplicationContext("applicationContext.xml"); //获取 AOP 代理实例 Person p=(Person)context.getBean("person"); //执行 AOP 代理方法 p.test(); //修改代理 bean 的属性,此时并未调用 lock 方法,修改成功 p.setAge(45); p.test(); //将代理强制转型成 Lockable Lockable lp=(Lockable)p; //加锁 lp.lock(); //修改属性,会打印错误信息 p.setAge(34); } } 程序执行结果: Java 框架 - 282 - 翰子昂系列理论教材 我的名字是:李明,今年的年龄为:51 我的名字是:李明,今年的年龄为:45 属性加锁,无法修改 4.4.6 Advistor Advistor 等于切入点加处理。Advistor 是切面的模块化表示。除了 Introduction 处理,Ad visor 可被用于任何处理。DefaultPointcutAdvisor 是最常用的 Advistor。例如,它可以和 Me- thodInterceptor,BeforeAdvice 和 ThrowsAdvice 一起使用。Spring 可在一个 AOP 代理中混合使 用 Advistor 和处理,Spring 将自动创建拦截器链。 下面示例中,有两个 Advice bean,一个 Advisor bean。Spring 创建拦截器链,拦截器的 源代码就是上面示例中的 Around 处理、Before 处理、After 处理的源代码,此处不在给出。 目标对象的类和接口如下 : //目标类所实现的接口 public interface Person { //info 方法声明 void info(); //run 方法声明 void run(); } //目标类,实现接口 public class PersonImpl implements Person { //两个成员属性 private String name; private int age; //name 属性依赖注入所需的 setter 方法 public void setName(String name) { this.name = name; } //age 属性依赖注入所需的 setter 方法 public void setAge(int age) { this.age = age; } //实现 Person 接口必须实现的 info 方法 public void info() { System.out.println("我的名字是:"+name+",今年的年龄为:"+age); } //实现 Person 接口必须实现的 run 方法 public void run() 专题三 Spring 翰子昂系列理论教材 - 283 - { if(age>45) { System.out.println("我还轻,奔跑迅速"); } else { System.out.println("我年老体弱,只能慢跑"); } } } 完整的配置文件如下(配置文件的代理工厂里,同时配置了三个拦截器,三个拦截器形 成拦截链): 李明 51 Java 框架 - 284 - 翰子昂系列理论教材 .*run.* Person myAroundInterceptor myBeforeAdvisor testAdvisor 拦截器的定义顺序很重要。拦截器链前面的处理先被调用,链后面的处理后被调用。看 程序运行结果: 调用方法前:invocation 对象[ReflectiveMethodInvocation: public abstract void Person.run(); target is of class [PersonImpl]] 方法调用前... 下面是方法调用的信息: 所执行的方法是:public abstract void Person.run() 掉用方法的参数:[Ljava.lang.Object;@1977b9b 目标对象是:PersonImpl@195d4fe 我还轻,奔跑迅速 方法调用结束...... 目标方法的返回值是:null 目标方法是:public abstract void Person.run() 目标方法的参数是:[Ljava.lang.Object;@340101 目标对象是:PersonImpl@195d4fe 专题三 Spring 翰子昂系列理论教材 - 285 - 调用结束.... 执行结果是目标方法执行之前:Around 处理先执行,Before 处理后执行;目标方法执行 之后:先执行正则表达式 after 处理,后执行 Around 处理。 5. 试验 按照上课的顺序依次练习,主要掌握: 1. 建立 UserInfoManager 工程并同时添加 Hibernate 和 Spring 支持。(10 分钟) 2. 编写 Userinfo.java 类和 Userinfo.hbm.xml 配置文件。(15 分钟) 3. 编写 UserinfoDao.java。(10 分钟) 4. 编写 UserServiceInterfaces.java 接口和 UserService.java 类。(15 分钟) 5. 编写 MyBeforeAdvisor.java 类。(10 分钟) 6. 编写 applicationContext.xml 和 hibernate.cfg.xml 配置文件。(20 分钟) 7. 编写主程序,并调试运行程序。(10 分钟) 6. 作业 利用 Struts aop 打印程序日志。 Java 框架 - 286 - 翰子昂系列理论教材 专题四 SSH 整合 翰子昂系列理论教材 - 287 - 专题四 SSH 整合 1. 教学目标 1.1 Spring 与 Hibernate 整合 1.2 Spring 与 Struts 整合 2. 工作任务 SSH 整合实现论坛登录 3. 相关实践知识 SSH 整合实现论坛登录 1. 首先建立一个 Web 项目,命名 SSHLogin。 图 4-1 建立 Web 工程 2. 然后在项目中加入 Struts 支持。 Java 框架 - 288 - 翰子昂系列理论教材 图 4-2 添加 Struts 支持 3. 添加 Spring 支持,右键选中工程【MyEclipse】Î【Add Springcapabilities】,选中 四个 Libraries,分别是 Spring 2.0 AOP Libraries、Spring 2.0 Core Libraries、Spring 2.0 Per sistence Libraries、Spring 2.0 Web Libraries,点击【Next】。 专题四 SSH 整合 翰子昂系列理论教材 - 289 - 图 4-3 添加 Spring 支持 4. 将 applicationContext.xml 放在 WEB-INF 目录当中,点击【Finish】,完成 Spring 支持 环境的添加。 Java 框架 - 290 - 翰子昂系列理论教材 图 4-4 将 Spring 配置文件放在 WEB-INF 下 5. 添加 Hibernate 支持,右键选中工程【MyEclipse】Î【Add Hibernatecapabilities】,选 中三个 Libraries,分别是 Hibernate 3.1 Core Libraries、Hibernate 3.1 Advanced Support Lib raries、Spring 2.0 Persistence Libraries,点击【Next】。 专题四 SSH 整合 翰子昂系列理论教材 - 291 - 图 4-5 添加 Hibernate 支持 6. 选中 Spring configuration file(applicationContext.xml),点击【Next】。 Java 框架 - 292 - 翰子昂系列理论教材 图 4-6 选择数据源采用的配置文件 7. 选中 Existing Spring configuration file,在 SessionFactory ID 中填写“sessionFactory”, 点击【Next】。 专题四 SSH 整合 翰子昂系列理论教材 - 293 - 图 4-7 填写 SessionFactory ID 8. Bean Id 填写 dataSource,DB Driver 选择我们建好的驱动,点击【Next】。 Java 框架 - 294 - 翰子昂系列理论教材 图 4-8 配置数据源 9. 将“Create SesssionFactory Class?”的钩去掉,点击【Finish】。 专题四 SSH 整合 翰子昂系列理论教材 - 295 - 图 4-9 去掉“Create SesssionFactory Class?”的钩 10. 工具会弹出对话框,我们选中【Keep Existing】点击。 Java 框架 - 296 - 翰子昂系列理论教材 图 4-10 点击 Keep Existing 11. 此时 applicationContext.xml 会报错,因为缺少 commons-pool.jar 和 commons-dbcp.jar, 我们手动添加。 图 4-11 缺少 jar 包 applicationContext.xml 报错 专题四 SSH 整合 翰子昂系列理论教材 - 297 - 图 4-12 手动添加 jar 包 12.为了解决 SSH 整合 jar 包冲突问题,我们需要删除三个 jar 包 asm-2.2.3.jar,asm-com mons-2.2.3.jar,asm-util-2.2.3.jar,此时工程建立完毕。 Java 框架 - 298 - 翰子昂系列理论教材 图 4-13 删除冲突 jar 包 13. 新建 com.pojo 和 com.dao 包,利用 MyEclipse 当中的 MyEclipse Database Explorer 工具在 com.pojo 包下生成 Userinfo.java 类,UserinfoDAO.java 类和 Userinfo.hbm.xml,并 将 U serinfoDAO.java 类放入 com.dao 包中。 专题四 SSH 整合 翰子昂系列理论教材 - 299 - 图 4-14 生成数据访问类 package com.pojo; public class Userinfo implements java.io.Serializable { //用户 id private Integer userid; //用户名 private String username; //密码 private String userpwd; //密码找回问题 private String userques; //密码找回答案 private String userans; //积分 private Integer integral; //等级 private String grade; Java 框架 - 300 - 翰子昂系列理论教材 //email private String useremail; //性别 private Byte sex; public Userinfo() { } public Userinfo(Integer userid, String username, String userpwd, String userques, String userans, Integer integral, String grade, String useremail) { this.userid = userid; this.username = username; this.userpwd = userpwd; this.userques = userques; this.userans = userans; this.integral = integral; this.grade = grade; this.useremail = useremail; } public Userinfo(Integer userid, String username, String userpwd, String userques, String userans, Integer integral, String grade, String useremail, Byte sex) { this.userid = userid; this.username = username; this.userpwd = userpwd; this.userques = userques; this.userans = userans; this.integral = integral; this.grade = grade; this.useremail = useremail; this.sex = sex; } public Integer getUserid() { return this.userid; } public void setUserid(Integer userid) { this.userid = userid; } 专题四 SSH 整合 翰子昂系列理论教材 - 301 - public String getUsername() { return this.username; } public void setUsername(String username) { this.username = username; } public String getUserpwd() { return this.userpwd; } public void setUserpwd(String userpwd) { this.userpwd = userpwd; } public String getUserques() { return this.userques; } public void setUserques(String userques) { this.userques = userques; } public String getUserans() { return this.userans; } public void setUserans(String userans) { this.userans = userans; } public Integer getIntegral() { return this.integral; } public void setIntegral(Integer integral) { this.integral = integral; } public String getGrade() { Java 框架 - 302 - 翰子昂系列理论教材 return this.grade; } public void setGrade(String grade) { this.grade = grade; } public String getUseremail() { return this.useremail; } public void setUseremail(String useremail) { this.useremail = useremail; } public Byte getSex() { return this.sex; } public void setSex(Byte sex) { this.sex = sex; } } 专题四 SSH 整合 翰子昂系列理论教材 - 303 - package com.dao; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.LockMode; import org.springframework.context.ApplicationContext; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import com.pojo.Userinfo; public class UserinfoDAO extends HibernateDaoSupport { private static final Log log = LogFactory.getLog(UserinfoDAO.class); public static final String USERNAME = "username"; public static final String USERPWD = "userpwd"; public static final String USERQUES = "userques"; public static final String USERANS = "userans"; public static final String INTEGRAL = "integral"; public static final String GRADE = "grade"; public static final String USEREMAIL = "useremail"; public static final String SEX = "sex"; protected void initDao() { } Java 框架 - 304 - 翰子昂系列理论教材 public void save(Userinfo transientInstance) { log.debug("saving Userinfo instance"); try { getHibernateTemplate().save(transientInstance); log.debug("save successful"); } catch (RuntimeException re) { log.error("save failed", re); throw re; } } public void delete(Userinfo persistentInstance) { log.debug("deleting Userinfo instance"); try { getHibernateTemplate().delete(persistentInstance); log.debug("delete successful"); } catch (RuntimeException re) { log.error("delete failed", re); throw re; } } public Userinfo findById(java.lang.Integer id) { log.debug("getting Userinfo instance with id: " + id); try { Userinfo instance = (Userinfo) getHibernateTemplate().get( "com.pojo.Userinfo", id); return instance; } catch (RuntimeException re) { log.error("get failed", re); throw re; } } public List findByExample(Userinfo instance) { log.debug("finding Userinfo instance by example"); try { List results = getHibernateTemplate().findByExample(instance); log.debug("find by example successful, result size: " + results.size()); return results; 专题四 SSH 整合 翰子昂系列理论教材 - 305 - } catch (RuntimeException re) { log.error("find by example failed", re); throw re; } } public List findByProperty(String propertyName, Object value) { log.debug("finding Userinfo instance with property: " + propertyName + ", value: " + value); try { String queryString = "from Userinfo as model where model." + propertyName + "= ?"; return getHibernateTemplate().find(queryString, value); } catch (RuntimeException re) { log.error("find by property name failed", re); throw re; } } public List findByUsername(Object username) { return findByProperty(USERNAME, username); } public List findByUserpwd(Object userpwd) { return findByProperty(USERPWD, userpwd); } public List findByUserques(Object userques) { return findByProperty(USERQUES, userques); } public List findByUserans(Object userans) { return findByProperty(USERANS, userans); } public List findByIntegral(Object integral) { return findByProperty(INTEGRAL, integral); } public List findByGrade(Object grade) { return findByProperty(GRADE, grade); Java 框架 - 306 - 翰子昂系列理论教材 } public List findByUseremail(Object useremail) { return findByProperty(USEREMAIL, useremail); } public List findBySex(Object sex) { return findByProperty(SEX, sex); } public List findAll() { log.debug("finding all Userinfo instances"); try { String queryString = "from Userinfo"; return getHibernateTemplate().find(queryString); } catch (RuntimeException re) { log.error("find all failed", re); throw re; } } public Userinfo merge(Userinfo detachedInstance) { log.debug("merging Userinfo instance"); try { Userinfo result = (Userinfo) getHibernateTemplate().merge( detachedInstance); log.debug("merge successful"); return result; } catch (RuntimeException re) { log.error("merge failed", re); throw re; } } public void attachDirty(Userinfo instance) { log.debug("attaching dirty Userinfo instance"); try { getHibernateTemplate().saveOrUpdate(instance); log.debug("attach successful"); } catch (RuntimeException re) { log.error("attach failed", re); throw re; 专题四 SSH 整合 翰子昂系列理论教材 - 307 - } } public void attachClean(Userinfo instance) { log.debug("attaching clean Userinfo instance"); try { getHibernateTemplate().lock(instance, LockMode.NONE); log.debug("attach successful"); } catch (RuntimeException re) { log.error("attach failed", re); throw re; } } //判断登录 public boolean verify(Userinfo user) { String hql = "from Userinfo as u where u.username=? and u.userpwd=?"; List list = this.getHibernateTemplate().find(hql, new String[]{user.getUsername(),user.getUserpwd()}); if(list.size() > 0) { return true; } return false; } public static UserinfoDAO getFromApplicationContext(ApplicationContext ctx) { return (UserinfoDAO) ctx.getBean("UserinfoDAO"); } } 14. 新建 com.service 包,在包中建立 UserinfoService.java 类,代码如下: package com.service; import com.pojo.Userinfo; import com.dao.UserinfoDAO; public class UserinfoService { //依赖注入 UserinfoDAO private UserinfoDAO userinfoDAO; public UserinfoDAO getUserinfoDAO() { return userinfoDAO; Java 框架 - 308 - 翰子昂系列理论教材 } public void setUserinfoDAO(UserinfoDAO userinfoDAO) { this.userinfoDAO = userinfoDAO; } //判断登录 public boolean Login(Userinfo user) { return userinfoDAO.verify(user); } } 15. 新建 com.struts 包,在包中建立 LoginAction.java 和 LoginActionForm.java 类,代码如 下: package com.struts; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import com.pojo.Userinfo; import com.service.UserinfoService; public class LoginAction extends Action { //依赖注入 UserinfoService private UserinfoService userinfoService; public UserinfoService getUserinfoService() { return userinfoService; } public void setUserinfoService(UserinfoService userinfoService) { this.userinfoService = userinfoService; } public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { LoginActionForm LoginActionForm = (LoginActionForm) form;// TODO Auto-generated method stub //得到 session 对象 专题四 SSH 整合 翰子昂系列理论教材 - 309 - HttpSession session=request.getSession(); //判断验证码 if(!session.getAttribute("rand").toString().equals(LoginActionForm.getR and())) { //页面跳转 return mapping.findForward("self"); } Userinfo user=new Userinfo(); user.setUsername(LoginActionForm.getName()); user.setUserpwd(LoginActionForm.getPassword()); //如果用户名和密码输入正确登录成功 if(userinfoService.Login(user)) { return mapping.findForward("success"); } //如果输入错误提示错误 else { return mapping.findForward("error"); } } } package com.struts; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; public class LoginActionForm extends ActionForm { //密码 private String password; //用户名 private String name; //验证码 private String rand; public String getRand() { return rand; } Java 框架 - 310 - 翰子昂系列理论教材 public void setRand(String rand) { this.rand = rand; } public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { return null; } public void reset(ActionMapping mapping, HttpServletRequest request) { } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } } 16. struts-config.xml 配置文件信息如下: 专题四 SSH 整合 翰子昂系列理论教材 - 311 - 17. applicationContext.xml 配置文件信息如下: Java 框架 - 312 - 翰子昂系列理论教材 org.hibernate.dialect.MySQLDialect com/pojo/Userinfo.hbm.xml 18. 本程序涉及到的四个 Jsp 页面前面已经给出,部署程序,在地址栏里输入 http://local host:8080/SSHLogin/jsp/login.jsp 打开登录页面。 专题四 SSH 整合 翰子昂系列理论教材 - 313 - 图 4-15 登录页面 19. 输入正确的用户名、密码和验证码后点击“提交”,提示“登录成功”。 图 4-16 提示登录成功 18. 验证码、用户名或密码输入错误将在登录页面提示用户。 Java 框架 - 314 - 翰子昂系列理论教材 图 4-17 提示用户名密码错误 4. 相关理论知识 4.1 SSH 简介 典型的 J2EE 三层结构,分为表现层、中间层(业务逻辑层)和数据服务层。三层体系将 业务规则、数据访问及合法性校验等工作放在中间层处理。客户端不直接与数据库交互,而 是通过组件与中间层建立连接,再由中间层与数据库交互。 以前,经常提到的 MVC,其实 就是这里所说的中间层的三个组成部分。 表现层采用传统的 JSP 技术,自 1999 年问世以来,经过多年的发展,以广泛的应用和稳 定的表现,为其作为表现层技术打下了坚实的基础。 中间层采用的是流行的 Spring+Hibernate,为了将控制层与业务逻辑层分离,又细分为以 下几种: Web 层,就是 MVC 模式里面的“C”(controller),负责控制业务逻辑层与表现层的交互, 调用业务逻辑层,并将业务数据返回给表现层作组织表现。SSH 中的“C”采用的是 Struts。 Service 层(就是业务逻辑层),负责实现业务逻辑。业务逻辑层以 DAO 层为基础,通 过对 DAO 组件的正面模式包装,完成系统所要求的业务逻辑。 DAO 层,负责与持久化对象交互。该层封装了数据的增、删、查、改的操作。 PO,持久化对象。通过实体关系映射工具将关系型数据库的数据映射成对象,很方便地 实现以面向对象方式操作数据库,该系统采用 Hibernate 作为 ORM 框架。 Spring 的作用贯穿了整个中间层,将 Web 层、Service 层、DAO 层及 PO 无缝整合,其 专题四 SSH 整合 翰子昂系列理论教材 - 315 - 数据服务层用来存放数据。 4.2 Spring 整合 Hibernate 对 Hibernate,Spring 提供很多 IoC 的特性的支持,方便处理大部分典型的 Hibernate 整合 的问题。所有的这些,都遵守 Spring 通用的事务和 DAO 异常体系。Spring 整合 Hibernate 后, 使持久层的访问更加容易,Spring 管理 Hibernate 持久层有如下优势: z 通用的资源管理:Spring 的 ApplicationContext 能管理 SessionFactory,使得配置值很 容易被管理和修改。无须使用 Hibernate 的配置文件。 z 有效的 Session 管理:Spring 提供了有效,简单和安全的 Hibernate Session 处理。 z 方便的事务管理:Hibernate 的事务管理处理不好,会限制 Hibernate 的表现,而 Spr ing 的声明式事务管理粒度是方法级。 z 异常包装:Spring 能够包装 Hibernate 异常,把它们从 checked exception 变为 runti me exception。开发者可选择在恰当的层处理数据库的不可恢复异常,从而避免繁琐 的 catch/throw 以及异常声明。 4.3 管理 SessionFactory SessionFactory:单个数据库映射关系编译后的内存镜像。大部分情况下,一个 J2EE 应用 对应一个数据库。Spring 通过 ApplicationContext 管理 SessionFactory,无须采用单独 Hiberate 应用必需的 hibernate.cfg.xml 文件。 SessionFactory 与数据库的连接,都由 Spring 的配置管理。实际的 J2EE 应用,通常使用 数据源,数据源会采用依赖注入的方式,传给 Hibernate 的 SessionFactory。具体配置如下所 示: Java 框架 - 316 - 翰子昂系列理论教材 org.hibernate.dialect.MySQLDialect com/pojo/Userinfo.hbm.xml SessionFactory 由 ApplicationContext 管理,会随着应用启动时候自动加载。SessionFactory 可以被处于 ApplicaionContext 管理的任意一个 bean 引用,比如 DAO。Hibernate 的数据库访 问需要在 Session 管理下,而 SessionFactory 是 Session 的工厂。Spring 采用依赖注入为 DAO 对象注入 SessionFactory 的引用。 Spring 更提供 Hibernate 的简化访问方式,Spring 采用模板设计模式,提供 Hibernate 访问 与其他持久层和访问保持一致。如果需要使用容器管理的数据源,则无须提供数据驱动等信 息,只需要提供数据源的 JNDI 即可。对上文的 SessionFactory 无须任何修改,只需将 dataSo urce 的配置替换成 JNDI 数据源,将原有的 dataSource Bean 替换成如下所示: java:comp/env/jdbc/myds 4.4 Spring 对 Hibernate 的简化 Spring 对 Hibernate 的简化主要有如下几个方面: 1. 基于依赖注入的 SessionFactory 管理机制。SessionFactory 是执行持久化操作的核心组 件。传统 Hibernate 应用中,SessionFactory 必须手动创建;通过依赖注入,代码无须关心 Ses 专题四 SSH 整合 翰子昂系列理论教材 - 317 - sionFactory,SessionFactory 的创建,维护由 BeanFactory 负责管理。 2. 更优秀的 Session 管理机制。Spring 提供“每事务一次 Session”的机制,该机制能大 大提高系统性能,而且 Spring 对 Session 的管理是透明的,无须在代码中操作 Session。 3. 统一的事务管理。无论是编程式事务,还是声明式事务,Spring 都提供一致的编程模 型,无须繁琐的开始事务,显式提交、回滚。如果使用声明式事务管理,事务管理逻辑与代 码分离,事务可在全局事务和局部事务之间切换。 4. 统一的异常处理机制。不再强制开发者在持久层捕捉异常,持久层异常被包装成 Dat aAccessException 异常的子类,开发者可以自己决定在合适的层处理异常,将底层数据库异常 包装成业务异常。 5. HibernateTemplate 支持类。HibernateTempate 能完成大量 Hibernate 持久层操作,这些 操作大多只需一行代码,非常简洁。 4.5 使用 HibernateTemplate HibernateTemplate 提供持久层访问模板化,使用 HibernateTemplate 无须实现特定接口, 它只需要提供一个 SessionFactory 的引用,就可执行持久化操作。SessionFactory 对象可通过 构造参数传入,或通过设值方式传入。如下: //获取 Spring 上下文 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //通过上下文获得 SessionFactory SessionFactory sessionFactory = (SessionFactory) ctx.getBean(“sessionFactory”); 然后创建 HibernateTemplate 实例。HibernateTemplate 提供如下三个构造函数 z HibernateTemplate() z HibernateTemplate(org.hibernate.SessionFactory sessionFactory) z HibernateTemplate(org.hibernate.SessionFactory sessionFactory, boolean allowCreate) 第一个构造函数,构造一个默认的 HibernateTemplate 实例,因此,使用 HibernateTempl ate 实例之前,还必须使用方法 setSessionFactory(SessionFactory sessionFactory)来为 Hibernate Template 传入 SessionFactory 的引用。 第二个构造函数,在构造时已经传入 SessionFactory 引用。 第三个构造函数,其 boolean 型参数表明:如果当前线程已经存在一个非事务性的 Sessi on,是否直接返回此非事务性的 Session。 对于在 Web 应用,通常启动时自动加载 ApplicationContext,SessionFactory 和 DAO 对象 都处在 Spring 上下文管理下,因此无须在代码中显式设置,可采用依赖注入解耦 SessionFact ory 和 DAO,依赖关系通过配置文件来设置,如下所示: Java 框架 - 318 - 翰子昂系列理论教材 org.hibernate.dialect.MySQLDialect com/pojo/Userinfo.hbm.xml 专题四 SSH 整合 翰子昂系列理论教材 - 319 - DAO 实现类中,可采用更简单的方式来取得 HibernateTemplate 的实例。代码如下: public class UserinfoDAO{ //以私有的成员变量来保存 SessionFactory。 private SessionFactory sessionFactory; //设值注入 SessionFactory 必需的 setter 方法 public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } public List loadUserByName(final String name) { HibernateTemplate hibernateTemplate = new HibernateTemplate(this.sessionFactory); //此处采用 HibernateTemplate 完成数据库访问 } } 4.5.1 HibernateTemplate 用法 HibernateTemplate 提供非常多的常用方法来完成基本的操作,比如通常的增加、删除、 修改、查询等操作,Spring 2.0 更增加对命名 SQL 查询的支持,也增加对分页的支持。大部 分情况下,使用 Hibernate 的常规用法,就可完成大多数 DAO 对象的 CRUD 操作。下面是 H ibernateTemplate 的常用方法简介: void delete(Object entity):删除指定持久化实例 deleteAll(Collection entities):删除集合内全部持久化类实例 find(String queryString):根据 HQL 查询字符串来返回实例集合 findByNamedQuery(String queryName):根据命名查询返回实例集合 get(Class entityClass, Serializable id):根据主键加载特定持久化类的实例 save(Object entity):保存新的实例 saveOrUpdate(Object entity):根据实例状态,选择保存或者更新 Java 框架 - 320 - 翰子昂系列理论教材 update(Object entity):更新实例的状态,要求 entity 是持久状态 setMaxResults(int maxResults):设置分页的大小 下面是一个完整 DAO 类的源代码: public class UserinfoDAO { //采用 log4j 来完成调试时的日志功能 private static Log log = LogFactory.getLog(UserinfoDAO.class); //以私有的成员变量来保存 SessionFactory。 private SessionFactory sessionFactory; //以私有变量的方式保存 HibernateTemplate private HibernateTemplate hibernateTemplate = null; //设值注入 SessionFactory 必需的 setter 方法 public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } //初始化本 DAO 所需的 HibernateTemplate public HIbernateTemplate getHibernateTemplate() { //首先,检查原来的 hibernateTemplate 实例是否还存在 if ( hibernateTemplate == null) { //如果不存在,新建一个 HibernateTemplate 实例 hibernateTemplate = new HibernateTemplate(sessionFactory); } return hibernateTemplate; } //返回全部用户的实例 public List getUserinfos() { //通过 HibernateTemplate 的 find 方法返回 Userinfo 的全部实例 return getHibernateTemplate().find("from Userinfo"); } /** * 根据主键返回特定实例 * @ return 特定主键对应的 Userinfo 实例 * @ param 主键值 public Userinfo getUserinfo (int userid) { return (Userinfo)getHibernateTemplate().get(Userinfo.class, new Integer(userid)); 专题四 SSH 整合 翰子昂系列理论教材 - 321 - } /** * @ Userinfo 需要保存的 Userinfo 实例 */ public void saveUserinfo(Userinfo userinfo) { getHibernateTemplate().saveOrUpdate(userinfo); } /** * @ param userid 需要删除 Userinfo 实例的主键 * / public void removeUserinfo (int userid) { //先加载特定实例 Object u = getHibernateTemplate().load(Userinfo.class, new Integer(userid)); //删除特定实例 getHibernateTemplate().delete(u); } } 4.6 Hibernate 的 DAO 实现 DAO 对象是模块化的数据库访问组件,DAO 对象通常包括:对持久化类的基本 CRUD 操作(插入、查询、更新、删除)操作。Spring 对 Hibernate 的 DAO 实现提供了良好的支持。 Spring 为 Hibernate 的 DAO 提供工具类:HibernateDaoSupport。该类主要提供如下两个 方法,方便 DAO 的实现: z public final HibernateTemplate getHibernateTemplate() z public final void setSessionFactory(SessionFactory sessionFactory) 其中,setSessionFactory 方法用来接收 Spring 的 ApplicationContext 的依赖注入,可接收 配置在 Spring 的 SessionFactory 实例,getHibernateTemplate 方法则用来根据刚才的 SessionFa ctory 产生 Session,最后生成 HibernateTemplate 来完成数据库访问。 典型的继承 HibernateDaoSupport 的 DAO 实现的代码如下: public class UserinfoDAO extends HibernateDaoSupport //采用 log4j 来完成调试时的日志功能 private static Log log = LogFactory.getLog(UserinfoDAO.class); //返回全部用户的实例 public List getUserinfos() { //通过 HibernateTemplate 的 find 方法返回 Userinfo 的全部实例 return getHibernateTemplate().find("from Userinfo"); Java 框架 - 322 - 翰子昂系列理论教材 } /** * 根据主键返回特定实例 * @ return 特定主键对应的 Userinfo 实例 * @ param 主键值 public News getUserinfo (int userid) { return (Userinfo)getHibernateTemplate().get(Userinfo.class, new Integer(userid)); } /** * @ Userinfo 需要保存的 Userinfo 实例 */ public void saveUserinfo (Userinfo user) { getHibernateTemplate().saveOrUpdate(user); } /** * @ param userid 需要删除 Userinfo 实例的主键 * / public void removeUserinfo (int userid) { //先加载特定实例 Object u = getHibernateTemplate().load(Userinfo.class, new Integer(userid)); //删除特定实例 getHibernateTemplate().delete(p); } } 可以与前面的 UserinfoDAO 对比,会发现代码量大大减少。事实上,DAO 的实现依然借 助于 HibernateTemplate 的模板访问方式,只是,HibernateDaoSupport 将依赖注入 SessionFact ory 的工作已经完成,获取 HibernateTemplate 的工作也已完成。该 DAO 的配置必须依赖于 S essionFactory,具体的配置如下: 专题四 SSH 整合 翰子昂系列理论教材 - 323 - org.hibernate.dialect.MySQLDialect com/pojo/Userinfo.hbm.xml Java 框架 - 324 - 翰子昂系列理论教材 程序中可以通过显式的编码来获得 userinfoDAO bean,然后执行 CRUD 操作。也可通过 依赖注入,将 personDAO 的实例注入其他 bean 属性,再执行 CRUD 操作。 在继承 HibrnateDaoSupport 的 DAO 实现里,Hibernate Session 的管理完全不需要 Hibern ate 代码打开,而由 Spring 来管理。Spring 会根据实际的操作,采用“每次事务打开一次 sess ion”的策略,自动提高数据库访问的性能。 4.7 声明式事务管理 Hibernate 建议所有的数据库访问都应放在事务内进行,即使只进行只读操作。事务又应 该尽可能的短,长事务会导致系统长时间无法释放,因而降低系统并发的负载。Spring 同时 支持编程式事务和声明式事务。尽量考虑使用声明式事务,声明式事务管理可分离业务逻辑 和事务管理逻辑,具备良好的适应性。 关于声明式事务管理的配置方式,通常有如下三种 z 使用 TransactionProxyFactoryBean 为目标 bean 生成事务代理的配置。此方式是最传 统,配置文件最臃肿、难以阅读的方式 z 采用 bean 继承的事务代理配置方式,比较简洁,但依然是增量式配置。 z 使用 BeanNameAutoProxyCreator,根据 bean name 自动生成事务代理的方式。 建议采用第三种配置方式,详细的配置代码如下: 专题四 SSH 整合 翰子昂系列理论教材 - 325 - org.hibernate.dialect.MySQLDialect com/pojo/Userinfo.hbm.xml PROPAGATION_REQUIRED PROPAGATION_REQUIRED,readOnly PROPAGATION_REQUIRED userinfoService transactionInterceptor 专题四 SSH 整合 翰子昂系列理论教材 - 327 - TranscationInterceptor 是一个事务拦截器 bean,需要传入一个 TransactionManager 的引用。 配置中使用 Spring 依赖注入该属性,事务拦截器的事务属性通过 transactionAttributes 来指定, 该属性有 props 子元素,配置文件中定义了三个事务传播规则: 所有以 insert 开始的方法,采用 PROPAGATION_REQUIRED 的事务传播规则。程序抛 出 MyException 异常及其子异常时,自动回滚事务。所有以 find 开头的方法,采用 PROPAG ATION_REQUIRED 事务传播规则,并且只读。其他方法,则采用 PROPAGATION_REQUI RED 的事务传播规则。 BeanNameAutoProxyCreator 是个根据 bean 名生成自动代理的代理创建器,该 bean 通常 需要接受两个参数。第一个是 beanNames 属性,该属性用来设置哪些 bean 需要自动生成代理。 另一个属性是 interceptorNames,该属性则指定事务拦截器,自动创建事务代理时,系统会根 据这些事务拦截器的属性来生成对应的事务代理。 常见的事务传播规则有如下几种: 1. PROPAGATION_MANDATORY::求调用该方法的线程必须处于事务环境中,否则抛 出异常。 2. PROPAGATION_NESTED:如果执行该方法的线程以处于事务环境下,依然启动新的 事务,方法在嵌套的事务里执行。如果执行方法的线程并未处于事务中,也启动新的事务, 然后执行该方法,此时与 PROPAGATION_REQUIRED 相同。 3. PROPAGATION_NEVER:不允许调用该方法的线程处于事务环境下,如果调用该方 法的线程处于事务环境下,则抛出异常。 4. PROPAGATION_NOT_SUPPORTED:如果调用该方法的线程处在事务中,则先暂停 当前事务,然后执行方法。 5. PROPAGATION_REQUIRED:要求在事务环境中执行该方法,如果当前执行线程已 处于事务中,则直接调用;如果当前执行线程不处于事务中,则启动新的事务后执行该方法。 6. PROPAGATION_REQUIRED_NEW:该方法要求有一个在新的事务环境中运行,如果 当前执行线程已处在事务中,先暂停事务,启动新事务后执行方法;如果当前线程不处在事 务中,则启动新的事务后执行方法。 7. PROPAGATION_SUPPORTS:如果当前执行线程处于事务中,则使用当前事务,否 则不使用事务。 Java 框架 - 328 - 翰子昂系列理论教材 4.8 整合 Struts Spring 提供与 Struts 的无缝结合。Spring 与 Struts 的整合有如下两种策略: z 让 Spring IoC 容器管理 Struts 的 Action,Struts 采用 ContextLoaderPlugin 创建 Sprin g 的 ApplicationContext 实例。 z 采用 Spring 的 ApplicationSupport 类的子类。该支持类具有 getWebApplicationConte xt()方法可以访问 Spring 的 ApplicationContext。 推荐使用 Spring IoC 容器管理 Struts Action 的方式。采用这种方式能充分利用 Spring 依 赖注入的优势,而无须显示地获取 Spring 的 ApplicationContext 实例。让 Spring IoC 容器管理 Action 也有两种方式: z 使用 DelegatingRequestProcessor z 使用 DelegatingActionProxy 不管采用哪一种,都需要随应用启动时创建 ApplicationContext 实例。创建 ApplicationCo ntext 实例采用 Spring 的 ContextLoaderPlugin 类,该类实现 org.apache.struts.action.Plugin 接口, Plugin 接口从 Struts1.1 开始才有,用于启动时加载某个模块。 ContextLoaderPlugin 默认加载的配置文件为 servletName-servlet.xml。其中 servletName 是 Struts 的 ActionServlet 对应的 Servlet 名。例如 web.xml 中进行如下定义: actionServlet org.apache.struts.action.ActionServlet ContextLoaderPlugin 默认加载\WEB-INF\actionServlet-servlet.xml,将该文件作为 Spring 的配置文件。因此如果 Spring 的配置文件只有一份,且文件名为 actionServlet-servlet.xml,则 只需在 Struts 配置文件的底部增加如下一行: 如果有多个配置文件,或者配置文件的文件名不符合规则,则可采用 contextConfigLocat ion 属性载入。同样,配置文件之间以“,”隔开。下面是载入多个配置文件的配置片段: 创建 ApplicationContext 实例后,关键是如何将 ActionServlet 截获的请求转发给 Spring 管理的 bean。Spring IoC 容器不仅管理本身的业务 bean,还负责管理 Struts 的 Action。因此, 需要让 ActionServlet 将请求不再转发给 struts-config.xml 配置的 action,而是转发给 ApplicationContext 里配置的 bean。完成这个转发也有两个策略: z 采用 DelegatingRequestProcessor z 采用 DelegatingActionProxy 专题四 SSH 整合 翰子昂系列理论教材 - 329 - 4.8.1 使用 DelegatingRequestProcessor 查看 Struts 的源代码,看到 ActionServlet 调用 RequestProcessor 完成实际的转发。如果想 将 Action 截获的请求转发给 ApplicationContext 的 bean,可以扩展 RequestProcessor 完成。 Spring 完成这种扩展。Spring 提供的 DelegatingRequestProcessor 继承 RequestProcessor。 为了让 Struts 使用 DelegatingRequestProcessor,还需要在 struts-config.xml 文件中增加如下一 行: //使用 Spring 的 RequestProcessor 替换 Struts 原有的 RequestProcessor 完成这个设置后,Struts 会将截获到的用户请求转发到 Spring context 下的 bean,根据 b ean 的 name 属性来匹配。而 Struts 中的 action 配置则无须配置 type 属性,即使配置了 type 属 性也没有任何用处,即下面两行配置是完全相同的: //配置 struts action 时,指定了实现类 //配置配置 struts action 时,没有指定类 下面一个简单的 struts-config.xml 文件,已经和 Spring 整合在了一起,如下: applicationContext.xml 应放置在 WEB-INF 目录下,其详细配置信息如下: 每次请求都应该启动新的 action 处理用户请求,因此应将 action bean 配置成 non-singleton 行为。 当我们访问 Login.do 的时候,DelegatingRequestProcessor 会将请求转发到 action。该 acti on 已经处于 IoC 容器管理之下,因此,可以方便的访问容器中的其他 bean。 4.8.2 使用 DelegatingActionProxy 使用 DelegatingRequestProcessor 简单方便,但有一个缺点:RequestProcessor 是 Struts 的 一个扩展点,也许应用本身就需要扩展 RequestProcessor,而 DelegationRequestProcessor 已经 占领了这个扩展点。此时有如下两个做法: z 应用程序的 RequestProcessor 不再继承 Struts 的 RequestProcessor,改为继承 Delegat ingRequestProcessor。 z 使用 DelegatingActionProxy。 前者常常有一些未知的风险,而后者是 Spring 推荐的整和策略。使用 DelegatingActionPr oxy 与使用 DelegatingRequestProcessor 目的只有一个,即将请求转发给 Spring 管理的 bean。 DelegationRequestProcessor 直接替换原有的 RequestProcessor,在请求转发给 action 之前, 转发给 Spring 管理 bean;而 DelegatingActionProxy 则配置成 Struts 的 action,即所有的请求 先被 ActionServlet 截获,请求被转发到相应的 action,而 action 实现类全都是 DelegatingActi onProxy。DelegatingActionProxy 再将请求转发给 Spring 容器的 bean。 可以看出,DelegatingActionProxy 比使用 DelegationRequestProcessor 要晚一步转发到 Spr ing 的 context,但通过这种方式可以避免占用扩展点。 与使用 DelegationRequestProcessor 对比,使用 DelegatingActionProxy 仅需去掉 controller 配置元素,并将所有的 action 实现类改为 DelegatingActionProxy。详细配置文件如下: 专题四 SSH 整合 翰子昂系列理论教材 - 331 - DelegatingActionProxy 接受 ActionServlet 转发过来的请求,然后转发给 ApplicationContext 管理的 bean,这是典型的链式处理。 4.8.3 使用 ActionSupport 代替 Action 还有一种方法可以用于 Spring 和 Struts 的整合,让 Action 在程序中手动获得 Application Context 实例。在这种整合策略下,Sturts 的 Action 不接受 IoC 容器管理,Action 的代码与 Sp ring API 部分耦合,造成代码污染。Actoin 中访问 ApplicationContext 有两种方法: z 利用 WebApplicationContextUtils 工具类。 z 利用 ActionSupport 支持类。 WebApplicationContextUtils 可以通过 ServletContext 获得 Spring 容器实例。ActionSuppor t 类则提供一个简单的 getWebApplicationContext()方法,该方法用于获取 ApplicationContext 实例。Spring 扩展 Strus 的标准类,Spring 的 Action 在 Struts 的 Action 后加上 Support。Sprin g 的 Action 有如下 4 中: z ActionSupport z DispatchActionSupport Java 框架 - 332 - 翰子昂系列理论教材 z LookUpDispatchActionSupport z MappingDispatchActionSupport 下面给出利用 ActionSupport 的示例代码: import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.springframework.web.struts.ActionSupport; //新的业务控制器,继承 Spring 的 ActionSupport public class LoginAction extends ActionSupport { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { LoginActionForm LoginActionForm = (LoginActionForm) form; return null; } } 这种整合策略下,表现层的控制器组件不再接受 IoC 容器管理。因此没有控制器 context, 我们应该修改配置文件将,applicationContext.xml 中的 action bean 删除,修改 action 配置,将 action 配置的 type 元素修改成实际的处理类。这种整合策略也有个好处:代码可读性强,对 传统 Struts 应用开发的改变很小,容易使用。 5. 试验 按照上课的顺序依次练习,主要掌握: 1. 建立 SSHLogin 工程,并添加 struts,spring,hibernate 支持。(10 分钟) 2. 编写 login.jsp,success.jsp,error.jsp,image.jsp 页面。(20 分钟) 3. 利用工具生成 userinfo.java、userinfo.hbm.xml 和 userinfoDAO 类。(20 分钟) 4. 编写 userinfoService.java 类。(10 分钟) 5. 编写 LoginAction 和 LoginActionForm 类。(20 分钟) 6. 配置 struts-config.xml 和 application.xml 文件,并调试运行程序。(10 分钟) 6. 作业 整合 SSH,并实现登录功能。 专题五 Ajax 翰子昂系列理论教材 - 333 - 专题五 Ajax 教学目标 理解 Ajax 工作原理 掌握 XmlHttpRequest 对象 使用 DWR 框架进行 Ajax 开发 Java 框架 - 334 - 翰子昂系列理论教材 案例一 AJAX 基础知识 1. 教学目标 1.1 了解 AJAX 及其 AJAX 优点 1.2 理解 AJAX 构成元素 1.3 掌握 XmlHttpRequest 对象 2. 工作任务 2.1 建立一个类似 Google Suggest 应用 2.2 建立一个用户注册时用户名验证应用 3. 相关实践知识 3.1 建立一个类似 Google Suggest 应用 1. 新建 Web 项目,项目名为“ajax”。如下图所示: 图 5-1 建立 ajax 工程 2. 在项目中建立名为 com.ajax.servlet 的包。 3. 在 com.ajax.servlet 包内建立名为 Suggest 的 servlet。代码如下: public class Suggest extends HttpServlet { private ArrayList lib = new ArrayList(); 专题五 Ajax 翰子昂系列理论教材 - 335 - //初始化数据集合,可以在这个字库中添加更多词条 public void init() throws ServletException { lib.add("a"); lib.add("able"); lib.add("access"); lib.add("advance"); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //设置生成文件的类型和编码格式 response.setContentType("text/xml; charset=UTF-8"); response.setHeader("Cache-Control", "no-cache"); PrintWriter out = response.getWriter(); String output = ""; //处理接收到的参数 String key = request.getParameter("key"); ArrayList matchList = getMatchString(key); if (!matchList.isEmpty()) { output += ""; for (int i = 0; i < matchList.size(); i++) { String match = matchList.get(i).toString(); output += "" + match + ""; } output += ""; } out.println(output); out.close(); } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } //取得所有匹配的字符串 public ArrayList getMatchString(String key) { ArrayList result = new ArrayList(); if (!lib.isEmpty()) { for (int i = 0; i < lib.size(); i++) { String str = lib.get(i).toString(); if (str.startsWith(key)) Java 框架 - 336 - 翰子昂系列理论教材 result.add(str); } } return result; } } 4. 在项目中建立 Suggest.jsp 文件,代码如下: %@ page language="java" import="java.util.*" pageEncoding="gb2312"%> 输入提示示例 输入提示示例(可以输入字母 a 开头的字符串进行测试)
请输入:
5. 将项目部署到服务器,启动服务器,打开 IE 浏览器输入地址 http://localhost:8080/ajax/ Suggest.jsp。显示内容如下: 专题五 Ajax 翰子昂系列理论教材 - 339 - 图 5-2 显示 Suggest.jsp 内容 6. 向文本框输入“a”,可以看到下图内容: 图 5-3 输入‘a’字母呈现内容 这样我们就完成了一个类似于 google suggest 的示例。 在上述的示例中我们将相关数据写在 servlet 中。在实际开发过程中,数据再存放到数据 库中,下面进行注册时用户名是否存在验证示例。 3.2 建立用户注册时用户名验证应用 1. 使用 Hibernate 示例中的 mybbs 数据库中的 admin 表。admin 表 Sql 语句如下: create table `mybbs`.`admin`( `adminid` int not null auto_increment, `adminname` varchar(20) default '' not null, `adminpwd` varchar(20) default '' not null, primary key (`adminid`) )TYPE=INNODB, default character set gbk; 同时向 admin 表中插入一条记录: Insert into admin(adminname,adminpwd) values(’admin’,’admin’); 2. 将 mysql 的驱动程序 mysql-connector-java-5.0.4-bin.jar 复制到 WEB-INF/lib 目录下。 Java 框架 - 340 - 翰子昂系列理论教材 3. 使用上述示例的“ajax”工程。在“ajax”工程中建立 com.ajax.pojo,com.ajax.dao, com.ajax.util 三个包。工程结构如下图所示: 图 5-4 “ajax”工程目录 4. 在 com.ajax.util 包内建立 DBUtil 类,该类得到数据库连接及关闭连接。代码如下: package com.ajax.util; import java.sql.*; public class DBUtil { private Connection conn; private PreparedStatement pstm; private ResultSet rs; public Connection getConn(){ try{ Class.forName("com.mysql.jdbc.Driver"); conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mybbs","root" ,"123456"); }catch(Exception ex){ ex.printStackTrace(); } return conn; } public void closeConn(Connection conn,PreparedStatement pstm,ResultSet rs){ if(rs!=null){ try { rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(pstm!=null){ 专题五 Ajax 翰子昂系列理论教材 - 341 - try { pstm.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(conn!=null){ try { conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } 5. 在 com.ajax.pojo 中建立 Admin 实体类。代码如下: package com.ajax.pojo; public class Admin { private String adminName; private String adminPwd; public String getAdminName() { return adminName; } public void setAdminName(String adminName) { this.adminName = adminName; } public String getAdminPwd() { return adminPwd; } public void setAdminPwd(String adminPwd) { this.adminPwd = adminPwd; } } 6. 在 com.ajax.dao 中建立 AdminDAO 类,该类负责进行业务逻辑的编写。代码如下: package com.ajax.dao; import java.sql.*; import com.ajax.pojo.Admin; Java 框架 - 342 - 翰子昂系列理论教材 import com.ajax.util.DBUtil; import java.util.*; public class AdminDAO { private Connection conn; private PreparedStatement pstm; private ResultSet rs; private Admin admin; private DBUtil db; public ArrayList selectName(){ db=new DBUtil(); ArrayList array=new ArrayList(); String name=""; try{ conn=db.getConn(); String sql="select adminname from admin"; pstm=conn.prepareStatement(sql); rs=pstm.executeQuery(); while(rs.next()){ name=rs.getString("adminname"); array.add(name); } }catch(Exception ex){ ex.printStackTrace(); } return array; } } 7. 在 com.ajax.servlet 中建立一个名字为 UserCheck 的 servlet。代码如下: package com.ajax.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ajax.dao.AdminDAO; import java.util.ArrayList; public class UserCheck extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse 专题五 Ajax 翰子昂系列理论教材 - 343 - response) throws ServletException, IOException { //设置生成文件的类型和编码格式 response.setContentType("text/xml; charset=UTF-8"); response.setHeader("Cache-Control", "no-cache"); PrintWriter out = response.getWriter(); String output = ""; //处理接收到的参数,生成响应的 XML 文档 String name = request.getParameter("name"); AdminDAO admin=new AdminDAO(); ArrayList array=admin.selectName(); if(name.length()>0) { //下面对用户的身份进行判断 for(int i=0;i 异步身份验证
姓名:
密码:
9. 将工程重新部署到服务器,启动服务器。打开 IE 浏览器在地址栏输入 http://localhost: 8080/ajax/UserCheck.jsp,看到如下视图: 图 5-5 显示 UserCheck.jsp 内容 10. 在用户名中输入“admin”后,将光标放到密码框内,看到如下视图: Java 框架 - 346 - 翰子昂系列理论教材 图 5-6 输入“admin”后呈现内容 输入其它用户名后,会看到如下视图: 图 5-7 输入其他用户名后呈现内容 通过上面示例就实现了很多论坛注册时用户名不能相同的应用。 4. 相关理论知识 4.1 什么是 AJAX,为什么要使用 AJAX? 1. 什么是 AJAX AJAX(Asynchronous JavaScript and XML)其实是多种技术的综合,包括 JavaScript、 XHTML 和 CSS、DOM、XML 和 XSTL、XMLHttpRequest。其中,使用 XHTML 和 CSS 标 准化呈现,使用 DOM 实现动态显示和交互,使用 XML 和 XSTL 进行数据交换与处理,使用 XMLHttpRequest 对象进行异步数据读取,使用 JavaScript 绑定和处理所有数据。 在 AJAX 提出之前,业界对于上述技术都只是单独的使用,没有综合使用,也是由于之 前的技术需求所决定的。随着应用的广泛,AJAX 在开发中起到越来越重要的作用。 2. 为什么要使用 AJAX 传统的 Web 应用采用同步交互过程,这种情况下,用户首先向 HTTP 服务器触发一个行 为或请求的呼求。反过来,服务器执行某些任务,再向发出请求的用户返回一个 HTML 页面。 这是一种不连贯的用户体验,服务器在处理请求的时候,用户多数时间处于等待的状态,屏 幕内容也是一片空白。如图所示: 专题五 Ajax 翰子昂系列理论教材 - 347 - 图 5-8 传统 Web 客户端与服务器交互过程 尤其是当负载比较大,响应时间要很长,1 分钟、2 分钟……钟的时候,这种等待就不可 忍受了。严重的,超过响应时间,服务器干脆告诉你页面不可用。另外,某些时候,我只是 想改变页面一小部分的数据,那为什么我们必须重新加载整个页面呢?当软件设计越来越讲 究人性化的时候,这么糟糕的用户体验简直与这种原则背道而驰。为什么总是要让用户等待 服务器取数据呢?至少,我们应该减少用户等待的时间。这时候迫切需要一种技术来解决这 种问题,AJAX 孕育而生。 与传统的 Web 应用不同,AJAX 采用异步交互过程。AJAX 在用户与服务器之间引入一 个中间媒介,从而消除了网络交互过程中的处理—等待—处理—等待缺点。用户的浏览器在 执行任务时即装载了 AJAX 引擎。AJAX 引擎用 JavaScript 语言编写,通常藏在一个隐藏的框 架中。它负责编译用户界面及与服务器之间的交互。AJAX 引擎允许用户与应用软件之间的 交互过程异步进行,独立于用户与网络服务器间的交流。现在,可以用 JavaScript 调用 AJAX 引擎来代替产生一个 HTTP 的用户动作,内存中的数据编辑、页面导航、数据校验这些不需 Java 框架 - 348 - 翰子昂系列理论教材 要重新载入整个页面的需求可以交给 AJAX 来执行。AJAX 交互原理,如图所示: 图 5-9 AJAX 客户端与服务器交互过程 使用 AJAX,开发人员、终端用户带来可见的便捷: z 减轻服务器的负担。AJAX 的原则是“按需取数据”,可以最大程度的减少冗余请求和 响应对服务器造成的负担。 z 无刷新更新页面,减少用户心理和实际的等待时间。特别是,当要读取大量的数据 的时候,不用像 Reload 那样出现白屏的情况,AJAX 使用 XMLHTTP 对象发送请求 专题五 Ajax 翰子昂系列理论教材 - 349 - 并得到服务器响应,在不重新载入整个页面的情况下用 JavaScript 操作 DOM 最终更 新页面。所以在读取数据的过程中,用户所面对的不是白屏,是原来的页面内容(也 可以加一个 Loading 的提示框让用户知道处于读取数据过程),只有当数据接收完 毕之后才更新相应部分的内容。这种更新是瞬间的,用户几乎感觉不到。 z 带来更好的用户体验。 z 可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理, 减轻服务器和带宽的负担,节约空间和宽带租用成本。 z 可以调用外部数据。 z 基于标准化的并被广泛支持的技术,不需要下载插件或者小程序。 z 进一步促进页面呈现和数据的分离。 4.2 AJAX 使用的技术 AJAX 不是一项技术,而是几项技术的整合。其中最重要的是 Javascript、XMLHttp- Request、DOM 和 XML 四项技术。 XMLHttpRequest 对象 XMLHttpRequest 是 XMLHTTP 组件的对象,通过这个对象,AJAX 可以像桌面应用程序 一样只同服务器进行数据层面的交换,而不用每次都刷新界面,也不用每次将数据处理的工 作都交给服务器来做;这样既减轻了服务器负担又加快了响应速度、缩短了用户等待的时间。 IE5.0开始,开发人员可以在 Web 页面内部使用 XMLHTTP ActiveX 组件扩展自身的功 能,不用从当前的 Web 页面导航就可以直接传输数据到服务器或者从服务器接收数据。,Moz illa1.0 以及 NetScape7 则是创建继承 XML 的代理类 XMLHttpRequest。对于大多数情况,X- MLHttpRequest 对象和 XMLHTTP 组件很相似,方法和属性类似,只是部分属性不同。 XMLHttpRequest 对象初始化: XMLHttpRequest 对象的方法: 方法 描述 abort() 停止当前请求 getAllResponseHeaders() 作为字符串返回完整的 headers getResponseHeader("headerLabel") 作为字符串返回单个的 header 标签 open("method","URL"[,asyncFlag[,"userName"[, "password"]]]) 设置未决的请求的目标 URL,方法,和其他参数 Java 框架 - 350 - 翰子昂系列理论教材 send(content) 发送请求 setRequestHeader("label", "value") 设置 header 并和请求一起发送 XMLHttpRequest 对象的属性: 属性 描述 onreadystatechange 状态改变的事件触发器 readyState 对象状态(integer): 0 = 未初始化 1 = 读取中 2 = 已读取 3 = 交互中 4 = 完成 responseText 服务器进程返回数据的文本版本 responseXML 服务器进程返回数据的兼容 DOM 的 XML 文档对 象 status 服务器返回的状态码, 如:404 = "文件未找到" 、 200 ="成功" statusText 服务器返回的状态文本信息 JavaScript JavaScript 一直被定位为客户端的脚本语言,应用最多的地方是表单数据的校验。现在, 可以通过 JavasScript 操作 XMLHttpRequest,来跟数据库打交道。 DOM DOM(Document Object Model)是 提 供 给 HTML 和 XML 使用的一组 API,提供了文件 的表述结构,并可以利用它改变其中的内容和可见物。脚本语言通过 DOM 才可以跟页面进 行交互。Web 开发人员可操作及建立文件的属性、方法以及事件都以对象来展现。比如: document 就代表页面对象本身。 DOM 中的 HTML 文档树 在 DOM 中,HTML 跟 XML 一样是一种树形结构的文档,是根(root)节点,、<body>是<html>的子(children)节点,互相之间是兄弟(sibling)节点;<bo dy>下面才是子节点<table>、<span>、<p>等等。如图所示: 专题五 Ajax 翰子昂系列理论教材 - 351 - 图 5-10 DOM 中的 HTML 文档 DOM 中,HTML 文档各个节点被视为各种类型的 Node 对象。每个 Node 对象都有自己 的属性和方法,利用这些属性和方法可以遍历整个文档树。由于 HTML 文档的复杂性,DO M 定义了 nodeType 来表示节点的类型。这里列出 Node 常用的几种节点类型: 接口 nodeType 常量 nodeType 值 备注 Element Node.ELEMENT_NODE 1 元素节点 Text Node.TEXT_NODE 3 文本节点 Document Node.DOCUMENT_NODE 9 document Comment Node.COMMENT_NODE 8 注释的文本 DocumentFragment Node.DOCUMENT_FRAGMENT_NODE 11 document 片断 Attr Node.ATTRIBUTE_NODE 2 节点属性 下面就对常用的元素进行分析: 1. DOM 树的根节点是个 Document 对象,该对象的 documentElement 属性引用表示文档 根元素的 Element 对象(对于 HTML 文档,这个就是<html>标记)。Javascript 操作 HTML 文档的时候,document 即指向整个文档,<body>、<table>等节点类型即为 Element。Comme nt 类型的节点则是指文档的注释 Document 定义的方法大多数是生产型方法,主要用于创建可以插入文档中的各种类型的 节点。常用的 Document 方法有: 方法 描述 createAttribute() 用指定的名字创建新的 Attr 节点。 createComment() 用指定的字符串创建新的 Comment 节点。 createElement() 用指定的标记名创建新的 Element 节点。 createTextNode() 用指定的文本创建新的 TextNode 节点。 getElementById() 返回文档中具有指定 id 属性的 Element 节点。 getElementsByTagName() 返回文档中具有指定标记名的所有 Element 节点。 2. 对于 Element 节点,可以通过调用 getAttribute()、setAttribute()、removeAttribute()方法 来查询、设置或者删除一个 Element 节点的性质,比如<table>标记的 border 属性。下面列出 Java 框架 - 352 - 翰子昂系列理论教材 Element 常用的属性: 属性 描述 tagName 元素的标记名称,比如<p>元素为 P。HTML 文档返回的 tabName 均为大写。 Element 常用的方法: 方法 描述 getAttribute() 以字符串形式返回指定属性的值。 getAttributeNode() 以 Attr 节点的形式返回指定属性的值。 getElementsByTabName() 返回一个 Node 数组,包含具有指定标记名的所有 Element 节点的子 孙节点,其顺序为在文档中出现的顺序。 hasAttribute() 如果该元素具有指定名字的属性,则返回 true。 removeAttribute() 从元素中删除指定的属性。 removeAttributeNode() 从元素的属性列表中删除指定的 Attr 节点。 setAttribute() 把指定的属性设置为指定的字符串值,如果该属性不存在则添加一个 新属性。 setAttributeNode() 把指定的 Attr 节点添加到该元素的属性列表中。 3. Attr 对象代表文档元素的属性,有 name、value 等属性,可以通过 Node 接口的 attributes 属性或者调用 Element 接口的 getAttributeNode()方法来获取。不过,在大多数情 况下,使用 Element 元素属性的最简单方法是 getAttribute()和 setAttribute()两个方法,而不是 Attr 对象。 4. 使用 DOM 操作 HTML 文档 Node 对象定义了一系列属性和方法,来方便遍历整个文档。用 parentNode 属性和 child Nodes[]数组可以在文档树中上下移动;通过遍历 childNodes[]数组或者使用 firstChild 和 next Sibling 属性进行循环操作,也可以使用 lastChild 和 previousSibling 进行逆向循环操作,也可 以枚举指定节点的子节点。而调用 appendChild()、insertBefore()、removeChild()、replaceChil d()方法可以改变一个节点的子节点从而改变文档树。 需要指出的是,childNodes[]的值实际上是一个 NodeList 对象。因此,可以通过遍历 chil dNodes[]数组的每个元素,来枚举一个给定节点的所有子节点;通过递归,可以枚举树中的所 有节点。下表列出了 Node 对象的一些常用属性和方法。 Node 对象常用属性: 属性 描述 attributes 如果该节点是一个 Element,则以 NamedNodeMap 形式返回该元素的属性。 childNodes 以 Node[]的形式存放当前节点的子节点。如果没有子节点,则返回空数组。 firstChild 以 Node 的形式返回当前节点的第一个子节点。如果没有子节点,则为 null。 lastChild 以 Node 的形式返回当前节点的最后一个子节点。如果没有子节点,则为 null。 nextSibling 以 Node 的形式返回当前节点的兄弟下一个节点。如果没有这样的节点,则返回 null。 nodeName 节点的名字,Element 节点则代表 Element 的标记名称。 nodeType 代表节点的类型。 专题五 Ajax 翰子昂系列理论教材 - 353 - 属性 描述 parentNode 以 Node 的形式返回当前节点的父节点。如果没有父节点,则为 null。 previousSibling 以 Node 的形式返回紧挨当前节点、位于它之前的兄弟节点。如果没有这样的节点, 则返回 null。 Node 对象常用方法: 方法 描述 appendChild() 通过把一个节点增加到当前节点的 childNodes[]组,给文档树增加节点。 cloneNode() 复制当前节点,或者复制当前节点以及它的所有子节点。 hasChildNodes() 如果当前节点拥有子节点,则将返回 true。 insertBefore() 给文档树插入一个节点,位置在当前节点的指定子节点之前。如果该节点已经 存在,则删除之再插入到它的位置。 removeChild() 从文档树中删除并返回指定的子节点。 replaceChild() 从文档树中删除并返回指定的子节点,用另一个节点替换它。 5. 遍历文档的节点示例 建立 node.html 文件,代码如下: <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> <title>无标题文档 开始统计 运行 node.html 文件,结果如下: 图 5-11 node.html 文件运行结果 XML 通过 XML(Extensible Markup Language),可以规范的定义结构化数据,是网上传输 的数据和文档符合统一的标准。用 XML 表述的数据和文档,可以很容易的让所有程序共享。 在数据表示方面,XML 文档更加结构化。DOM 在支持 HTML 的基础上提供了一系列的 API,支持针对 XML 的访问和操作。利用这些 API,我们可以从 XML 中提取信息,动态的 创建这些信息的 HTML 呈现文档。处理 XML 文档,通常遵循“加载 XML 文档提取信息,加 工信息,创建 HTML 文档的过程。下面的例子演示了如何加载并处理 XML 文档。 建立一个名为 employees.xml 文件,代码如下: Programmer 32768 Sales 70000 CEO 100000 专题五 Ajax 翰子昂系列理论教材 - 355 - 建立一个名为 test.html 文件,代码如下: 无标题文档 运行 test.html 文件,结果如下: 图 5-12 test.html 运行结果 4.3 AJAX 开发流程 这里,我们通过一步步的解析,来形成一个发送和接收 XMLHttpRequest 请求的程序框架。 AJAX 实质上也是遵循 Request/Server 模式,所以这个框架基本的流程也是:对象初始化 a 发 送请求 a 服务器接收,a 服务器返回 a 客户端接收 a 修改客户端页面内容。只不过这个过程是 异步的。 1. 初始化对象并发出 XMLHttpRequest 请求。 2. 为了让 JavaScript 可以向服务器发送 HTTP 请求,必须使用 XMLHttpRequest 对象。使 用之前,要先将 XMLHttpRequest 对象实例化。之前说过,各个浏览器对这个实例化过程实现 不同。IE 以 ActiveX 控件的形式提供,而 Mozilla 等浏览器则直接以 XMLHttpRequest 类的形 式提供。为了让编写的程序能够跨浏览器运行,要这样写: if (window.XMLHttpRequest) { // Mozilla, Safari, ... http_request = new XMLHttpRequest(); } else if (window.ActiveXObject) { // IE http_request = new ActiveXObject("Microsoft.XMLHTTP"); } 有些版本的 Mozilla 浏览器处理服务器返回的未包含 XML MIME-type 头部信息的内容时 会出错。因此,要确保返回的内容包含 text/xml 信息。 http_request = new XMLHttpRequest(); 专题五 Ajax 翰子昂系列理论教材 - 357 - http_request.overrideMimeType('text/xml'); 3. 指定响应处理函数 接下来要指定当服务器返回信息时客户端的处理方式。只要将相应的处理函数名称赋给 XMLHttpRequest 对象的 onreadystatechange 属性就可以了。比如: http_request.onreadystatechange = processRequest; 需要指出的时,这个函数名称不加括号,不指定参数。也可以用 Javascript 即时定义函数 的方式定义响应函数。比如: http_request.onreadystatechange = function(){ }; 发出 HTTP 请求 指定响应处理函数之后,就可以向服务器发出 HTTP 请求了。这一步调用 XMLHttpRequ est 对象的 open 和 send 方法。 http_request.open('GET', 'http://www.example.org/some.file', true); http_request.send(null); open 的第一个参数是 HTTP 请求的方法,为 Get,Post 或者 Head。 open 的第二个参数是目标 URL。基于安全考虑,这个 URL 只能是同网域的,否则会提 示“没有权限”的错误。这个 URL 可以是任何的 URL,包括需要服务器解释执行的页面,不仅 仅是静态页面。目标 URL 处理请求 XMLHttpRequest 请求则跟处理普通的 HTTP 请求一样, 比如 JSP 可以用 request.getParameter(“”)或者 request.getAttribute(“”)来取得 URL 参数值。 open 的第三个参数只是指定在等待服务器返回信息的时间内是否继续执行下面的代码。 如果为 true,则不会继续执行,直到服务器返回信息。默认为 true。 按照顺序,open 调用完毕之后要调用 send 方法。send 的参数如果是以 Post 方式发出的 话,可以是任何想传给服务器的内容。不过,跟 form 一样,如果要传文件或者 Post 内容给服 务器,必须先调用 setRequestHeader 方法,修改 MIME 类别。如下: http_request.setRequestHeader(“Content-Type”,”application/x-www-form-urlencoded”); 这时资料则以查询字符串的形式列出,作为 sned 的参数,例如: name=value&anothername=othervalue&so=on 4. 处理服务器返回的信息 在第二步我们已经指定了响应处理函数,这一步,来看看这个响应处理函数都应该做什 么。 首先,它要检查 XMLHttpRequest 对象的 readyState 值,判断请求目前的状态。参照前文 的属性表可以知道,readyState 值为 4 的时候,代表服务器已经传回所有的信息,可以开始处 理信息并更新页面内容了。如下: if (http_request.readyState == 4) { //信息已经返回,可以开始处理 } else { //信息还没有返回,等待 Java 框架 - 358 - 翰子昂系列理论教材 } 服务器返回信息后,还需要判断返回的 HTTP 状态码,确定返回的页面没有错误。所有 的状态码都可以在 W3C 的官方网站上查到。其中 200 代表页面正常。 if (http_request.status == 200) { //页面正常,可以开始处理信息 } else { //页面有问题 } XMLHttpRequest 对成功返回的信息有两种处理方式: responseText,将传回的信息当字符串使用。 responseXML,将传回的信息当 XML 文档使用,可以用 DOM 处理。 5. 总结上面的步骤,我们整理出一个初步的开发框架,供以后调用。这里将服务器返回 的信息用 window.alert,以字符串的形式显示出来。 专题五 Ajax 翰子昂系列理论教材 - 365 - 12. 在 IE 中输入地址 http://localhost:8080/dwr/index.jsp,到页面如图所示: 图 5-19 运行 index.jsp 后的结果 13. 点击“测试”按钮,得到结果如图所示: 图 5-20 点击“测试”按钮的结果 这样我们就完成了一个简单的 Hello World 程序的应用。 DWR 框架是非常优秀的 AJAX 框架,其应用很广泛。我们已经初次尝试了一个简单应用, 下面我们一起尝试一个在实际工作中经常用到的案例,使用 DWR 框架实现。 3.2 用 DWR 框架建立二级菜单联动的应用 1. 新建一个名为“dynamic”的 Web 工程。如图所示: Java 框架 - 366 - 翰子昂系列理论教材 图 5-21 新建“dynamic”Web 工程 2. 在工程中建立名为“com.dwr.service”包,在该包内建立 Bike 类。代码如下: package com.dwr.service; import java.util.Map; import java.util.TreeMap; public class Bike { private Map bikes; public Bike() { bikes = new TreeMap(); bikes.put("2000", new String[] { "2000 T1", "2000 T2", "2000 T3" }); bikes.put("2001", new String[] { "2001 A1", "2001 A2" }); bikes.put("2002", new String[] { "2002 BW1", "2002 BW2", "2002 BW" }); bikes.put("2003", new String[] { "2003 S320" }); bikes.put("2004", new String[] { "2004 TA1", "2004 TA2", "2004 TA3" }); } public String[] getYears() { String[] keys = new String[bikes.size()]; int i = 0; for (String key : bikes.keySet()) { keys[i++] = key; } return keys; } 专题五 Ajax 翰子昂系列理论教材 - 367 - public String[] getBikes(String year) { return bikes.get(year); } } 3. 将 dwr.jar 和 commons-logging-1.1.1.jar 文件,复制到 WEB-INF/lib 目录下。 4. 配置 web.xml 文件。在 web.xml 加入如下代码,进行 DWR 框架配置。 dwr-invoker org.directwebremoting.servlet.DwrServlet debug true dwr-invoker /dwr/* 5. 编写 dwr.xml 文件。 6. 在 WEB-INF 目录下建立“js”文件夹,在“js”文件夹下创建 bike.js 文件。代码如下: function refreshYearList() { Bike.getYears(populateYearList); } function populateYearList(list) { DWRUtil.removeAllOptions("years"); DWRUtil.addOptions("years", list); Java 框架 - 368 - 翰子昂系列理论教材 refreshBikeList(); } function refreshBikeList() { var year = $("years").value; Bike.getBikes(year, populateBikeList); } function populateBikeList(list) { DWRUtil.removeAllOptions("bikes"); DWRUtil.addOptions("bikes", list); } 7. 在 WEB-INF 下建立 bike.html 文件。代码如下: Insert title here 年份:

型号:
8. 将工程部署到 tomcat,启动 tomcat。在 IE 中输入地址 http://localhost:8080/dynamic/bi ke.html,得到页面如下: 专题五 Ajax 翰子昂系列理论教材 - 369 - 图 5-22 运行 bike.html 后结果 9. 选择不同的“年份”,可以得到属于本年份的“型号”。如图所示: 图 5-23 选择不同“年份”后结果 这样我们就轻松的完成了二级菜单的联动。 4. 相关理论知识 4.1 什么是 DWR DWR 是一个可以允许你去创建 AJAX Web 站点的 Java 开源库,它可以通过浏览器端的 JavaScript 代码去调用服务器端的 Java 代码,看起来就像是 Java 代码运行在浏览器上一样。 DWR 是一个完整的异步 AJAX 框架,它隐藏了 XMLHttpRequest 对象,程序员在开发过程中 不需要接触 XMLHttpRequest 对象就可以向服务器发送异步请求并通过回调方式处理服务器 的返回值。 DWR 包含两个主要部分: z 运行在服务器端的 servlet 控制器(DwrServlet),它负责接收请求,调用相应业 务逻辑进行处理,向客户端返回响应。 z 运行在浏览器端的 JavaScript,它负责向服务器端发送请求,接收响应,动态更新 页面。 DWR 工作原理是通过动态的把 Java 类生成为 JavaScript。它的代码就像 Ajax 魔法一样, Java 框架 - 370 - 翰子昂系列理论教材 你感觉调用就像发生在浏览器端,但是实际上代码调用发生在服务器端,DWR 负责数据的传 递和转换。这种从 JavaScript 到 Java 的远程调用功能的方式使 DWR 用起来有种非常像 RMI 或者 SOAP 的常规 RPC 机制,而且 DWR 的优点在于不需要任何的网页浏览器插件就能运行 在网页上。 Java 从根本上讲是同步机制,然而 Ajax 却是异步的。所以你调用远程方法时,当数据已 从网络上返回的时候,你要提供有回调(callback)来接收数据。 图 5-24 显示 DWR 如何选择一个下拉列表的内容作为 JavaScript 的 onclick 事件的结果 4.2 web.xml 文件配置 1. 主要配置 在 web.xml 中配置 DWR servlet,没有它 DWR 就不起作用。 dwr-invoker org.directwebremoting.servlet.DwrServlet dwr-invoker /dwr/* 注意:在 DWR2.x 里,DwrServlets 是类 org.directwebremoting.servlet.DwrServlet,尽管 uk.ltd.getahead.dwr.DWRServlet 仍然可以用。但在 DWR 1.x 中你不得不使用后者。 有些额外的 servlet 参数,在有些地方很重要,尤其是 debug 参数。这个参数扩展 DWR 的标准结构是使用。放在内,就像如下使用 : 专题五 Ajax 翰子昂系列理论教材 - 371 - dwr-invoker org.directwebremoting.servlet.DwrServlet debug true dwr-invoker /dwr/* 2. 常用参数列表 allowGetForSafariButMakeForgeryEasier 开始版本:2.0 默认值:false 描述:设置成 true 使 DWR 工作在 Safari 1.x ,会稍微降低安全性。 crossDomainSessionSecurity 开始版本:2.0 默认值:true 描述:设置成 false 使能够从其他域进行请求。注意,这样做会在安全性上有点冒险。 Debug 开始版本:1.0 默认值:false 描述:设置成 true 使 DWR 能够 debug 和进入测试页面 scriptSessionTimeout 开始版本:2.0 默认值:1800000(30 分钟) 描述:script session 的超时设置 maxCallCount 开始版本:2.0rc2 和 1.1.4 默认值:20 描述:一次批量(batch)允许最大的调用数量。(帮助保护 Dos 攻击) 4.3 dwr.xml 文件配置 dwr.xml 是你用来配置 DWR 的文件,默认是将其放入 WEB-INF 文件夹。 Java 框架 - 372 - 翰子昂系列理论教材 dwr.xml 基本结构: ... 1. 标签 这个初始化部分申明被用来创建远程 beans 而且这个类能被用来以某种过程转换。大多 数例子你将不需要用它,如果你想去定义一个新的 Creator 或者 Converter,就要在此声明。 在 init 部分里有的定义只是告诉 DWR 这些扩展类的存在,给出了如何使用的信息。这时它们 还没有被使用。这种方式很像 Java 中的 import 语句。多数类需要在使用前先 import 一下,但 是只有 import 语句并不表明这个类已经被使用了。每一个 creator 和 converter 都用 id 属性, 以便后面使用。 2. 标签 allow 部分定义了 DWR 能够创建和转换的类。 3. 标签 每一个在类中被调用的方法需要一个有若干类型的 creator,使用“new”关键字 或者 Spring 框架等。create 元素是如下的结构 ... creator 属性。 new:Java 用“new”关键字创造对象 是 DWR 默认的 creator。 专题五 Ajax 翰子昂系列理论教材 - 373 - 这个 creator 将使用默认构造器创建类的实例,以下是用 new 创建器的好处:安全:DW R 创造的对象生存的时间越短,多次调用中间的值不一致的错误机会越少,内存消耗低。如 果你的站点用户量非常大,这个创造器可以减少 VM 的内存溢出。 javascript 属性 在 JavaScript 里给你创建的对象命名。避免使用 JavaScript 保留字。这个名字将在页面里 作为 js 被导入。例如本章第一个 Hello World 应用。dwr.xml 配置如下: 在 index.jsp 对应引用中,代码如下: 使用 DWREngine.setX() 函数来设置全局相关的一些属性。 4.5 util.js 功能 util.js 包含了一些工具函数来帮助你用 JavaScript 数据(例如从服务器返回的数据)来更新 你的 Web 页面。你可以在 DWR 以外使用它,因为它不依赖于 DWR 的其他部分。你可以下 载整个 DWR 或者单独下载。它有 4 个基本的操作页面的函数:getValue[s]()和 setValue[s]() 可以操作大部分 HTML 元素除了 table,list 和 image。getText()可以操作 select,list。要修改 table 可以用 addRows()和 removeAllRows()。要修改列表(select 列表和 ul,ol 列表)可以用 addO ptions()和 removeAllOptions()。 还有一些其他功能不是 DWRUtil 的一部分。但它们也很有用, 它们可以用来解决一些小问题,但是它们不是对于所有任务都通用的。 下面介绍 util.js 中常用的函数: 1. $() 函数。 大致上的讲:$ = document.getElementById。因为在 Ajax 程序中,你会需要写很多这样 的语句,所以使用$()会更简洁。通过指定的 id 来查找当前 HTML 文档中的元素,如果传递 给它多个参数,它会返回找到的元素的数组。所有非 String 类型的参数会被原封不动的返回。 这个函数的灵感来自于 prototype 库,但是它可以在更多的浏览器上运行。 2. addOptions and removeAllOptions DWR 的一个常遇到的任务就是根据选项填充选择列表。下面的例子就是根据输入填充列 表。下面将介绍 DWRUtil.addOptions()的几种使用方法。如果你希望在你更新了 select 以后, 它仍然保持原来的选择,你要像下面这样做: var sel = DWRUtil.getValue(id); DWRUtil.removeAllOptions(id); DWRUtil.addOptions(id, ...); DWRUtil.setValue(id, sel); DWRUtil.addOptions 有 5 种模式 : 数组: DWRUtil.addOptions(selectid, array)会创建一堆 option,每个 option 文字和值都是数组元素中的值。 对象数组(指定 text):DWRUtil.addOptions(selectid, data, prop)用每个数组元素创造一 个 option,option 的值和文字都是在 prop 中指定对象的属性。 对象数组(指定 text 和 value 值):DWRUtil.addOptions(selectid,array,Valueprop,tex tprop) 用每个数组元素创造一个 option,option 的值是对象的 valueprop 属性,option 的文字 是对象的 textprop 属性。 对象:DWRUtil.addOptions(selectid, map, reverse)用每个属性创建一个 option。对象属性 名用来作为 option 的值,对象属性值用来作为属性的文字,这听上去有些不对,但是事实上 却是正确的方式。如果 reverse 参数被设置为 true,那么对象属性值用来作为选项的值。 专题五 Ajax 翰子昂系列理论教材 - 375 - 对象的 Map:DWRUtil.addOptions(selectid,map,valueprop,textprop) 用 map 中的每一 个对象创建一个 option。用对象的 valueprop 属性做为 option 的 value,用对象的 textprop 属性 做为 option 的文字。 ol 或 ul 列表:DWRUtil.addOptions(ulid,array)用数组中的元素创建一堆 li 元素,他们的 innerHTML 是数组元素中的值。这种模式可以用来创建 ul 和 ol 列表。 3. addRows and removeAllRows DWR 通过这两个函数来帮你操作 table:DWRUtil.addRows()和 DWRUtil.removeAllRows ()。这个函数的第一个参数都是 table、tbody、thead、tfoot 的 id。一般来说最好使用 tbody, 因为这样可以保持你的 header 和 footer 行不变,并且可以防止 Internet Explorer 的 bug。 DWRUtil.removeAllRows() DWRUtil.removeAllRows(id); 描述:通过 id 删除 table 中所有行。 参数列表: id:table 元素的 id(最好是 tbody 元素的 id) DWRUtil.addRows() DWRUtil.addRows(id, array, cellfuncs, [options]); 描述:向指定 id 的 table 元素添加行。它使用数组中的每一个元素在 table 中创建一行。 然后用 cellfuncs 数组中的函数创建一个列。单元格是依次用 cellfunc 根据数组中的元素创建 出来的。 DWR1.1 开始,addRows()也可以用对象做为数据。如果你用一个对象代替一个数组来创 建单元格,这个对象会被传递给 cell 函数。 参数列表: Id:table 元素的 id(最好是 tbody 元素的 id)。 array:数组(DWR1.1 以后可以是对象),做为更新表格数据。 Cellfuncs:函数数组,从传递过来的行数据中提取单元格数据。 Options:一个包含选项的对象(见下面) 选项包括:rowCreator:一个用来创建行的函 数(例如,你希望 tr 加个 css)。默认是返回一个 document.createElement("tr")。cellCreator:一 个用来创建单元格的函数(例如:用 th 代替 td)默认返回一个。 document.createElement("td") 4. getValue DWRUtil.getValue(id)是 setValue()对应的“读版本”。它可以从 HTML 元素中取出其中 的值,而你不用管这个元素是 select 列表还是一个 div。这个函数能操作大多数 HTML 元素, 包括 select(去处当前选项的值而不是文字)、input 元素(包括 textarea)、div 和 span。 5.getValues Java 框架 - 376 - 翰子昂系列理论教材 getValues()和 getValue()非常相似,除了输入的是包含 name/value 对的 Javascript 对象。 name 是 HTML 元素的 ID,value 会被更改为这些 ID 对象元素的内容。这个函数不会返 回对象,它只更改传递给它的值。从 DWR1.1 开始 getValues()可以传入一个 HTML 元素(一个 DOM 对象或者 id 字符串),然后从它生成一个 reply 对象。 6. onReturn 当按下 return 键时,得到通知。当表单中有 input 元素,触发 return 键会导致表单被提交, 这往往不是你想要的,所以你需要触发一些 JavaScript 事件,不幸的是不同的浏览器处理这个 事件的方式不一样。而 DWRUtil.onReturn 可以修复这个差异,如果你需要提交表单中的一个 元素,可以用这样代码实现: 7. selectRange 选择一个输入框中的一定范围的文字。你可能为了实现类似“Google suggest”类型的功 能而需要选择输入框中的一定范围的文字,但是不同浏览器间选择的模型不一样。但是 DWR Util 函数可以帮你实现。 DWRUtil.selectRange(ele, start, end) 8. setValue DWRUtil.setValue(id,value)根据第一个参数中指定的 id 找到相应元素,并根据第二个参 数改变其中的值。 这个函数能操作大多数 HTML 元素包括 select、input 元素(包括 textarea)、 div 和 span。 9.setValues setValues()和 setValue()非常相似,除了输入的是包含 name/value 对的 Javascript 对象。n ame 是 HTML 元素的 ID,value 是你想要设置给相应的元素的值。 4.6 综合分析 Hello World 应用 1. 新建的 Service 类中有一个 sayHello 方法这个方法就是作为远程调用的服务端方法, 这个 Service 类也就是远程调用的类。 2. dwr.xml 是 DWR 用来配置服务端类和浏览器端 JavaScript 对象之间的映射。在里配置映射关系,示范代码如下: 专题五 Ajax 翰子昂系列理论教材 - 377 - javascript="service"是在浏览器端的 JavaScript 映射的对象名,但不要用 JavaScript 里的关 键字,creator="new"是表示这个类是 dwr 自己创建的,如果 creator="new"那么就必须有下面 的,最后 scope=”application”是说这个 pojo 类的范围,和 Jsp 是一样的。 3. 创建 index.jsp。注意的是在 index.jsp 中导入 js 文件的时候注意顺序和路径命名规律, 如果自己写 hello.js 一定放在后面,因为要调用其它 js 文件中的函数(例如实践知识第二个示 例)。当我们点击按钮的时候,实质调用的是服务器端对应的 Service 类的 sayHello 方法,可 以向函数传递参数,不过要在最后加一个回调函数。 service.sayHello("dwr",callBackHello); 4. 在 web.xml 里加入 DWRServlet 的配置一是为了远程调用,二是自动生成了 文件。 同学们根据相关的理论知识,分析实践知识第二个示例。 5. 实验 1. 完成实践知识示例一,使用 DWR 框架构建一个 HelloWorld 应用。(30 分钟) 2. 完成实践知识示例二,使用 DWR 框架构建一个二级联动菜单应用。(60 分钟) 6. 作业 1. 将本章实践知识的联动菜单示例,改成从数据库中查找数据进行联动菜单的实现。 Item 表和 classes 表的 ddl 语句。一个 item 中有多个 classes。 if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[item]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[item] GO CREATE TABLE [dbo].[item] ( [tid] [int] NOT NULL , [tname] [varchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL ) ON [PRIMARY] GO if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[classes]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) Java 框架 - 378 - 翰子昂系列理论教材 drop table [dbo].[classes] GO CREATE TABLE [dbo].[classes] ( [cid] [int] NOT NULL , [cname] [varchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL , [tid] [int] NOT NULL ) ON [PRIMARY] GO 专题六 Web Service 基础知识 翰子昂系列理论教材 - 379 - 专题六 Web Service 基础知识 1. 教学目标 1.1 了解 WebService 1.2 了解 WSDL 1.3 了解 SOAP 协议 1.4 了解 UDDI 1.5 了解 XFire 1.6 掌握使用 XFire 开发 webservice 2. 工作任务 使用 XFire 构建简单的 WebService 应用。 3. 相关实践知识 用 XFire 构建 WebService 应用 1. 新建 Web Service 项目。如图所示: 图 6-1 建立 Web Service 工程 2. 将该工程命名为“helloworldservice”。如图所示: Java 框架 - 380 - 翰子昂系列理论教材 图 6-2 将工程名命名为“helloworldservice” 3. 点击【Next】。看到如下视图: 图 6-3 点击【Next】看到视图 4. 点击【Next】。看到如下视图: 专题六 Web Service 基础知识 翰子昂系列理论教材 - 381 - 图 6-4 点击【Next】看到视图 5. 点击【Finish】。看到工程结构如图所示: 图 6-5“helloworldservice”工程结构 6. 打开 web.xml 文件。web.xml 文件代码如下: XFireServlet org.codehaus.xfire.transport.http.XFireConfigurableServlet 0 Java 框架 - 382 - 翰子昂系列理论教材 XFireServlet /services/* index.jsp 在 web.xml 配置 org.codehaus.xfire.transport.http.XFireConfigurableServlet。 7. 打开 services.xml 文件。services.xml 文件代码如下: 8. 新建一个 Web Service。如图所示: 图 6-6 建立 Web Service 9. 点击【Next】。如图所示: 专题六 Web Service 基础知识 翰子昂系列理论教材 - 383 - 图 6-7 点击【Next】看到视图 10. 选中【Create new Java bean】,点击【Next】。如图所示: Java 框架 - 384 - 翰子昂系列理论教材 图 6-8 点击【Next】看到视图 11. 【Web service name】命名为“HelloWorld”。如图所示: 专题六 Web Service 基础知识 翰子昂系列理论教材 - 385 - 图 6-9 输入“HelloWorld”结果 此时可以看到【Service interface】命名为:“IHelloworld”;【Service impl class】命名为: “HelloWorldImpl”。 12. 点击【Java package】后的【New】,新建一个包,包命名为“hellos”。如图所示: 图 6-10 新建名为“hellos”包 13. 建包后结果如图所示: Java 框架 - 386 - 翰子昂系列理论教材 图 6-11 建包后结果视图 14. 点击【Finishi】。工程结构如图所示: 图 6-12 工程结构视图 15. 打开“HelloWorldImpl.java”文件。代码如下: package hellos; //Generated by MyEclipse public class HelloWorldImpl implements IHelloWorld { public String example(String message) { return message; } } 专题六 Web Service 基础知识 翰子昂系列理论教材 - 387 - 16. 打开“HelloWorld.java”文件。代码如下: package hellos; //Generated by MyEclipse public interface IHelloWorld { public String example(String message); } 17. 打开“services.xml”文件。代码如下: HelloWorld hellos.IHelloWorld hellos.HelloWorldImpl literal application 18. 将工程部署到 Tomcat 服务器上,然后启动 Tomcat。打开 IE,在浏览器地址栏输入 地址:http://localhost:8080/helloworldservice/services/HelloWorld?wsdl。而后看到页面内容如下: Java 框架 - 388 - 翰子昂系列理论教材 专题六 Web Service 基础知识 翰子昂系列理论教材 - 389 - 此时证明这个 Web Service 开发成功。 18. 测试这个 Web Service。点击 MyEclipse 的【Web Services Explorer】浏览器按钮。 如图所示: 图 6-13 【Web Services Explorer】浏览器按钮 19. 点击【Web Services Explorer】按钮后,在 MyEclipse 中可以看到如下视图: 图 6-14 MyEclipse 中【Web Services Explorer】视图 20. 点击【Web Services Explorer】视图中的“WSDL Main”按钮。如图所示: Java 框架 - 390 - 翰子昂系列理论教材 图 6-15 点击“WSDL Main”按钮后的视图 21. 在“WSDL URL Browse…”地址栏输入 http://localhost:8080/helloworldservice/servic es/HelloWorld?wsdl。如图所示: 图 6-16 在“WSDL URL Browse..”输入地址 22. 点击【Go】按钮。结果如图所示: 专题六 Web Service 基础知识 翰子昂系列理论教材 - 391 - 图 6-17 点击【Go】按钮后的视图 23. 点击【example】超连接。结果如图所示: Java 框架 - 392 - 翰子昂系列理论教材 图 6-18 点击【example】超链接后的视图 24. 在【Web Services Explorer】视图的文本框中输入“java web”,点击【Go】按钮。 如图所示: 图 6-19 在文本框中输入“java web”点击【Go】按钮 此时在【Status】视图中,可以看到输出结果。这时候证明 Web Service 开发成功。 25. 建立一个工程调用 Web 服务。建立一个“Java Project”命名为“helloworldservicecli ent”。如图所示: 专题六 Web Service 基础知识 翰子昂系列理论教材 - 393 - 图 6-20 新建“helloworldserivceclient”工程 26. 在“helloworldserviceclient”工程中建立名为“helloclient”的包。 27. 在“helloclient”包内新建【Web Service Client】。如图所示: Java 框架 - 394 - 翰子昂系列理论教材 图 6-21 新建【Web Service Client】 28. 点击【Next】。结果如图所示: 图 6-22 建立“Web Service Client”视图 29. 在【web service project】中选择,刚刚建立过的“helloworldserviceclient”工程。 30. 选中【WSDL URL】这项,在文本框中输入 http://localhost:8080/helloworldservice/se rvices/HelloWorld?wsdl 这个地址。 31.【Java package】为“helloclient”。 32. 将各项填写完毕后,结果如图所示: 专题六 Web Service 基础知识 翰子昂系列理论教材 - 395 - 图 6-23 在【Web Service Client】中进行的配置 33. 点击【Next】。结果如图所示: 注意:此处必须保证 Tomcat 服务器处于启动状态,否则会有错误信息。 Java 框架 - 396 - 翰子昂系列理论教材 图 6-24 点击【Next】按钮的结果 34. 点击【Next】按钮。结果如图所示: 专题六 Web Service 基础知识 翰子昂系列理论教材 - 397 - 图 6-25 点击【Next】按钮结果 35. 点击【Finish】按钮。查看工程结构如图所示: 图 6-26 helloworldserviceclient 工程目录 36. 打开“HelloWorldClient.java”文件。该文件代码如下: package helloclient; import java.net.MalformedURLException; import java.util.Collection; import java.util.HashMap; import javax.xml.namespace.QName; import org.codehaus.xfire.XFireRuntimeException; import org.codehaus.xfire.aegis.AegisBindingProvider; import org.codehaus.xfire.annotations.AnnotationServiceFactory; import org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations; import org.codehaus.xfire.client.XFireProxyFactory; import org.codehaus.xfire.jaxb2.JaxbTypeRegistry; import org.codehaus.xfire.service.Endpoint; import org.codehaus.xfire.service.Service; import org.codehaus.xfire.soap.AbstractSoapBinding; import org.codehaus.xfire.transport.TransportManager; public class HelloWorldClient { private static XFireProxyFactory proxyFactory = new XFireProxyFactory(); private HashMap endpoints = new HashMap(); private Service service0; public HelloWorldClient() { create0(); Endpoint HelloWorldPortTypeLocalEndpointEP = service0 .addEndpoint(new Java 框架 - 398 - 翰子昂系列理论教材 QName("http://hellos", "HelloWorldPortTypeLocalEndpoint"), new QName("http://hellos", "HelloWorldPortTypeLocalBinding"), "xfire.local://HelloWorld"); endpoints.put(new QName("http://hellos", "HelloWorldPortTypeLocalEndpoint"), HelloWorldPortTypeLocalEndpointEP); Endpoint HelloWorldHttpPortEP = service0 .addEndpoint(new QName("http://hellos", "HelloWorldHttpPort"), new QName("http://hellos", "HelloWorldHttpBinding"), "http://localhost:8080/helloworldservice/services/HelloWorld"); endpoints.put(new QName("http://hellos", "HelloWorldHttpPort"), HelloWorldHttpPortEP); } public Object getEndpoint(Endpoint endpoint) { try { return proxyFactory.create((endpoint).getBinding(), (endpoint).getUrl()); } catch (MalformedURLException e) { throw new XFireRuntimeException("Invalid URL", e); } } public Object getEndpoint(QName name) { Endpoint endpoint = ((Endpoint) endpoints.get((name))); if ((endpoint) == null) { throw new IllegalStateException("No such endpoint!"); } return getEndpoint((endpoint)); } public Collection getEndpoints() { return endpoints.values(); } private void create0() { TransportManager tm = (org.codehaus.xfire.XFireFactory.newInstance().getXFire().getTransportManag er()); HashMap props = new HashMap(); props.put("annotations.allow.interface", true); AnnotationServiceFactory asf = new AnnotationServiceFactory(new Jsr181WebAnnotations(), tm, new AegisBindingProvider(new 专题六 Web Service 基础知识 翰子昂系列理论教材 - 399 - JaxbTypeRegistry())) ; asf.setBindingCreationEnabled(false); service0 = asf.create((helloclient.HelloWorldPortType.class), props); { AbstractSoapBinding soapBinding = asf.createSoap11Binding(service0, new QName("http://hellos", "HelloWorldHttpBinding"), "http://schemas.xmlsoap.org/soap/http"); } { AbstractSoapBinding soapBinding = asf.createSoap11Binding(service0, new QName("http://hellos", "HelloWorldPortTypeLocalBinding"), "urn:xfire:transport:local"); } } public HelloWorldPortType getHelloWorldPortTypeLocalEndpoint() { return ((HelloWorldPortType)(this).getEndpoint(new QName("http://hellos", "HelloWorldPortTypeLocalEndpoint"))); } public HelloWorldPortType getHelloWorldPortTypeLocalEndpoint(String url) { HelloWorldPortType var = getHelloWorldPortTypeLocalEndpoint(); org.codehaus.xfire.client.Client.getInstance(var).setUrl(url); return var; } public HelloWorldPortType getHelloWorldHttpPort() { return ((HelloWorldPortType)(this).getEndpoint(new QName("http://hellos", "HelloWorldHttpPort"))); } public HelloWorldPortType getHelloWorldHttpPort(String url) { HelloWorldPortType var = getHelloWorldHttpPort(); org.codehaus.xfire.client.Client.getInstance(var).setUrl(url); return var; } public static void main(String[] args) { HelloWorldClient client = new HelloWorldClient(); Java 框架 - 400 - 翰子昂系列理论教材 //create a default service endpoint HelloWorldPortType service = client.getHelloWorldHttpPort(); //TODO: Add custom client code here // //service.yourServiceOperationHere(); System.out.println("test client completed"); System.exit(0); } } 37. 在“HelloWorldClient.java”中的 main 方法中加入测试代码。代码如下: public static void main(String[] args) { HelloWorldClient client = new HelloWorldClient(); //create a default service endpoint HelloWorldPortType service = client.getHelloWorldHttpPort(); //TODO: Add custom client code here // //service.yourServiceOperationHere(); System.out.println(service.example("这是我的第一个 webservice 测试程序")); System.out.println("test client completed"); System.exit(0); } 38. 运行“HelloWorldClient.java”,在控制台可以看到输出结果如下: 图 6-27 “HelloWorldClient.java”运行结果 通过以上步骤完成了一个简单的 Web Service 的开发与应用。 4. 相关理论知识 4.1 WebService 起源 Web 应用的巨大成功和不断发展,使其渗透到商业领域和个人生活的各个方面。人们只 专题六 Web Service 基础知识 翰子昂系列理论教材 - 401 - 要使用浏览器,就可以享受到各种各样的 Web 服务,例如网上购物、网上交易、网络游戏、 预定车票、网上聊天和交友等等。与此同时,由于 Web 技术所带来的优势(统一的客户端和 较好的维护性),使一些传统的应用纷纷转型到 B/S 结构上。 然而,在发展中,逐步暴露了一些问题。所有这些 Web 页面都是为人准备的,是让人去 阅读、去输入、去判断。因此各种反映视觉效果的内容占用了大量的网络带宽,例如各种图 片,字体信息,文字排版样式等。而真正含有高价值的一些信息,被深深埋在这些显示信息 中,很难被其他应用和程序所使用。更重要的是,各种 web 服务之间缺少交互和通讯的机制。 程序之间的互相通讯很重要吗?简单举一个例子。 假设你经常去国外出差,在你回国以后,第一件事就是费用报销了。而你们公司有这样 的财务规定,所有的报销款,都按报销当天的外汇比价进行结算。因此在你填写报销单的时 候必须先填写每一笔在各个国家的花销,然后上网查出当天的外汇比价,填写到报销单上。 剩下的事情也就不用你做了,你的报销单填写工具会自动进行换算和统计。 觉得有什么不妥吗?作为 IT 公司的员工,也许都有一个特点,计算机能做的事情,尽量 要计算机去做。外汇比价的查询可以让计算机自动去做嘛!然而,让你的程序自动去网页上 查找指定的外汇比价可不是一件容易的事。因为这些网页是给人阅读的,人眼和大脑的反应 速度有多快,它们可以从一整页信息中快速定位到你所要的内容,而且无论网页怎样变化和 改版都不会带来太大的影响。而应用程序想要做同样的事就差得太远了。因此,现在需要的 是专门为应用程序制定的 Web 服务。 随着应用程序之间通讯的需求越来越大,这就需要制定统一的标准和协议。HP 公司是最 先提出这个观点的公司,他们制定了有关“e-Speak”的标准来保证应用程序之间的交互,并 声称将成为下一代 Internet 信息交互的标准。而随后,MicroSoft 意识到此计划的美好前景, 便推出了.Net 战略;IBM 很快就发布了 Web Services Toolkit(WSTK),和 Web Services Dev elopment Environment(WSDE),申明对 Web Services 的全力支持。与此同时,Oracle 也开发 出自己的 Dynamic Services,并和 Oracle 8i Release 2 集成在一起。在这以后,W3C 统一制 定了 Web Services 的各种标准。而 SUN 公司在宣布了自己的 Web Services 的框架以后,将 Web Services 的标准溶入 J2EE 的环境,使 Web Services 有了广泛支持的基础和平台。 4.2 WebService 概念 使用 Web 服务技术,应用程序可以与平台和编程语言无关的方式相互通信。Web 服务是 一个软件接口,它描述了一组可以在网络上通过标准化的 XML 消息传递访问的操作。它使用 基于 XML 语言的协议来描述要执行的操作或者要与另一个 Web 服务交换的数据。在面向服 务的体系结构(Service-Oriented Architecture,SOA)中,一组以这种方式交互的 Web 服务定 义了特定的 Web 服务应用程序。 软件业最终会接受这样的事实:跨多个操作系统、编程语言和硬件平台集成软件应用程 序不可能由任何一种专门的环境来解决。传统上,这个问题一直是一个紧耦合问题,调用远 程网络的应用程序通过自己发出的函数调用和请求的参数与远程网络紧密地联系在一起。在 Web 服务出现之前,在大多数系统上,采用的是固定的接口,但对于不断变化的环境或需求, 这样做缺乏灵活性或适用性。 即 WebService 提供了基于文本(实际上是基于 XML)的方法来往返传递消息,这意味 着应用程序不仅不依赖计算机,而且也不依赖于操作系统和编程语言。只要双方遵循相同的 Java 框架 - 402 - 翰子昂系列理论教材 Web 服务标准,就不受各方运行何种软件的影响。 Webserviece 能够做什么 z 让任何平台上的用任何语言编写的服务进行交互。 z 将应用程序功能概念化成任务,从而形成面向任务的开发和工作流。这使得 更抽象的软件能够为工作在业务层面具有较少软件分析技术的用户所用。 z 允许松耦合,这意味着,每当其中某个或多个服务在设计或实现中发生改变 时,服务应用程序之间的交互不会因此而中断。 z 使现有的应用程序能适应不断变化的业务条件和客户需求。 z 向现有或遗留软件应用程序提供服务接口,而无需改变原来的应用程序,从 而使这些应用程序完全可以运行在这种服务环境下。 z 引入其他一些与原有功能无关的管理或操作管理功能,比如可靠性、责任性 和安全性等等,从而在业务计算环境中增加其通用性和实用性。 4.3 WebService 工作原理 Web Services 是通过一系列标准和协议来保证程序之间的动态链接。其中最基本的协议 包括:SOAP,WSDL,UDDI SOAP:是“Simple Object Access Protocol”的缩写,SOAP 是消息传递的协议,它规定 了 Web Services 之间是怎样传递信息的。简单的说,SOAP 规定了: 1. 传递信息的格式为 XML。这就使 Web Services 能够在任何平台上,用任何语言进行 实现。 2. 远程对象方法调用的格式。规定了怎样表示被调用对象以及调用的方法名称和参数类 型等。 3. 参数类型和 XML 格式之间的映射。这是因为,被调用的方法有时候需要传递一个复 杂的参数,例如,一个 Person 对象。怎样用 XML 来表示一个对象参数,也是 SOAP 所定义 的范围。 4. 异常处理以及其他的相关信息. WSDL:是“Web Services Description Language”的缩写。意如其名,WSDL 是 Web Services 的定义语言。当你实现了某种服务的时候(如:股票查询服务),为了让别的程序调 用,你必须告诉大家你的服务的接口。例如:服务名称、服务所在的机器名称、监听端口号、 传递参数的类型、个数和顺序、返回结果的类型等等。这样别的应用程序才能调用你的服务。 WSDL 协议就是规定了有关 Web Services 描述的标准。 UDDI:是 Universal Description, Discovery and Integration 的缩写。简单说,UDDI 用于 集中存放和查找 WSDL 描述文件,起着目录服务器的作用。 专题六 Web Service 基础知识 翰子昂系列理论教材 - 403 - 如上图,一个 Web Services 的生命周期是: 实现一个 Web Services,使其能够接受和响应 SOAP 消息(现在有很多工具都可以帮助 实现)。 撰写一个 WSDL 文件用于描述此 Web Services。(现在有很多工具可以自动生成 WSDL 文件)。 将此 WSDL 发布到 UDDI 上。 其他的应用程序(客户端)从 UDDI 上搜索到你的 WSDL。 根据你的 WSDL,客户端可以编写程序(现在有很多工具可以自动生成调用程序)调用 你的 Web Services。 4.4 什么是 SOAP SOAP 把 XML 的使用代码化为请求和响应参数编码模式,并用 HTTP 作传输。这似乎有 点抽象。具体地讲,一个 SOAP 方法可以简单地看作遵循 SOAP 编码规则的 HTTP 请求和响 应。一个 SOAP 终端则可以看作一个基于 HTTP 的 URL,它用来识别方法调用的目标。 SOAP 请求是一个 HTTP POST 请求。SOAP 请求的 content-type 必须用 text/xml。而且它 必须包含一个请求-URI。服务器怎样解释这个请求-URI 是与实现相关的,但是许多实现中可 能用它来映射到一个类或者一个对象。一个 SOAP 请求也必须用 SOAPMethodName HTTP 头 来指明将被调用的方法。简单地讲,SOAPMethodName 头是被 URI 指定范围的应用相关的方 法名,它是用#符作为分隔符将方法名与 URI 分割,如下: SOAPMethodName: urn:strings-com:IString#reverse 这个头表明方法名是 reverse,范围 URI 是 urn:strings-com:Istring。在 SOAP 中,规定方 法名范围的名域 URI 在功能上等同于在 DCOM 或 IIOP 中规定方法名范围的接口 ID。 简单的说,一个 SOAP 请求的 HTTP 体是一个 XML 文档,它包含方法中[in]和[in,out]参 数的值。这些值被编码成为一个显著的调用元素的子元素,这个调用元素具有 SOAPMethod Name HTTP 头的方法名和名域 URI。调用元素必须出现在标准的 SOAP<Envelope>和<Bo dy>元素。下面是一个最简单的 SOAP 方法请求: Java 框架 - 404 - 翰子昂系列理论教材 POST /string_server/Object17 HTTP/1.1 Host: 209.110.197.2 Content-Type: text/xml Content-Length: 152 SOAPMethodName: urn:strings-com:IString#reverse <Envelope> <Body> <m:reverse xmlns:m=‘urn:strings-com:IString‘> <theString>Hello, World</theString> </m:reverse> </Body> </Envelope> SOAPMethodName 头必须与<Body>下的第一个子元素相匹配,否则调用将被拒绝。这 允许防火墙管理员在不解析 XML 的情况下有效地过滤对一个具体方法的调用。 SOAP 响应的格式类似于请求格式。响应体包含方法的[out]和[in,out]参数,这个方法被编 码为一个显著的响应元素的子元素。这个元素的名字与请求的调用元素的名字相同,但以 Re sponse 后缀来连接。下面是对前面的 SOAP 请求的 SOAP 响应: 200 OK Content-Type: text/xml Content-Length: 162 <Envelope> <Body> <m:reverseResponse xmlns:m=‘urn:strings-com:IString‘> <result>dlroW ,olleH</result> </m:reverseResponse> </Body> </Envelope> 这里响应元素被命名为 reverseResponse,它是方法名紧跟 Response 后缀。要注意的是这 里是没有 SOAPMethodName HTTP 头的。这个头只在请求消息中需要,在响应消息中并不需 要。 4.5 什么是 WSDL Web Services Description Language 的缩写,是一个用来描述 Web 服务和说明如何与 Web 服务通信的 XML 语言。 它来描述下列问题的答案: z 您的在线业务提供什么服务? z 您如何调用业务服务? z 当用户调用您的业务服务时,该业务服务需要他/她提供什么信息? z 用户将如何提供这些必需信息? 专题六 Web Service 基础知识 翰子昂系列理论教材 - 405 - z 服务将以什么格式发送返回给用户的信息? 它主要由五个要素构成: Types:定义 WSDL 定义中所用到的数据类型,即 XML Schema Types。 Message:对一组消息的输入和输出参数的定义。 portType:定义 Web 服务的操作。 Binding:描述特定服务接口的协议、数据格式、安全性和其它属性。 Services:制定特定服务的 URL 和提供的调用接口,包含一组端口元素。 这样可以分析示例中的 WSDL 文件。 Java 框架 - 406 - 翰子昂系列理论教材 4.6 什么是 UDDI Universal Description Discovery and Integration 即统一描述、发现和集成协议。 UDDI 始于 2000 年,由 Ariba, IBM, Microsoft 和其他 33 家公司创立.UDDI registries 提供 了一个机制,以一种有效的方式来浏览,发现 Web Services 以及它们之间的相互作用。 UDDI 计划是一个广泛的,开放的行业计划,它使得商业实体能够(1)彼此发现,(2) 定义他们怎样在 internet 上互相作用,并在一个全球的注册体系架构中共享信息。UDDI 是这 样一种基础的系统构筑模块,他使商业实体能够快速,方便地使用他们自身的企业应用软件 来发现合适的商业对等实体,并与其实施电子化的商业贸易。 专题六 Web Service 基础知识 翰子昂系列理论教材 - 407 - UDDI 同时也是 Web 服务集成的一个体系框架。它包含了服务描述与发现的标准规范。 UDDI 规范利用了 W3C 和 Internet 工程任务组织(IETF)的很多标准作为其实现基础,比如 扩展标注语言(XML),HTTP 和域名服务(DNS)这些协议。另外,在跨平台的设计特性 中,UDDI 主要采用了已经被提议给 W3C 的 SOAP(Simple Object Access Protocol,简单对 象访问协议)规范的早期版本。 4.7 什么是 Axis 引擎 Apache Axis 是 Apache Web Service 项目中的子项目之一,它是 Apache SOAP 项目的 延续。Axis 的主要功能是作为一个 SOAP 的实现来让开发者通过它来构建自己的 Web Ser_vi ce(支持 Java 和 C++)。随着 Web Services 以及 SOA 的不断发展,越来越多的人投入到 了相关技术的实现、开发和标准的制定的工作中。Apache Axis 凭借它强大的功能和稳定性 倍受开发人员的青睐,成为了用于实现 Web Services 的主要途径。 Axis 总体上是一个 SOAP 引擎,它提供了创建 SOAP 客户机、服务器端、网关 SOAP 的 基本框架。Axis 是 SOAP 的后续版本(该项目的前身是 IBM 的“SOAP4J”),我们介绍的 Axis 版本是 Axis1.1for Java 版,这个版本是 java 的 Web Service 实现。 Axis 除了提供 SOAP 引擎之外,它还是一个可插入到 servlet 引擎中的独立的服务器,并 且还提供了以下工具:将 WSDL 转化为 java 类的工具和一个 TCP/IP 数据包监视工具。 Axis1.1 for Java 版本相比它的以前版本有了重大的改进,这些改进让 Axis 灵活性更好、 管理更方便、同时支持 SOAP 和 W3C 中 XML 的新规范,Axis1.1 for Java 提供了以下重要特 性: 1. 速度提高 Axis1.1 for Java 版本使用了 SAX(事件驱动)来解析 XML 文档。 2. 灵活 Axis1.1 for Java 版允许用户灵活地定制扩展 Axis 的功能,用户可以加入他们想要增加的 任何特性:个性化的信息头处理、扩展的系统管理和其他特性。 3. 稳定性提高 Axis 中定义了一套稳定的发布接口(publicshed interface),他们不会随着 Axis 的更新 而更新,除非相关的标准发生了改变,这样减少了 Axis 新发布版本中发生重构时对用户的影 响。比如 axis 中处理 JAVA-RPC 的接口就是一套稳定的发布接口,它支持 Java-RPC1.1 规范, 除非 Java-RPC 规范发生改变,否则这些接口都不会发生变化。 4. 提供面向组件的部署 用户可以轻松地建立可重用的网络环境来实现处理工作的一些模式,或者将这些发布给 自己的合作伙伴。 5. 提供一个简洁的传输抽象框 Axis1.1 版本中的网络传输设计中使用彻底的、简化的抽象,SOAP 消息的发送方和接收 方可以使用多种传输协议比如 SMTP、FTP 或者消息中间件等,Axis1.1 版本的核心引擎完全 是独立于传输协议的,从而使用户可以更加灵活地选择用何种协议来传输。 6. 支持 WSDL1.1 Java 框架 - 408 - 翰子昂系列理论教材 Axis1.1 版本支持 WSDL1.1 版本,用户通过使用这个特性可以非常简单地创建访问远程 的 Web 服务需要用到的类文件,也可以使用该特性从你的 Java 类中自动生成正确的 WSDL 文档。 Axis 现在的版本是 2.0,基于篇幅的问题,具体的使用实现在这里不做过多的介绍。我们 主要介绍另一种引擎 XFire。 4.8 XFire 构建 WebService 步骤 XFire 是新一代的 Java Web 服务引擎,XFire 使得在 JavaEE 应用中发布 Web 服务变得轻 而易举。和其他 Web 服务引擎相比,XFire 的配置非常简单,可以非常容易地和 Spring 集成, 它使得 Java 开发人员终于可以获得和.Net 开发人员一样的开发效率。 由于使用了 STAX(the Streaming API for XML,基于流的 XML 解析)作为 XML 解析 器,XFire 的运行速度又有了质的提高,并且 XFire 支持最新的 JSR 181 的 Web 服务注解。 如果使用 Java5,只需要在源代码中编写相应的 JSR 181 注解,XFire 就可以根据 Java5 注解 自动提取所需的全部信息。由于 JSR 181 也是 JavaEE Web 服务标准的一部分,使用它最大 的好处在于不仅极大地简化了配置,而且避免了配置文件和某个特定的 Web 服务引擎的锁定 z 支持多个重要的 WebService 标准,包括 SOAP、WSDL、WS-I BasicProfile、WS-Add ressing、WS-Security 等。 z 高性能的 SOAP 栈。 z 可选的绑定方式,如 POJO、XMLBeansJAXB1.1、JAXB2.0、Castor 和 JiBX 等。 z 支持 JSR181 API z 多种传输方式,如 HTTP、JMS、XMPP、In-JVM 等。 z 灵活的接口 z 支持多个容器,如 Spring、Pico、Plexus、Loom。 z 支持 JBI z 客户端和服务器代码生成。 在 MyEclipse 下使用 XFire 构建 WebService 步骤如下: 1. 建立 WebService 项目,添加 XFire 库。如图 14-1 到 14-5 2. 配置 web.xml 文件。 3. 在项目中建立一个 WebService,创建相关接口和实现类。如图 14-8 到 14-13 4. 配置 services.xml。 5. 测试 Web 服务是否发布成功。 6. 编写客户端程序访问创建的 WebService。如图 14-21 到 14-28 5. 实验 1. 完成实践知识的示例。(40 分钟) 2. 使用 WebService 开发一个简易计算器。(50 分钟) 专题六 Web Service 基础知识 翰子昂系列理论教材 - 409 - 6. 作业 使用 WebService 做一个获取生日信息的服务。 提示:客户输入身份证号,取出其出生的年月。
还剩418页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 8 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

czglb

贡献于2013-06-19

下载需要 8 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf