Java实用系统开发指南(彭晨阳著)_阅读密码www.zasp.net_仅提供试看如需要请购买原版书
 zasp.net内容简介zasp.net
本书是国人原创的第一本涉及框架、组件构件等重用概念的Java畅销书,该书通过8个系统,指出了一个成熟Java/J2EE系统设计开发的正确之道:Model(建模)、Patterns(模式)和Framework(框架)。
需求UML分析与域模型(Domain Model)建立; J2EE架构技术考量和选择,不同的应用有不同的工具; 代码详细设计和模式的应用; 可重用框架Framework的提炼和应用; Java/J2EE带来的不仅是语言变革;还有工程方法、设计思维的创新,如何使用全新的OO思维设计开发Java/J2EE项目,该书指引了正确的方法和路径,这在同类书籍包括进口书籍中是不多见的。
更难能可贵的是,作者通过8个系统不同架构技术的选择展现了当前Java/J2EE主流技术,集成了Jive 、 Petstore等优秀开源系统的精华。
从Jsp/JDBC简单系统到Jive这样的复杂系统,引入设计模式介绍和使用,以自己设计的网上商店替代了Petstore 1.3,Petstore的Web层是WAF框架实现,书中使用流行框架Struts替代了,而Pestore中EJB相关使用和模式则在书中均有详细介绍和应用,因此本书的网上商店案例应该比Petstore 1.3更实用,更注重性能。
在第四章有专门关于XML Mapping(可方便进一步跨越到数据库的O/R Mapping)的介绍,提供了对XML文件方便实用的操作办法。
本书所有例程都是基于开源软件Tomcat或JBoss容器平台,因此有大量篇幅介绍其相关知识。
对于已经全部掌握上述知识的高手,第七章和第八章有关框架(Framework)的设计可能提供了一种更高层次编程设计的新思路。
由于本书涉及知识面太广,是真正的一本用J2EE和UML开发Java企业级应用程序书籍,从用例UML、数据建模到设计模式、从Struts、EJB到J2EE服务器配置等多个方面,因此,初学者需要配合相关书籍才能阅读本书,这些书籍包括Jsp/Servlet基本概念知识、Struts开发详解、Tomcat配置、J2EE基本概念、JBuilder开发和JBoss配置、GoF设计模式。
zasp.net详细目录zasp.net
第一章:高性能聊天系统
本章以建立一个聊天系统为例,介绍如何使用J2SE 1.4非堵塞I/O(NI/O)为核心开发一个高性能的实时互动服务器系统,这个高性能服务器系统可以拓展为更广阔的应用,如游戏系统、社区系统或者数据实时采集系统。
1.1. 系统需求 聊天交流是目前互联网提供的主要内容。聊天系统有多种实现方式,类似ICQ属于一种点对点的聊天系统,还有一种是基于Socket的集中式聊天系统,这种聊天系统需要登录统一的聊天服务器,每个人的聊天信息其他人都可以看到,类似一种会议室,当然,两个人之间也可以进行保密的私语。 在基于Socket的聊天系统中,主要有两种角色:服务器和客户端,不同的客户端登录集中式的服务器,通过服务器将一个客户端发出的信息推送到其他所有客户端。 基于Socket的聊天系统最早实现是使用网页刷新方式,通过客户端不断地自动刷新,将服务器端整个页面内容下载到客户端显示,这种方式的聊天速度慢,而且有刷屏现象,很快被更新的聊天技术所替代。 聊天系统在客户端和服务器之间主要传送的是文字信息,服务器端只需要把最新的文字信息推送到客户端,这样减少了网络传输内容,节省了网络传输的时间,无疑提高了聊天速度。这种“推”技术是目前基于Socket聊天系统的主要实现技术。 一个基于Socket的聊天系统有下列具体功能要求: (1)客户端和服务器必须保持随时随地的连接。这有别于普通Web浏览的连接方式。在使用浏览器访问服务器时,先由客户端发出HTTP协议,然后服务器响应处理这个客户端的响应,再返回处理结果;请求(Request)和响应(Response)是一种一对一的前后因果关系。 而在基于Socket的聊天系统中,客户端发出聊天信息的同时,客户端也在接受服务器发送过来的其他人的聊天信息,因此,请求和响应不存在那种前后对应关系,是两种分别独立进行的进程。 因为服务器任何时候都可能发送信息到客户端,因此,客户端和服务器一旦建立连接,必须能让服务器在以后发送中寻找定位到这个连接。 (2)在速度性能方面,聊天系统提出了更高的要求。在网络连接的薄弱环节I/O通信方面,要求能够实现无堵塞地、流畅地数据读写。在面对几百个甚至更多的客户端同时发出连接信息的情况下,服务器要求能够保持高性能的并发处理机制,迅速地完成这几百个并发请求的处理和发送任务。 (3)在扩展性和伸缩性方面,聊天系统也提出了一定的要求。当一台服务器不能满足要求时,必须在客户端不知晓的情况下,通过不断增加服务器就能方便地拓展聊天系统的整体处理能力。对于客户端用户来说,这些服务器群都象征一个统一的服务器,不需要他们在进入聊天室之前先选择具体的服务器;也没有单个聊天室最大人数的限制,如果可以,服务器群可以支撑一个巨大容量的聊天室。
1.2 架构设计
本系统的设计核心是Socket底层通信,基于快速稳定的Socket底层通信架构,不但可以实现聊天系统,还可以实现其他如游戏、数据采取等实时性要求较高的系统,甚至可以建立一个快速的平台服务器系统。相比J2EE服务器系统,该平台系统的最大优势就是精简的代码带来的高性能。 当然,如果单纯追求高性能和速度,也许直接使用汇编就可以。使用Java设计这样的实时系统,实际还有一种很重要的目的,即追求高度的可扩展性和伸缩性。 因此,本系统设计必须将高性能和高伸缩性两个方面和谐地统一起来,不能盲目追求性能而破坏面向对象的编程风格和模式;也不能因为追求更大的重用性,建立太多复杂的中间层,其实这方面J2EE已经做得很好,有关J2EE的应用将在以后章节重点讨论。 当然,高性能应该是本系统的主要特色,为了实现本系统高效率的并发处理性能,设计上将采取Reactor模式实现自我快速触发;网络通信上,使用非堵塞I/O进行流畅地数据读写,在应用逻辑上,通过非堵塞的多线程技术实现并发功能处理。特别是J2SE 1.4以后版本推出的非堵塞I/O,将使得Java表现出和C语言同样的优越性能。
1.2.1 JAVA事件模型
事件只有在被触发时才会发生,它的发生是程序系统无法预料和计划的。例如,火警探测器只有在发生火警时才会触发,但是火警的发生是无法事先预料的,它处于时刻可能发生当中。为了能响应这些无法预料发生的事件,必须建立一套事件处理机制,以预备在发生事件时实现相应的处理。
1.2.2 架构设计图
考虑到系统的可重用性和伸缩性,需要将本系统的网络通信底层和应用系统分离开。这样,基于可重用的网络通信层,可以实现其他各种实时性较高的应用系统,同时,系统还需要提供一些基本功能支持,如网络连接状态管理以及用户状态相关管理,前者为实现一个动态的实时在线系统提供基本连接的管理,后者类似J2EE中Servlet部分的Session管理。
1.2.3 协议设计
TCP是一种面向连接的协议,传输数据比较可靠。TCP协议中包含了专门的传递保证机制:当接收方收到发送方传来的信息时,会自动向发送方发出确认消息;发送方只有在接收到该确认消息之后才正式传送数据信息,否则将一直等待直到收到确认信息为止。 UDP是面向非连接的协议,传送数据之前不需要建立专门的连接,直接发送就可以,因此速度要比TCP快。由于UDP协议并不提供数据传送的保证机制,因此可能发生丢包的情况。UDP适合一次只传送少量数据、对可靠性要求不高的应用环境。
1.2.4 多线程
在 Java 程序中使用多线程要比在 C 或 C++ 中容易得多,因为 Java 编程语言提供了语言级的支持,但是这并非意味着在使用时可以避开线程的一些基本问题。在以后章节中介绍的JSP/Servlet容器,实际是一个线程池容器,JSP在运行时将编译成Servlet,而Servlet是一种线程类,J2EE通过Servlet概念的提出,确保开发者不用担心线程以及同步等问题,可以像往常一样编程。
1.2.5 线程池
1.2.4节主要介绍了多线程使用时对资源争夺情况的处理,只要掌握几个基本原则,在一般情况下使用多线程实现应用处理就没有太大问题。
1.2.6 非堵塞I/O NoneBlock I/O
在前面讨论中,非堵塞I/O 作为Reactor模式的实现,实际上提供了一种事件发生、自我激活、主动通知的机制。因此使用非堵塞I/O将大大提高系统的I/O处理性能。 非堵塞I/O中有3个重要的类:Selector、SelectionKey 和Channel。
1.3 Socket核心设计和实现
1.3.1 TCP和Reactor模式 1.3.2 UDP实现 1.3.3 客户端实现
1.4 Socket接口设计和实现
1.4.1 队列和对象类型 1.4.2 访问者模式定义
Visitor模式表示作用于某对象集合中各元素的操作。这种操作不是一种原生的,而是使用了访问者模式后才有的方法,即这些元素原本没有做好接受这种操作的准备。因此,没有提供一种专门方法用于外界统一访问,如果强行需要对这些元素进行访问,那么就需要访问者模式为这些元素再设计一个统一接口。这样,通过这个统一接口提供的统一方法,外界可以对这些元素实现统一访问了。
1.4.3 访问者模式实现 1.4.4 协议封装 1.4.5 重整Refactoring
在这种情况下,就开始要考虑重整(Refactoring),消灭这个隐患。决定重整之前,如果还有其他因素促进重整就更好了,Socket I/O读写部分功能有两个,整个Socket底层有4处代码:UDPClient、服务器端的UDPHandler、TCPCLient和服务器端的TCPHandler,每个类中有两次Socket读写操作,Socket读写操作代码发生重复两次以上。 当一种现象重复3次以上时,就需要使用重整了。
1.5 应用接口设计和实现 1.5.1 Connection API 1.5.2 ConnectionFactory API 1.5.3 TcpConnection API 1.5.4 UdpConnection API
1.6 应用层设计和实现 1.6.1 客户端聊天测试 1.6.2 服务器聊天测试
1.7 性能测试
第二章:简单的用户注册系统
本章以一个简单的用户注册系统为例,介绍J2EE中JSP/Servlet技术的应用,JSP/Servlet 属于J2EE中的Web部分,负责处理客户端的请求,然后进行简单的处理,传送到后台实现进一步商业逻辑运算。
2.1需求分析
用户注册验证几乎是每个Web系统都需要的系统,只有那些注册过的用户才能访问一些特定的资源。 用户注册验证系统是安全权限系统的重要组成部分。安全权限系统主要是解决“Who对What进行How的操作”问题,安全权限系统中还有一个重要组成部分就是资源访问控制权限,只有被授权后的用户才能访问特定的资源,这部分功能将在以后章节讨论实现。
2.2系统设计
用户注册验证系统可以有很多种架构设计,从简单系统到复杂系统的单点登录以及JAAS(Java验证和授权服务框架)等。为对J2EE的JSP/servlet以及JavaBeans有初步了解,将使用JSP/JavaBeans技术完成一个简单的用户注册验证系统。 JavaBeans是Java的软件组件架构技术,这些组件可以非常有效地、方便地组装成一个应用系统。近一个世纪以来,同样的想法已经被应用在不同领域,这就是工业革命。大量工厂的组装生产线出现,软件组件也是基于这样的概念:一次性创建小而可重用的模块,然后可以尽可能地重复使用它们。
2.2.1 Jsp/Servlet与J2EE
第1章介绍了使用多线程和线程池来提高应用系统的处理性能,这已经形成一种通用的性能提高处理模式。但是有一个最大问题是,由于线程难以使用和驯服,如果每个应用系统的开发都需要开发者了解如何使用线程池,如何防止多线程同步死锁,这将大大妨碍开发者使用Java软件系统开发应用系统的热情。 J2EE的JSP/Servlet开发者不用担心线程以及同步等问题,只要像往常一样编程,JSP/Servlet容器会自动使用线程池等技术来支持系统的运行。因此,JSP/Servlet的实质是一种线程技术,JSP在运行时会被编译成Servlet进行运行
2.2.2 结构设计图 2.2.3 Jsp/JavaBeans技术要点
session表示这个JavaBeans将作为一个对象绑定于HttpSession中,直至用户退出系统前一直有效。也表示这个用户登陆进入系统以后,本JavaBean的实例将一直保存在系统内存中,直到该用户离开本系统。 <jsp:setProperty>专门用来设置bean的属性,必须首先在这个bean中声明这个属性的设置set方法,比如需要设置bean的userid属性,必须在这个bean中有setUserid方法;需要设置bean的abc属性,必须在这个bean中有setAbc方法,其中setAbc中的A作为属性名第一个字母要大写,这些都是硬性的技术规定。 property="*"被很多人称为超级星就是因为它有这样神奇的超级功能,简化了很多无谓的枯燥语句。property="*"将用户提交的表单参数自动写入JavaBeans的相应属性值中。因为表单提交的参数都是String型的,而在JavaBeans中同名属性有各种类型,那么系统将实现String类型到各种参数类型的自动转换,可以参考<jsp:setProperty>的参数转换表。
2.2.4 JDBC和连接池
JDBC是Java中支持基本SQL一个底层通用API接口,它在不同数据库的基础上提供了一个统一的操作界面,这样就可以在Java中方便地直接对数据库进行各种操作。JDBC API利用了Java跨平台优势,对企业数据实现了一次编写到处运行的目标。 JDBC API一般是由具体数据库厂商提供,例如MySQL就提供了MySQL-Connector -XXX.jar作为JDBC API支持,在本系统开发时,需要将该驱动包放入classpath中,以便本系统的应用程序在编译和运行时能定位寻找到该驱动包。 Class.forName("com.mysql.jdbc.Driver"). newInstance(); 大多数初学者一开始使用JDBC就喜欢在JSP中直接使用ResultSet。其实ResultSet是一个临时集合,只能作为从数据库中获取查询数据用,不能再作为中转站,这样做的缺点还有耦合性太强,容易发生内存泄漏。如果确实需要将数据库中查询的大量数据再传递到JSP中,那么可以使用Collection或者使用Iterator。具体实现方法将在以后章节中介绍。 数据库连接池属于基于线程设计的Java底层技术,因此很多J2EE容器服务器本身就有数据库连接池支持。因此,作为J2EE商业应用,一般不提倡自己钻研开发连接池,直接使用容器提供的连接池就可以。在本章后面章节将介绍Tomcat连接池的使用。
2.2.5 数据库设计
2.3 类的详细设计和实现
2.3.1 Façade模式
GOF的《设计模式》一书中关于外观Facade模式定义如下:为子系统中的一组接口提供一个一致的界面。Facade模式提供了一个更高的接口,使得子系统更加容易使用。 细分封装是面向对象编程中常用的设计方法,将一个系统划分为若干子系统有利于降低系统的复杂性,一直划分到子系统之间耦合度最小。所谓耦合度就是通信和相互依赖的关系,这类似一个建筑物的设计建造,建筑物有一个基本架构,而架构是由一些模块组成的,模块又是由一块块砖石组成的,而砖石是由一粒粒沙组成的。划分到如此细的部件,这样就可以重复利用这些部件来搭建任何建筑物了
2.3.2 JDBC通用操作类
关于数据库的主要操作有:获取数据库连接;数据库查询、插入、修改、删除;断开数据库连接。而数据库连接和断开数据库连接对于不同的数据表应该说都是统一的,因此,数据库的JDBC操作可以做成一个通用类,这样就能达到重用目的。
2.3.3 Email发送通用类 2.3.4 用户资料管理 2.3.5 密码数据操作类 2.3.6 登陆验证功能
2.4 界面编程实现 2.4.1 登陆验证页面 2.4.2 注册页面
2.5 调试、发布和运行
2.5.1 单元测试
本项目中需要对两个通用类实现单元测试,一方面因为它们功能非常重要,另外一方面因为这两个通用类涉及到系统环境的配置。 以E-mail发送的SendMail.java测试为例。使用JUnit实现单元测试,Junit(http://www.junit.org)是一个开放源代码的测试软件,应用非常广泛。 Junit的使用非常简单,实际只要两步: (1)继承junit.framework.TestCase类编写自己的测试类; (2)在测试类中编写自己的testXXXXX()方法。 一般开发J2EE系统都会使用IDE工具辅助开发,在以后章节中谈到在J2EE开发中为什么必须依赖IDE才能提高开发速度。JBuilder作为首选的IDE工具使用也非常广泛,在JBuilder中使用Junit更加方便。
2.5.2 快速配置开发环境
JBuilder是一个很强大的Java开发工具。因为其包含内容太多,有时用来作为小型项目的JSP/Javabeans开发显得过于“笨重”,而且学习JBuilder配置使用还需要一定的时间和精力。下面介绍一套简单快速轻便的开发和调试环境。 Jcreator(http://www.jcreator.com/)用来编辑调试JavaBean。 Dreamweaver (editplus)用来编辑JSP和HTML文件。 Tomcat作为Serlvet/JSP容器服务器。 MySQL作为数据库服务器。 这套组合可以直接通过IE浏览器调试JSP或JavaBean,与JBuilder这样庞大的IDE工具软件相比,最大优点是资源消耗少,可以发挥各自工具的特点。例如DreamWeaver强大的可视化图形开发界面非常方便HTML/JSP的开发。
2.5.3 Tomcat配置和调试
2.5.4 Tomcat连接池使用
使用数据库连接池可以提高数据库的访问性能。Tomcat的数据库连接池是使用Jakarta-Commons项目中Database Connection Pool,包含下列组件:Jakarta-Commons DBCP 、Jakarta-Commons Collections和Jakarta-Commons Pool。这些库文件包必须和JDBC驱动程序一起安装在$CATALINA_HOME/common/lib这个目录下。
2.6 Hibernate使用
Hibernate(http://sourceforge.net/projects/hibernate/)是著名的开源O/R mapping产品,可以将普通的Java对象直接映射到数据库持久层中,无需insert或update之类的SQL操作语句。
2.7 小结
本章主要介绍J2EE中Web技术的一种实现,通过直接调用JDBC访问数据库。因此,在设计理念上还是沿用传统系统的混合式架构。在这种架构下,用户界面处理和数据库处理混淆在一起,彼此联系紧密,在可维护性和可拓展性方面相当薄弱,经常会发生修改一个点引发整个系统的运行崩溃,调试或追查内存泄漏等问题时困难,系统的稳定性和健壮性很差。这些问题将在以后章节介绍的J2EE完整的多层结构中得到解决。
第三章:Jive论坛系统
Java系统开发学习的一个不可忽视的步骤就是研读优秀的源码,从中汲取前人探索的结晶,从而不断提高自己的设计和编程水平。本章主要是从设计模式角度来剖析Jive论坛系统,既能充分理解Jive论坛的源码,又能学习设计模式在Java系统中的使用。
3.1 Jive功能需求
Jive功能需求分析类似于一个新系统的需求分析。只有了解Jive系统实现了哪些论坛功能,才能进一步研究和学习它是怎样巧妙、优雅地实现这些功能的。
3.2 Jive与设计模式
Jive论坛系统使用大量设计模式巧妙地实现了一系列功能。因为设计模式的通用性和可理解性,将帮助更多人很快地理解 Jive论坛源码,从而可以依据一种“协定”来动态地扩展它。那么使用设计模式还有哪些好处?
3.2.1 设计模式
设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。毫无疑问,设计模式于己于他人于系统都是多赢的。设计模式使代码编制真正工程化,设计模式是软件工程的基石。 GOF(设计模式作者简称)《设计模式》这本书第一次将设计模式提升到理论高度,并将之规范化,该书提出了23种基本设计模式。自此,在可复用面向对象软件的发展过程中,新的大量的设计模式不断出现。 很多人都知道Java是完全面向对象的设计和编程语言,但是由于接受教育以及经验的原因,大多数程序员或设计人员都是从传统的过程语言转变而来,因此在思维习惯上要完全转变为面向对象的设计和开发方式是困难的,而学习设计模式可以更好地帮助和坚固这种转变。 凡是学习完成设计模式的人都有一种类似重生的感觉,这种重生可以从很多方面去解释。换一种新的角度来看待和解决问题应该是一种比较贴切的解释,而这种新的思维角度培养属于基础培训,因此,设计模式是学习Java的必读基础课程之一。
3.2.2 ForumFactory与工厂模式
工厂模式是GOF设计模式的主要常用模式,它主要是为创建对象提供了一种接口,工厂模式主要是封装了创建对象的细节过程,从而使得外界调用一个对象时,根本无需关心这个对象是如何产生的。 在GOF设计模式中,工厂模式分为工厂方法模式和抽象工厂模式。两者主要区别是,工厂方法是创建一种产品接口下的产品对象,而抽象工厂模式是创建多种产品接口下的产品对象,非常类似Builder生成器模式。在平时实践中,使用较多的基本是工厂方法模式。 ForumFactory中提供了很多抽象方法如createForum、createThread和createMessage()等,它们是创建各自产品接口下的具体对象,这3个接口就是前面分析的基本业务对象Forum、ForumThread和ForumMessage,这些创建方法在ForumFactory中却不立即执行,而是推迟到ForumFactory子类中实现。
3.2.3 统一入口与单态模式
在上面ForumFactory的getInstance方法使用单态(SingleTon)模式。单态模式是保证一个类有且仅有一个对象实例,并提供一个访问它的全局访问点。 前面曾提到ForumFactory是Jive提供客户端访问数据库系统的统一入口。为了保证所有的客户端请求都要经过这个ForumFactory,如果不使用单态模式,客户端下列调用语句表示生成了ForumFactory实例: ForumFactory factory = new DbForumFactory(); 客户端每发生一次请求都调用这条语句,这就会发生每次都生成不同factory对象实例,这显然不符合设计要求,因此必须使用单态模式。
3.2.4 访问控制与代理模式
代理模式是属于设计模式结构型模式中一种,它是实际访问对象的代理对象,或者影子对象,主要达到控制实际对象的访问。这种控制的目的很多,例如提高性能等。即远程代理模式,这种模式将在以后章节讨论。
3.2.5 批量分页查询与迭代模式
迭代(Iterator)模式是提供一种顺序访问某个集合各个元素的方法,确保不暴露该集合的内部表现。迭代模式应用于对大量数据的访问,Java Collection API中Iterator就是迭代模式的一种实现。 在前面章节已经讨论过,用户查询大量数据,从数据库不应该直接返回ResultSet,应该是Collection。但是有一个问题,如果这个数据很大,需要分页面显示。如果一下子将所有页面要显示的数据都查询出来放在Collection,会影响性能。而使用迭代模式则不必将全部集合都展现出来,只有遍历到某个元素时才会查询数据库获得这个元素的数据。
3.2.6 过滤器与装饰模式
Jive论坛实现了信息过滤功能。例如可以将帖子内容中的HTML语句过滤掉;可以将帖子内容中Java代码以特别格式显示等。这些过滤功能有很多,在实际使用时不一定都需要,是由实际情况选择的。例如有的论坛就不需要将帖子内容的HTML语句过滤掉,选择哪些过滤功能是由论坛管理者具体动态决定的。而且新的过滤功能可能随时可以定制开发出来,如果试图强行建立一种接口包含所有过滤行为,那么到时有新过滤功能加入时,还需要改变接口代码,真是一种危险的行为。
3.2.7 主题监测与观察者模式
在Jive中,用户也许会对某个主题感兴趣,希望关于此主题发生的任何新的讨论能通过电子邮件通知他,因此他订阅监视了这个主题。因为这个功能的实现会引入电子邮件的发送。在前面章节已经讨论了电子邮件发送有可能因为网络原因延迟,如果在有人回复这个主题时,立即进行电子邮件发送,通知所有订阅该主题的用户。那么该用户可能等待很长时间得不到正常回应。 使用观察者模式,可以通过触发一个观察者,由观察者通过另外线程来实施邮件发送,而被观察者发出触发通知后,可以继续自己原来的逻辑行为。
3.3 Jive安全管理机制
3.3.1 安全验证机制
Jive的安全验证机制是按照比较通用的思路设计的。类似前面“简单的用户注册管理系统”中的介绍,Jive也是在所有的JSP页面中include一个安全检验功能的global.jsp。由于global.jsp是在每个JSP一开始必须执行的功能,因此通过拦截global.jsp拦截发往各个JSP页面的请求(request)。如果这个请求是合法的,将被允许通过;如果不是,将注明请求者身份是Anonymous(匿名者)。 Jive的安全验证机制比较简单易懂,值得在实践中学习借鉴。但是注意到这套安全验证机制只是Web层的“手工”验证,资源访问权限(ACL)也是自己“手工”来实现的。如果使用EJB技术,因为EJB容器本身有一定的资源访问控制体系,因此在Web层验证通过后,需要将这些登录信息传递到EJB层。当然如果直接使用Web容器的安全验证机制,那么Web层与EJB层之间的登录信息传递将由容器实现,这样就更加简单方便。
3.3.2 用户资料管理
3.4 Jive的缓存机制
Jive论坛的一个主要特点就是其性能速度快,因此很多巨大访问量的网站都采用了Jive论坛。这些都是由于Jive采取了高速缓存机制。 缓存(Cache)机制是提高系统运行性能必不可少的技术。缓存机制从原理上讲比较简单,就是在原始数据第一次读取后保存在内存中,下次读取时,就直接从内存中读取。原始数据有可能保存在持久化介质或网络上。缓存机制也是代理模式的一种实现。
3.4.1 缓存原理和实现
Jive的Cache总体来说实现得不是非常精简和有效。它是针对每个具体数据对象逐个实现缓冲,这种“穷尽”的办法不是实践所推荐的用法。通过使用动态代理模式,可以根据具体方法的不同来实现缓存是值得推荐的做法。Jive的缓存实现得比较简单,可以用来学习和研究缓存机制
3.4.2 缓存使用 3.4.3 小结
3.5 Jive的其他组件技术 3.5.1 Jive的树形结构 3.5.2 XML与JDOM
分析读取XML数据有很多工具,如DOM(http://www.w3.org/DOM/)和SAX(http://www.saxproject.org/)。这两种是标准的XML分析器,可以使用任何语言来实现,DOM分析XML数据时,是将整个文档一下子读入内存,如果文档很大,性能就发生影响,而SAX则是动态地对每一行分析,无需全部读入,因此在分析大文档时速度比较快。 但是这两种分析方法都是围绕XML树形结构展开的,在编制这两种分析器时,会涉及到大量XML概念的API,需要一定的XML基础和知识,使用起来有一定难度。 JDOM(http://www.jdom.org)封装了DOM/SAX的具体使用技术,以非常符合Java编程方式的形式来分析XML,因此使用起来非常方便。
3.5.3 全文检索和Lucene
Jive中支持全文检索,这个功能主要核心依赖另外一个开放源代码项目Lucene(http://jakarta.apache.org/lucene/docs/index.html)。Jakarta Lucene是一个高性能全文搜索引擎,可以跨平台应用于任何搜索应用。
3.5.4 Jive的中文问题
要从根本上解决Java的中文问题,只要将Java系统的统一编码定义为UTF-8。UTF-8编码是一种兼容所有语言的编码方式,惟一比较麻烦的就是要找到应用系统的所有出入口,然后使用UTF-8去“结扎”它。
3.6 Jive图形处理
服务器端的图形处理需要使用到Java的图形处理技术,而且图形处理是在服务器端的Web容器中进行的。和以往Java在客户端进行图形处理稍微有所不同,相同的是都要使用计算机的底层图形支持资源。
3.6.1 图形上传处理 3.6.2 服务器端图形处理
3.7 Jive安装调试运行
3.8 小结
Jive系统是一个完全的Web系统,整个系统的最大特点是自我定制实现,它为了提高数据库的访问性能,使用了自己开发的数据库连接池;为了提高系统的数据处理系统,它使用了缓存机制;为了实现用户安全管理机制,它使用Proxy模式实现了角色权限的定位和检查等。这些模块功能在很多系统中都是需要的,但是如果想从Jive系统提炼出这些模块功能以达到重用,又是非常困难的。
第四章:网站内容管理系统
X公司是一家处于快速发展中的中小型销售公司,他们打算架构一个网站。计划分两步走:首先做一个宣传型的网站,然后在公司规模达到一定程度时扩展为网上商店。 在第一步计划中,X公司的主要想法是通过网站能及时介绍公司的产品、服务以及其他公司相关情况;由于不断有新品上架,他们希望能通过公司市场销售人员直接来修改页面,将新的信息直接发布在网站上;同时他们也希望页面美工风格能定期地改进和更换。 X公司这样的需求其实代表了大多数公司进军电子商务的意图,因此这个案例有一定的普遍性。
4.1 需求分析
4.2 架构设计
根据前面的需求分析,以一个中型系统的架构来设计本项目。在设计本例架构时,应充分考虑其扩展性和通用性。 在框架技术选择上,预备完全在J2EE的Web框架里实现。这样可以充分了解熟悉J2EE的Web技术,同时又因为Web技术相对后端EJB层来说是比较成熟的,发展变化不是非常大。 因此,本例的架构可以说是一个J2EE的Web实现的标准架构,可以将它应用到更多的中小型项目中。
4.2.1 系统架构图
多层架构的优点主要体现为: 良好的解耦性:各个功能层只负责自己相应的事务,不再相互混淆在一起。每个功能层如果在将来有所变化时,不会涉及到其他功能层,因为每个功能层是相对独立的。 高度的重用性:各个层的技术都可以移植到其他应用系统。比如表现层的框架一旦确定,可以在第二个项目中同样使用这样的技术,同时还可以提高开发速度。 灵活的扩展性:由于表现层和核心功能分开,可以将系统从PC应用拓展到无线等应用中,所作的修改只是表现层的更改,系统核心功能无需变化。
4.2.2 MVC模式和Struts
表现层涉及很多用户界面的元素,因此比较难以实现重用。但是,有一个宗旨是:不能将功能性的代码与显示性的代码混合在一起,否则,当需要更改页面或者扩展新功能时会带来很大的修改量,甚至破坏原有系统的稳定性。 因此,需要对表现层进行细化,可以将表现层分3个部分: 视图(View)负责显示功能。 控制器(Controller)根据Model处理结果,调节控制视图View的输出。 业务对象模型(Business Object Model)是对真实世界实体的抽象,可以是一些数据,也可以是一些处理对象或事件对象。在本项目中,业务对象就是那些包含状态和行为的Javabeans,如图4-4所示。 有一种观点认为只有大项目才需要采取MVC。实际上,很多时候无法确定项目的规模。因为客户的需求始终不断在变化,如果原有的基础架构不具有很强的拓展性,那么项目进行到中途时可能要再进行重新设计,很容易陷入左右为难的地步。所以,无论任何项目,用标准的架构去设计它,就如按标准的方法去做事一样,相当于成功了一半。 具体实现MVC模式的软件框架有很多,其中应用最广泛的有下列3种: Apache Struts(http://jakarta.apache.org/struts/) Strut是基于JSP的框架软件,有大量文章和参考资料面世,其中《Struts in Action》一书非常值得一读。 Apache Cocoon (http://xml.apache.org/) Cocoon是基于XML和XSLT技术的MVC模式实现框架,在Cocoon中很巧妙地利用XML技术实现了内容和模板分离的功能。 Petstore WAF(Web Application Framework)Petstore是SUN公司推出的一个J2EE实例样本,WAF是其Web实现框架,其原理类似Strut。 Jsp中使用include的主要问题是: 如果要修改的不只是头部和尾部,而是整个页面的布局,那么就必须逐个进行页面的修改。 大量include嵌套使用,这类似面向过程语言中的GoTO语句,会使维护扩展变得非常困难,但是因为编写简单直接,不少程序员还是可能喜欢这样做。在一个项目中开这种先河,将出现大量层层重叠的include语句,严重破坏了页面的可扩展性和可维护性,使得页面修改扩展成为整个系统的恶梦。 调试不方便。include分动态静态两种用法,静态用法调试很不方便,因为即使被include的JSP页面在一些容器中,主页面如果不修改,容器将不会重新载入新的使用“include”语句调用的子页面,除非调用者页面和被调用者页面均被修改
4.2.3 DBO模式和Castor
在Java中,对象序列化成二进制数据是比较方便的。但是将一个对象序列化成XML文本时,也许就没那么简单了。对象和XML之间序列化和反序列化依赖很多方面,比如文本的编码、映射设置等。 将包含数据的对象序列化成XML文本后,就可以很方便地实现数据的持久化保存。 数据持久化表示数据将脱离应用程序的生存周期,也就是说,当这个应用程序退出或计算机关机,这些数据还将继续存在。因为这些数据已经被保存到永久存储介质上,如硬盘文件系统或关系数据库系统。 在讨论DBO模式(Data Bind Object Pattern)之前,首先必须了解一个很重要的概念,就是MVC模式中的Model。它泛指的是一种业务对象模型(Business Object Model),数据对象模型(Data Object Model)是业务对象模型的一种,它包含状态以及操作状态的行为。数据对象也可以被认为等同于经常提到的另外一个名词:值对象(value Object)。 数据对象是对真实世界中实体软件的抽象。它可以是人、事或地方以及一些商业概念,比如订单、客户这些概念也属于业务对象。 DBO模式有3个参与角色: 数据对象:将要被序列化到XML或从XML反序列化的对象。 数据绑定对象:这是一个工具型的基本对象,这个基本对象抽象了XML的序列化和XML API的具体使用细节。 XML 序列化 API: 真正实现XML序列化的具体行为,具体实现可以采取JDOM、Castor、JAXB或其他类似工具。
4.2.4 Cache设计
是否有缓冲机制(Cache)是衡量一个J2EE产品是否成熟的重要标志。因为缓冲对于J2EE系统的运行性能有至关重要的作用,特别是在大量用户并行访问时,没有缓冲机制几乎是不可想象的事情。 J2EE作为一个多层结构,每发生一次请求,将可能经过系统的许多层次。这些层次中有的可能位于另外一台服务器上,那么网络连接开销将延迟请求信号的处理时间。使用缓冲可以节约请求信号的处理时间,大大提高系统的整体响应能力。 因此,在一个分布式的集群Cluster(负载平衡和错误恢复)环境中,Cache的设计就比较复杂。综合各种因素,使用JMS(Java Message System)进行这种通知信息提示是一种比较切实可行的方案,带来的性能损失也不是很大,支持分布式环境的不少缓冲产品已经面世。因此,一个系统从单机环境升级到分布式环境,缓冲机制可能要作一定的修改。
4.3 详细设计和实现
4.3.1 基本对象设计 4.3.2 数据模型的实现 4.3.3 抽象工厂(Abstract Factory)模式
在本项目中,使用了XML文件系统作为数据存储介质,但是如果将来系统发展到一定规模,使用关系数据库作为存储介质,那么就需要替换存储介质。这种替换工作力求修改最小,不影响原有系统的稳定性,最好能无缝过渡到数据库系统,也就是说为了让系统具备灵活的扩展性和伸缩性。 为了达到这个目的,需要将有关XML数据操作的细节封装起来,使得外界根本不知道系统内部是采取XML文件还是使用数据库来实现数据持久化的。
4.3.4 生成器(Builder)模式
Builder模式是一种将复杂对象的组装创建过程封装起来的模式,将过程中具体实现细节隐含在抽象接口后面。想到Builder模式应该就会联想到汽车的组装:汽车分很多部件,部件组装起来就成为一辆汽车,Builder模式是将汽车组装过程进行了封装。 “页”是这个项目中的一个复杂对象,需要通过复杂的混合组装过程,将内容在一个“页”中以一定模板显示出来。分别依据Builder模式的参与者设计如下: “页(Page)”是Builder模式中要构造的复杂对象。“页(Page)”是由菜单(Menu)、内容(body)以及模板JSP等部件组成。
4.4 表现层的实现
4.4.1 Struts相关设置 4.4.2 创建PageForm
Struts中需要一个ActionForm,它实际是一个Javabean,字段都是JSP中form的一些字段,如果有过JSP/Javabeans开发经验的人会使用下列代码: <jsp:useBean id="userInfo" class="com.ora.jsp.beans.userinfo.UserInfoBean"> <jsp:setProperty name="userInfo" property="*" /> </jsp:useBean> 这里UserInfoBean的字段对应HTML中Form表单的字段,从而在Form提交时,表单字段自动映射到UserInfoBean的字段。如果不清楚,可以参阅有关JSP/Javabeans的章节。
4.4.3 创建PageAction
Action是和控制器Servlet在一起工作的,Action是整个系统中关键的、起调度控制作用的类。 在Action类中,将要处理前面页面输入本类的参数,进行相关业务逻辑运算。然后根据结果,从struts-config.xml中根据用户定制的Forward页面,将Forward页面推到用户的浏览器中去。
4.4.4 创建Page.jsp页面
在JSP中消除Java代码分两个步骤: 能够在Action中实现的功能移动到Action中实现。
使用Struts标签语句替代Java代码实现的功能。在JSP中使用Struts特殊标签taglib语句,参考 http://jakarta.apache.org/struts/userGuide/dev_html.html。
4.4.5 自定义标签库 4.4.6 创建SavePageAction 4.4.7 Tiles模板
在前面设计中,一个页面被分为几个区域:标题栏、页尾栏、菜单栏以及内容栏部分。如何使用Tiles将这几个部分组合在一起?
Tiles的页面组装配置可以在JSP,也可以在tiles-defas.xml中。
4.4.8 创建cmsMenu.jsp 4.4.9 创建index.jsp 4.4.10 小技巧
在J2EE应用中经常需要知道某个文件的绝对路径,如何能从Web容器中自动获得某文件的绝对路径?
例如上节中,1.jsp中body指向的是“/data/1.jsp”,在相对目录data下的1.jsp是系统程序自动生成的。也就是说使用了File有关API操作,而File操作必须提供绝对路径。例如,在项目系统建立c:\cms下,目录结构见前面章节,那么“/data/1.jsp”的绝对路径应该是c:\cms\data\1.jsp。注意,如果有本地文件操作,那么系统就不能打包成*.war,必须提供完整的目录路径。
那么如何获取根目录c:\cms?通过配置文件设定也可以,但是不灵活,每次移动项目都要修改配置文件。
4.5 项目重整Refactoring 4.6 调试、发布和运行 4.6.1 配置和运行
本项目由于只使用了J2EE中的Web框架,因此可以使用专门的Web容器服务器。Tomcat (http://jakarta.apache.org/tomcat/)是免费开源的Web服务器,是Jakarta项目中的最著名的子项目,它还是Sun公司推荐的servlet和jsp容器(http://java.sun. com/products/jsp/tomcat/)。
很多人一直对免费或开放源代码的产品实现商业应用存有疑惑,但是Tomcat作为类似Apache一样的Java服务器,经过几年来大量使用证明,其稳定性和成熟性是有目共睹的。经过最新的研究表明:系统在并发用户100以下时,Tomcat的响应时间和商业服务器相差不多,可见Tomcat是能够大量被应用到中小型企业系统中。
4.6.2 Log调试信息的配置
项目第一次调试总会存在诸多问题,因此需要打印出系统中每个步骤的运行结果,这样可以跟踪调试。以前是使用System.out.println来设置输出调试信息。但是这有一个缺点,就是当项目切入正式运行后,不希望这些日志输出到容器的控制台上。因为这样还会降低系统性能,但是如果手工逐个到程序中删除,又将为以后扩展维护带来不便。
以后章节会谈到如何使用Log4J来跟踪调试,但是在Tomcat中设置Log4J不是一件方便的事情。需要做一个专门的Proxy类,通过这个Proxy类,可以动态地设置是使用System.out.println还是Log4J,或者关闭一些调试信息。
定义Log的级别如下:
ALWAYS = 0;
VERBOSE = 1;
TIMING = 2;
INFO = 3;
IMPORTANT = 4;
WARNING = 5;
ERROR = 6;
FATAL = 7;
4.7 小结
单从网站内容管理系统上看,使用Java或J2EE实现这个系统并不比其他语言PHP或ASP有更多的优势和好处,但是,在目前Java世界中,网站内容管理系统已经被整合进入门户Portal系统中,内容管理成为Protal系统中一个主要的Portlet。
一个灵活的、可伸缩的门户Portal系统是复杂的,Java平台所具有的可重用性、可拓展性以及高度安全性等特点才能够得到真正的体现。
第五章:订阅信息系统
本章通过订阅信息系统的设计和开发,介绍EJB在实践项目中的应用。当一个J2EE项目达到一定的规模,Web层和EJB层中很多类要进行频繁地交互操作。如果设计一种Web层与EJB层通用接口框架,在配置文件中设定Web层和EJB层中执行类的对应关系。这样就能完成两个大层次之间松散而且快速的联系,实现J2EE项目可动态扩展。在本章也将介绍这种接口框架系统的设计和使用
5.1 需求分析
5.2 架构设计
EJB作为一种成熟的分布式对象组件技术,已经得到越来越广泛的应用,EJB已经成为了一种事实上的工业标准。它允许在一个供应商的EJB服务器上开发和部署的Bean能轻易地部署到其他供应商的EJB服务器上。最重要的是:开发EJB时已经不用考虑有关负载平衡、事务安全性等复杂的跨服务器技术,这些技术已经由EJB容器服务器实现,只要将具体的EJB Bean部署到EJB服务器中,经过适当简单的配置,开发者自己的应用系统就可以成为一个分布式的、集群的网络系统,这样使得开发大访问量的应用系统项目不再成为少数大型公司的专利。
5.2.1 Cache和Pool
在J2EE项目开发中,包括本项目,都会将重要的业务逻辑运算封装在EJB层,Web层只是作为EJB的一种客户端。这种设计安排已经不同于之前在JSP/Javabeans下的思路,在以前JSP/Javabeans框架下,业务逻辑运算等重要功能都是在Javabeans实现。但是随着应用系统访问用户的不断增长,系统的性能需要不断提高,提高性能方式有很多,主要思路是使用Cache(缓冲)和Pool(池)机制。
如果使用EJB,那么上述那些Cache或Pool、事务机制、安全访问权限以及分布式运算等都无需再考虑了,所有这些都将由EJB容器来完成。
5.2.2 EJB框架体系
在EJB中有两种类型:无状态Session Bean(Stateless session bean)和有状态Session Bean(Stateful session bean)。EJB容器使用Cache或Pool机制来支持EJB运行。
有状态Session Bean实际是运行在EJB容器的Cache中;而无状态Session Bean则是运行在EJB容器的Pool中。
5.2.3 架构图 5.2.4 接口框架设计
在本项目中,将建立一个通用的Web和EJB接口框架,实现图5-4中Web层与EJB层之间的统一通道。这种框架实现了两个容器之间的解耦,同时又使得Web层对EJB层的调用变得标准而有规则,可以推广应用到大多数J2EE项目中。
框架的优点:
可重用性强,使用方便。由于形成了一个标准框架,在具体应用中只要分别实现相应接口就可以直接使用该框架。
松散的耦合性。与Web层和EJB层都保持了松散的耦合性,同时为两者之间联系提供了一个可控制的通道。
易于开发,该框架好似一根扁担,一头挑起了Web层,另外一头挑起了EJB层,使得整个J2EE的开发分工明确。负责Web层程序开发的Team只要做好与框架的接口工作,而负责EJB层程序开发的Team做好与框架接口另外一边,再通过配置文件配置,就可以将整个J2EE系统轻松地组合运行起来。
易于拓展,可以在这个框架中很方便地加入缓冲Cache,这样减少Web层对EJB层的频繁访问。
5.3 EJB详细设计和实现
5.3.1 业务对象建模
5.3.2 开发环境配置
目前,使用JBoss 3.0 作为应用服务器,MySQL 4.0作为数据库,JBuilder作为编程工具和Rose作为设计工具的J2EE设计开发运行模式已经日趋成熟稳定。其显著特点是打破了J2EE成本居高不下的弊端,以极其低廉的运行成本受到广大用户的欢迎。
尤其是JBoss作为免费的J2EE服务器,对webshere和weblogic商业服务器形成了有力的冲击。SQL 4.0在继承以前版本快速稳定的基础上,整合了InnoDB (http://www.InnoDB.com/)先进的事务处理机制,从最近关于MySQL伴随Linux不断蚕食商业数据库市场的报道中,可以预见MySQL辉煌的未来。
EJB的开发如果完全手工编码是非常繁琐的,当然需要依靠强大的IDE工具来达到快速开发的目的,JBuilder作为快速有力的开发工具,其独特性主要表现在7.0以后版本提供的EJB快速可视化开发功能。
跟随最新技术版本新的开发环境请按这里。
5.3.3 CMP图形开发 5.3.4 实体Bean 5.3.5 Facade Session Bean
Facade模式可以在客户端和EJB之间建立一个统一接口,客户端只要直接和Facade类交互操作,通过Facade类再操作那些实体Bean,如图5-12所示。
5.3.6 Transfer Object 模式
客户端Web层向EJB层发出的调用无非是两个方面:数据和有关数据功能操作。前面章节已经通过Facade模式统一了有关数据功能操作的封装。在本节,将讨论数据本身的统一封装问题。
在实际项目中,客户端所要求数据项目也可能是多个的。例如,如果在客户端要显示一个客户的城市、街道等详细地址,那么是不是分别通过EJB查询数据库?如果每个数据项都分别操作一次EJB,那这种情况又类似于客户端直接操作实体Bean,带来的危害是增加了网络操作负担,降低了性能。
5.4 WEB与EJB接口框架
了避免在大项目中Web层和EJB层过分耦合,提高Web层和EJB层的开发效率,加强J2EE系统的可维护性和可拓展性,需要在Web层和EJB层之间建立一个类似接口网关的框架系统,这样可以适应推广在各个J2EE项目中。
5.4.1 框架的设计
在本项目中,由于使用了CustomerManager代表实体Bean的统一接口,这样客户端可以直接调用CustomerManager了。在本项目中这个客户端位于Web容器中,是Strut的一个Action,以CustomerAction为例,它负责处理前台JSP提交的表单数据,经过简单封装,然后递交到EJB层进行“深加工”
在CustomerAction中实现了对EJB的调用,这种将EJB调用直接嵌入Action中有很多缺点:
加重网络负担。如果在CustomerAction一个方法体内分别调用多个不同的Facade类,就类似客户端直接调用实体Bean的问题,大大加重了网络传输负担。
可维护性差。如果在CustomerAction再增加editCustomer等其他方法,CustomerAction就变得复杂,导致维护性和拓展性降低。
与Web层过分耦合。Action属于Strut的一个部分,这样造成整个系统严重依赖Strut,如果将来有新的客户端(非HTML等),Strut框架不能使用的情况下,需要对系统Web层进行较大改动。
与EJB层过分耦合。由于在Action代码中直接指定了EJB调用代码。如CustomerAction中直接指定了EJB CustomerManager作为逻辑运算的进一步处理。但是在一个大型系统中,这种情况随时会发生改变。如其他Team由于开发了新的客户EJB组件,那么就需要直接修改CustomerAction代码。
针对这些缺点,首先采取分离措施,将EJB访问调离Action类。这样在前台表现层和EJB层之间形成了一个新的Proxy层。
这些优点都从PetStore源码中可以发现,以用户资料修改为例,CustomerEvent通过WebController触发了EJBControllerLocal,而EJBControllerLoca委托状态机StateMachine读取配置文件eventmappings.xml调用相应的Action类进行处理,CustomerEvent对应的Action类是CustomerEJBAction,CustomerEJBAction是一个普通的Javabean,即处理CustomerEvent是由EJB委托普通Javabean来实现真正的逻辑运算,如创建新的客户资料。
这里有一个明显的缺点,事务处理机制粗糙。因为创建新客户资料每一句代码是在Javabean中实现,而不是在正常的session bean实现,因此不能实现方法体内的事务机制。
5.4.2 框架的实现
在该框架中,比较重要的核心类是ServiceProxy类。在该类中要做到一个接口的作用,承前启后,同时又不能与前后过分耦合,这是通过两种方式达到的:首先通过XML配置文件和Web层建立对应关系,这样是可扩展而且安全的;然后通过JNDI调用EJB层的一个Facade总类,这个Facade总类在具体应用系统中是可以定义的。
5.4.3 框架的使用
该框架比较方便,只要完成三步就可以:首先,将基本对象包装成Event事件;其次,实现ServiceProxy;最后,配置ServiceProxy和前台Action的对应表eventmappings.xml。
5.5 表现层的设计和实现
上节中已经实现了Struts的Action类CustomerAction,这里将在Struts框架进行其他的操作步骤,JBuilder 8/JBuilderX以后版本整合了Strut框架,因此利用JBuilder提供的可视化开发可以提高速度。
5.5.1 创建ActionForm
5.5.2 创建Action类
Action类主要是接受处理前台表单输入的数据,CustomerAction代码已经在前面章节罗列出,该Action类有两个步骤:
将CustomerForm中的数据导入CustomerDTO,CustomerForm中数据是JSP页面用户输入的值。
将CustomerDTO对象传送访问EJB层,实现customer数据的插入数据库操作。
CustomerAction处理成功后,将页面导向createCustomOk页面,这就需要在struts-config.xml中配置createCustomOk指向的页面。
5.5.3 创建Jsp页面
5.6 调试 配置和运行
EJB层和Web层开发完成后,可以进行各自的单元测试。通过使用JBuilder等IDE工具提供的EJB Test工具可以直接建立一些客户端对EJB实现测试。当然,EJB需要部署到EJB容器中才能运行,而各个EJB容器在遵循J2EE标准的总体框架下有些细微的区别。
本项目使用JBoss作为J2EE服务器,MySQL作为数据库服务器。目前需要对这两者进行配置,使得JBoss具备访问MySQL数据库的能力。
5.6.1 JBoss和MySQL的配置
各个版本JBoss和MySQL的配置有些细节的区别,总体可以掌握下列几点。
5.6.2 JNDI配置
在JBoss中部署J2EE应用程序,其EJB的配置文件共有3个:ejb-jar.xml、jboss.xml和jbosscmp-jdbc.xml,这些配置是将程序代码系统和容器服务器联系在一起的纽带。
其中最关键的,也是比较复杂的是有关JNDI和EJB引用reference的对应关系设置。因为EJB是分布式的,所以并没有按照一般思维习惯直接将JNDI和EJB对应起来,而是在JNDI和EJB之间加入了一个EJB引用reference,这样整个系统做到了灵活部署,但是也增加了复杂性。
5.6.3 部署和发布
由于本项目包括Web层和EJB层,相应的文件是.war和.jar,可以将这两个文件直接复制到JBoss/server/default/deploy下。如果把这两个文件打包成一个.ear文件,部署起来可能更方便。
通过JBuilder可以迅速建立一个EAR包,选择new →Enterprise的EAR,将需要的Web包和EJB包加入,如果需要其他EJB组件,也可以在这里一起加入。Rebuilder整个项目将生成如CmpSUB.ear这样的发布包,直接复制到JBoss/server/default/deploy下。这时会注意到JBoss控制台进行EAR发布的信息,分别部署其中的EJB和Web。
5.6.4 调试和测试
这时可以进入系统整合测试,整合测试相比部署时出现的问题更多。这时需要依靠Logger的日志输出,或者依赖Junit单元测试。个别按照正常逻辑不可思议发生的问题,可以使用JBuilder的断点调试功能追踪系统中实际运行的路线,可能发现一些潜在的问题。 这里有一些调试经验供参考:
5.7 小结
EJB的实战应用和其理论相差比较大,本章主要是通过一个小型的订阅信息系统介绍了EJB相关实战知识。 对于有丰富JavaBeans开发经验的程序员来说,JavaBeans应用基本分两大类:功能JavaBeans和数据JavaBeans。前者是实现一定功能的,有一定行为的JavaBeans;而后者则只是包含数据的、没有功能的JavaBeans。这也是通常说的Transfer Object、value Object或者POJO(无格式普通Java 对象,英文是Plain Old Java Object)。
第六章:用户安全管理系统
前面面的章节演示了一个简单的用户注册系统,但在中大型系统的分布式应用中,这个简单的用户注册系统已经完全不适用。同时,由于引入了复杂的资源权限控制访问机制,使得这样情况下的用户注册系统需要重新设计和架构。 J2EE提供了基于容器的用户安全管理框架,这个框架可以很方便地帮助解决在中大型分布式系统中的权限控制和验证问题。
6.1 需求分析
在很多实际应用中,不只是简单地要求用户需要注册登录。还要求不同的用户对不同资源拥有不同的权限。某单位的新闻部门的某些人才拥有对外发布新闻的权限;每个部门只有对自己部门内部资源才拥有创建、修改权限,对于其他部门创建的资源如网页等只有浏览的权限。这些复杂的用户权限验证需要可以总结为如下几点:
用户资料管理系统:对用户的注册资料进行管理。可进行用户资料的新增修改,能够查询获取丢失的密码。
权限的验证和授权:拦截所有访问受保护资源的请求,检查该用户是否被授权访问该资源。
可控制的资源限制访问:对于系统的资源,无论是数据或行为动作,对确定的一组用户都有限制访问。例如对于有的用户只允许读操作,不能进行写操作;有些资源只有创建者才可以访问和操作等。
这3种需求适合大部分应用系统,通过它们的实现,可以形成一个用户安全管理方面的组件框架,并且能够重复地在不同的应用系统中使用。
6.2 架构设计
实现一个完整的用户安全管理框架,可以有两种实现途径。在J2EE出现以前,大部分是由应用系统本身实现。因此,很多有经验的软件商都拥有自己成熟的用户安全管理系统,但是缺点也是比较明显,自己设计的用户安全管理系统可重用性低,有的和具体应用程序过分紧密地绑定在一起,无法移植到其他系统上。 但是,随着业务量上升,应用系统的不断增加,会出现不同应用系统拥有不同的用户登录验证体系,很显然,这给用户访问带来了不方便,用户不可能为每个系统注册一套用户和密码,定制一套用户角色。因此,整个服务器群中需要统一的用户权限验证体系。而J2EE容器的用户安全管理框架再辅助以LDAP或数据库系统,可以很方便地达到这个目标。
6.2.1 角色
J2EE容器的用户安全框架是基于RBAC(Roled-Based Access Control,相关网址:http://csrc.nist.gov/rbac/)设计模型建立的,这是一个基于角色的访问权限控制模型。
首先必须了解角色的含义,在RBAC中角色Role的定义是:Role是明确表达访问控制(Aceess Control)策略的一种语义构建词。
角色可以是指做某些事情的资格,比如医生或物理学家;也可以包含权力和责任的意思,如部门经理或局长等。角色和组(groups)是有区别的。组就是纯粹代表一群用户;角色一方面代表一系列用户,另外一方面可以代表一系列权限,因此可以说是用户和权限的结合体。
引入角色的概念主要是为了分离用户和访问权限的直接联系。用户与访问权限的直接组合可能是短暂的,而角色则可以相对稳定,因为一个系统中和角色相关的权限变化是有限的。
6.2.2 J2EE的JAAS
通过使用J2EE容器提供的JAAS (Java Authentication Authorization Service,Java验证和授权API),JAAS是J2EE服务器用来帮助应用系统实现安全功能的。当应用系统的开发者具体实现了LoginModule API,那么J2EE容器就执行LoginModule接口,通过接口和具体实现之间的关系,J2EE容器将结合具体应用系统实现特定的JAAS功能。
6.2.3 单点登陆
单点登录(SSO,single sign on)是指在分布式环境下,整个系统只有一个可以登录进入的点,它对所有的请求(Request)都是通用的。单点登录可以保证用户能够访问到可以访问的资源,如果有一个未被授权的请求要求访问被保护的资源,这个请求将自动被导向到相应的验证点进行登录验证。
J2EE容器支持单点登录模式的实现。通过使用J2EE Web层安全机制,可以保护Web层的一些资源如URL、URL模型以及HTTP的提交方式(POST或GET等)。当未被授权的用户访问这些受保护的资源时,J2EE容器会自动将用户导向到规定的登录界面,要求用户输入用户名和密码。
6.2.4 邮件发送组件
在本项目用户管理的需求中,需要对丢失的密码进行查询。输入用户的信箱,系统将查询后的密码发往用户的信箱。这将使用JavaMail连接专门的SMTP服务器进行信件发送,这个信件的发送过程有可能因为网络原因或其他未知原因导致处理时间延长,如果系统中的其他处理事务都要等待这个过程的完成,显然是没有效率而且问题会很多。
采用JMS的邮件发送组件是基于一个异步消息机制的可重用系统,该组件系统采取session bean作为Queue的消息生产者,而MDB作为Queue的消息使用者,作为EJB的一个实现,可以重复使用在需要邮件发送功能的应用系统中,如图6-4所示。
6.3 详细设计和实现 6.3.1 业务对象建模 6.3.2 数据库设计 6.3.3 实体Bean实现 6.3.4 Session Bean实现 6.3.5 EJB容器安全配置
J2EE容器的安全管理框架以角色为联系纽带,分两个方向。一个是用户资料系统;另外一个是访问权限系统。后者是通过web.xml或ejb-jar.xml配置实现的。
在本项目中,为了限制角色对某些类或方法的访问权限,可以在ejb-jar.xml中设置。
<assembly-des criptor>
<security-role>
<des cription>the role is super user </des cription>
<role-name>Admin</role-name>
</security-role>
<security-role>
<des cription>register user</des cription>
<role-name>User</role-name>
</security-role>
<method-permission>
<role-name>User</role-name>
<method>
<ejb-name>RoleManager</ejb-name>
<method-name>*</method-name>
</method>
</method-permission>
…
</assembly-des criptor>
在本EJB中定义了两个角色:Admin和User(当然可以根据实际情况定义角色)。具体的权限是在method-permission中设置,可以指定某个类的具体方法,“*”表示所有的方法。
6.4 JMS邮件发送组件
JMS中的消息类型有两种:Topic和Queue。Topic的操作是使用发布/订阅(publish/subscribe)的方式;Queue的操作是点对点(ponit to point)的方式。
publish/subscribe:发布者(publisher)发布消息到Topic,订阅者(subsribe)从Topic订阅消息,订阅者的数量是不受限制的。
ponit to point:点对点传输消息,建立在消息队列Queue的基础上,每个消息使用者只对应一个消息队列,不像publish/subscribe那样可以有很多消息使用者。
本项目中,可以由单个独立的程序来实现邮件发送,这个程序作为一个消息使用者,只需一个就可以了,因此使用Queue来实现。
6.4.1 消息发送器
AsyncSender属于消息生产者,它是具体应用系统直接调用的,可以使用无状态Session Bean来实现。
在AsyncSender中,首先要通过JNDI获得一个ConnectionFactory,创建一个QueueConnection实例,这样就获得与JMS Provider的一个连接。再由这个QueueConnection创建一个QueueSession,这样就启动了一个和JMS Provider连接相关的线程,这个线程可以是发送或接收消息。
6.4.2 MDB
在Queue的另外一端是消息的使用者。MDB(Message-Driven Beans)专门处理JMS异步消息,Session Bean和Entity Bean只允许同步地去发送消息和接收消息,不支持异步。MDB是一个message listener,它能够从一个Queue或一个durable subs cription中可靠地接收消息。
MDB与一个普通的消息使用者客户端的区别是,EJB容器将自动做下面的事情,无需应用者再自己编程实现:
创建一个消息接受者(QueueReceiver/TopicSubscriber)接收消息。在部署时,将destination和ConnectionFactory与MDB联合起来。在JBoss中通过指定destination-jndi-name来实现。
自动实现message listener接口(无需调用setMessageListener方法)。
容器自动指定了消息签收模式。
因此,使用MDB作为消息的使用者就非常简单,而且没有home和remote接口,只有一个bean类。
6.5 WEB层的实现
6.5.1 用户资料管理 6.5.2 WEB容器安全配置 6.6 调试 配置和运行
采用J2EE容器的安全机制实现本项目的安全框架。虽然代码编程工作减少,但带来的是详细而复杂的J2EE配置,在前面章节已经分别介绍在Web容器和EJB容器下如何实现角色的访问权限限制。在Web容器中,可以指定角色对路径模式(path pattern)的访问权限;在EJB容器中,可以指定角色对具体EJB类及其方法的访问权限。
6.6.1 JAAS配置
下面需要定制自己的LoginModule,在JBoss中已经绑定提供两个LoginModule。
org.JBoss.security.plugins.samples.LdapLoginModule 是通过JNDI访问LDAP 服务器的LoginModule,详细用法参考JBoss使用手册。
org.JBoss.security.plugins.samples.DatabaseServerLoginModule是基于数据库JDBC的LoginModule,基于两个基本逻辑表:Principals 和 Roles,在Principals表中有两个字段principalID和有效的password;而Roles表中有字段principalID和Role以及RoleGroup。
本项目可以直接使用DatabaseServerLoginModule作为LoginModule,配置JBoss/server/default/conf下的login-config.xml文件
6.6.2 邮件服务的配置
本项目中使用了邮件发送组件来发送用户丢失的密码,在开发配置中,需要3个配置在部署时配置J2EE服务器。前两个是QueueConnectionFactory和Queue,在JBoss中通过配置文件jbossmq-service.xml和jbossmq-destinations-service.xml(依JBoss具体版本有所区别)设置,这些可以参考JBoss手册配置自己的设置。在本项目中,这两个设置使用了现成的JBoss配置java:/ConnectionFactory和testQueue。
6.6.3 部署和发布
6.7 小结
本章主要是讨论如何利用J2EE容器的安全框架来实现应用系统的安全管理功能,这些操作实现原理可以应用于任何需要安全机制的系统。
相比前面章节讨论的“简单的用户注册系统”,本章部分操作显得过于复杂,但是这些技术的选用都是依据不同应用要求作出的。
使用J2EE容器的安全框架比较适合一些中大型项目,在这些项目中,数据量以及访问量都比较大,运行的J2EE服务器可能达到几十台甚至几百台,而且运行内容都千差万别。如何在这样一个复杂的分布式系统中实现一个统一的用户安全管理机制?使用J2EE的JAAS以及LDAP支持的单点登录才能满足这样的大型应用。
第七章:EJB方法调用框架
本章将介绍一种EJB方法调用框架,它使得组装业务功能的EJB适合专门的远程客户端调用,如Java Application、Applet或J2ME等客户端。这些客户端通过本框架和EJB服务实现交互,从而可以形成基于C/S(客户端/服务器)模式的多层结构系统。
本框架framework是通过Java的动态代理机制实现,而目前很多AOP(面向方面编程)框架也是基于动态代理机制实现,因此通过本框架的研发可有帮于掌握AOP原理。
7.1 框架概况
J2EE多层结构根据客户端形式的不同,分为两种主要形式,客户端基于浏览器的B/S多层结构和基于肥客户端的C/S多层结构。后者在一些专业专用领域有一定的应用市场,特别是Java无线技术的发展,J2ME作为一个拥有功能强大的手机终端系统正显现其特有的魅力。
使用肥客户端技术可以减轻J2EE服务器系统的操作开销,降低客户端和服务器端的通信次数,提供交互层次,改善数据的确认速度,缺点是需要用户专门下载安装。
7.1.1 远程调用技术背景
C/S多层结构首先需要解决的问题是:肥客户端和服务器之间的远程通信和调用。远程调用的概念由来已久,Java RMI (Remote Method Invocation 远程方法调用)是远程方法调用的简称,它大大增强了Java开发分布式应用的能力。
所谓远程方法调用,就是在一台机器上调用另外一台机器上对象的方法。正如前面章节所讨论的,客户端调用EJB一般是通过RMI方式进行的。这样,EJB能以分布式运行在多台服务器之间,对于客户端的调用是透明的,几乎没有什么区别。
RMI所提供的远程方法调用是有一定限制的,即“远程”并不意味着非常远,一般是指在同一个防火墙内的区域。因为实际应用中,网络防火墙因为安全原因,对大多数协议或端口都进行了封锁,在这种情况下,防火墙以外的客户端如果希望通过RMI调用EJB服务,那几乎是不可能的事情,因此,需要寻找一个能穿透大多数防火墙的协议,将远程客户端调用导入防火墙内部,再通过RMI调用EJB服务。
7.1.2 框架结构
在前面章节中也讨论了一种接口框架系统的设计,本框架是在其基础上再进行深入和抽象。同时兼顾到远程肥客户端的调用情况,更加提高了系统的可重用性以及可伸缩性,加快了系统开发进程。
7.2 框架设计
7.2.1 代理(Proxy)模式
在GOF设计模式中对代理模式的定义是:为其他对象提供一种代理,以控制对这个对象的访问。代理模式可以强迫客户端对一个对象的方法调用间接通过代理类进行。
通常代理模式有以下几种:访问代理(Access Proxy)、虚拟代理和远程代理等。
访问代理是对访问服务或数据对象实现安全权限控制,如Jive论坛中的代理模式就属于这种。
虚拟代理可实现对象不同方式的初始化,具体有两种方式:即时的和懒惰的,懒惰初始化是相对即时初始化而言,它不是在这个对象生成时立即进行初始化工作,而是只在被访问使用时的才进行初始化。
远程代理是用于屏蔽或保护客户端离开远距离的原始对象,当客户端访问一个对象需要经过漫长的网络调用或大数据读写时,可以通过在本地设置中间代理来代替远程的原始对象。
7.2.2 动态代理
动态代理(Dynamic Proxy)是JDK 1.3以后推出的新API,它是代理模式的一种高级实现,可以更加彻底地解耦调用者和被调用者之间的关系,动态代理利用Java的反射(Reflect)机制,可以在运行时刻将一个对象实例的方法调用分派到另外一个对象实例的调用,这种类似中途拦截的原理通常被AOP框架用来作为拦截器(Interceptor)实现,掌握动态代理是理解AOP框架原理的重要初始环节。
7.2.3 反射(Reflection)和方法
上节的Proxy.newProxyInstance属于Java反射(Reflection)的一种实现,除此之外,反射还有字段Field、方法Method等实现,Java反射机制是一种提供了类安全的,支持类和对象自省机制的API。
这样,通过客户端的动态代理机制和服务器端的方法反射机制,就可以完成远程客户端对服务器EJB Service的访问。那么,如何将远程客户端调用的EJB方法名、参数类型和参数数值传送到服务器端。
7.2.4 Http协议和对象序列化
HTTP协议是一种请求/应答式的协议。以浏览器为例,当浏览器发送一个请求,在后台建立一个连接到服务器80端口的基于Socket的TCP连接,服务器处理后,返回该请求的应答。HTTP协议最新的版本是HTTP/1.1,HTTP/1.1由 RFC 2616 定义。
7.2.5 框架设计图 7.2.6 HttpSession和缓存机制
尽管在J2SE 1.4以后版本中,Java的反射机制性能得到了很大提高。但是在实际运行中,还是需要通过缓存Cache来进一步提高性能。
考察本系统有两处需要实现缓存:
一个是Proxy.newProxyInstance生成的动态对象实例,如果每次都使用这条反射语句获得动态对象实例,显然会影响速度,可以在第一次生成动态对象实例后将其保存起来,下次需要该对象实例时从缓存中获取。
另外一处是EJBLocalObject或EJBObject实例的生成,当EJB创建后,调用该EJB的客户端拥有一个指向EJBLocalObject或EJBObject的引用。
7.2.7 基于Http的安全机制
从前面章节“用户安全管理系统”中已经知道,对用户名和口令进行拦截验证有两种主要方式:一种是使用容器的安全验证机制,如基于HTTP的验证登录机制;还有一种是自己实现用户名和口令的验证。可以根据实际情况进行两种方式的选用。
7.3 类的详细设计和实现
以肥客户端/服务器体系为例,肥客户端通过本框架系统获得EJB的代理实例后,启动EJB 的方法调用,动态代理机制将调用的方法名、参数类型和参数数值进行打包序列化,向远程J2EE服务器发出HTTP的请求。
7.3.1 基本业务对象
7.3.2 动态代理工厂
7.3.3 肥客户端/服务器架构下实现
7.3.4 Web层代理Sevlet Proxy 7.3.5 浏览器/服务器架构下实现 7.3.6 核心代理Business Proxy实现
框架核心部分主要是在Business Proxy中实现,尽管向Business Proxy发出请求可能来自两种客户端,但是Business Proxy无需进行这种区别,只要知道它们都是进行EJB服务调用请求的。
Business Proxy由两大部分组成:HTTPSession缓存处理和EJB方法调用。
ComponentManager是HTTPSessionBindingListener的一个实现者,是一个HTTPSession的监视器。其作用是能够在HTTPSession超时失效或用户退出调用removeAttribute方法时,自动激活valueUnbound(HTTPSessionBindingEvent event)方法。在这个方法中,实现EJBObject引用清除工作,将那些曾经被保存到HTTPSession中的EJBObject清除干净。这样,垃圾回收机制能够回收这些EJBObject,无疑可以提高内存使用性能,防止内存泄漏。
7.4 框架的使用和调试
7.4.1 配置
7.4.2 浏览器/服务器架构下的应用
7.4.3 肥客户端/服务器架构下的应用 7.5 小结
章主要介绍了一种EJB调用框架,通过Java的方法(Method)反射机制直接调用EJB方法。这种框架的最大优点是隔离了Web层和EJB层,实现了两者的完全解耦,简化了EJB服务的调用结构。 在中大型系统中,由于系统功能的复杂,导致实现这些功能的EJB数量剧增。同时,一个功能的完全实现常常是贯穿于Web层和EJB层,客户端通过Web层调用EJB方式可能有多种形式。最通常的情况是,Web层调用一个EJB Facade类,通过这个Facade再调用其他EJB,这种模式在EJB数量巨大的情况下,会导致系统调用混乱。有时除了开发者本人掌握调用的来龙去脉外,其他人很难了解其中的调用路径,犹如走入迷宫,这种情况必然导致系统的可维护性和可拓展性降低。
第八章:网上商店系统
PetStore(http://developer.java.sun.com/developer/releases/petstore/)是著名的J2EE学习例程。但是从实用角度考虑,该系统中很多功能无法实现重用。本章讨论网上商店系统,立足实用,并基于前面章节介绍的EJB Service方法调用框架,讨论如何建立一套快速的数据操作通用框架。 数据的增、删、改、查是信息系统最常用的基本功能。在传统的语言环境中,该功能虽然能够很方便地实现,但扩展性和维护性很差。在J2EE框架下,由于引入了多层结构又显得过于复杂,每个功能的实现需要穿越多个层次才能完成,降低了这些基本功能的开发速度。 本章将针对这种情况,讨论设计出一套J2EE框架下的数据操作通用框架。虽然该框架设计实现时有一定难度,但是使用起来却非常方便,可以在大量不同项目中反复重用,这将大大简化数据的增、删、改、查功能的开发过程,极大地提高J2EE开发速度,同时,又不丧失多层结构的天然优势,继承延续J2EE特有的可伸缩性和可扩展性。
8.1 系统需求和设计
8.1.1 基本业务对象
本系统中,基本业务对象有8个,分别是商品类别(Category)、商品(Product)、商品规格条目(Item)、订单(Order)、订单条目(OrderItem)、订单状态(OrderStatus)、订单地址(Address)和订单客户(Customer)。
8.1.2 数据表设计
8.2 数据操作通用框架
在本系统中,有相当一部分是数据的新增、修改、删除和查询,这些功能分别是在EJB和Web两层中实现的。 在EJB层,通过Facade Session Bean调用实体Bean,实现数据库数据的增、改、删和查,这些都是数据库的基本操作,能非常方便而且有规律地实现。 在Web层,需要通过相应界面来激活后台EJB的数据库操作功能,在使用Strut的情况下,这个过程也是有规律的。 首先看看在一般情况下,增、改、删和查功能是怎样在Struts下实现的,以本项目中的商品类别数据对象Category为例。
8.2.1 框架的提炼和设计
在设计一个框架之前,首先需要明确这个框架操作的对象是什么。 图8-5是从Model Object角度提炼数据对象的增、删、改和查功能抽象原理图,该图将一个数据对象在J2EE中划分为3种Object。第一种是界面显示的表单,这其实是一个ActionForm对象;中间一种是Model或者是DTO,在表现层和EJB层之间实现数据交换;第三种是EJB持久层的实体Bean Object,代表数据库中数据表数据。
8.2.2 增删改查框架实现
在ModelViewAction代码中,ModelHandler的具体子类是从配置文件中获得,数据对象的key也是这样。使用配置文件,可以将具体的子类实现赋值到这个框架系统中。在这个新的层面框架中,有3个抽象对象需要实现:Model、ModelForm和ModelHandler,它们都要根据具体数据对象,实现不同的子类。这些都是通过modelmapping.xml配置实现的。
8.3 商品类别管理功能的实现
虽然增、删、改、查通用数据操作框架在设计上比较复杂,但是使用起来非常方便。下面以本系统为例,展示该框架结合EJB 和EJB Service调用框架的使用。
8.3.1 创建Session Bean 8.3.2 EJB配置 8.3.3 创建Category相关类实现 8.3.4 Web配置 8.3.5 创建Category.sp
8.4 商品管理功能的实现
8.4.1 创建ProductManager 8.4.2 EJB配置 8.4.3 创建Product相关类实现 8.4.4 Web配置 8.4.5 创建Product.jsp 8.4.6 商品图片上传功能
8.5 商品批量查询和多页显示
在本系统中,一个商品类别下可能有很多商品,这些商品可能需要多个页面才能完全显示。这涉及到一个批量查询、多页显示的框架设计。 该框架需要分别在EJB和Web两个层面实现。EJB实现需要使用DAO+JDBC模式,而Web主要是解决多页显示的问题。 通常的思路是:从数据库中将当前页面需要显示的数据对象查询后,组成数据Model集合,传送到Web层用于显示。 这种思路有一个缺点:将不能使用设置在Web层的Model缓存系统。Web层的Model缓存保存着大量之前调用过的数据Model。在批量规模查询时,不使用这些缓存将浪费很多性能,特别是这些数据Model被不同客户反复调用查询时。
8.5.1 DAO模式
DAO模式是J2EE核心模式中一种,DAO模式主要是在业务核心和具体数据源之间增加了一层,这样实现了两者的解耦。因为具体持久层数据源是可能多样化的,可能是XML或者可能是关系数据库,在具体关系数据库中,也可能有不同的产品如Oracle或MySQL,通过使用DAO模式,业务核心部分就无需涉及具体数据库是如何实现数据库操作的,如图8-10所示。
8.5.2 Struts框架下设计和实现
设计目标的两个功能已经在EJB中实现,剩余的3个功能将需要在Web中实现。在Struts框架下,这3个功能将分别在Action、ActionForm和taglib 3个组件技术中实现。
8.5.3 页导航条实现
以上讨论的是在Strut框架下的实现,ModelListForm实现了页面上数据列表显示。除此之外,还要在JSP页面中实现页导航条显示,如下: 该导航条使用taglib来实现,有专门的taglib开源项目,这里只是简单地实现一下。实现原理参考Jive论坛的页导航条功能,Jive采取在JSP中写入Java代码的方式实现的,该方式不利于重用和封装,改进方法就是将其转换成JSP taglib来实现。
8.6 购物车功能的实现
商店客户浏览商品时,可以将愿意购买的商品品种放入购物车,购物车功能使用由状态Session Bean实现,首先根据功能需求进行基本对象建模。 经过分析,购物车有两个基本业务对象:ShoppingCart和CartItem。ShoppingCart代表整个购物车,CartItem则是车中每个购买的品种。CartItem和商品Item有一点区别,CartItem包含Item一些属性,但增加了一个购买数量属性。
8.6.1 有状态Session Bean
EJB ShoppingCart是一个有状态Session Bean。商店客户在浏览多个商品页面,不断地挑选商品,那么之前的购物状态一定要保存,使用有状态Session Bean保存购物车状态是一个好办法。 注意,有状态Session Bean的引用需要保存在HttpSession中,以便同一个客户再次访问时能获取到同一个有状态Session Bean。所幸的是,因为这里使用了“EJB方法调用框架”,该机制已经被框架实现了,因此就无需再考虑这些细节了。
8.6.2 WEB
8.7 小结
一个软件系统一般由两大部分组成:针对本应用的新设计和可重用的软件组件或框架。如果后者占据越大,无疑需要实现的新设计或实现工作量就越小,生产效率越高,成本越低。因此,可根据自己项目领域特点,在J2EE框架下架构设计自己领域的应用框架,提高在该领域软件开发生产的效率,节约成本。
电子书:PDF文件。
www.zasp.net仅提供试看,如需要请购买原版书。请用下载工具下载,否则下载不了的。 |