西北工业大学网络与控制研究所 于彩荣 戴冠中 朱正超 | ||
一、概述
模式是用来描述所交流的问题及其解决方案。简单的说,模式可以帮助我们在一个特定的环境中整理并记录已知的可重现的问题及其解决方案,并且通过模式来与他人交流这些知识。模式的目标就是
通常情况下,模式尽管有时不易理解,但使用却非常简单。模式是实践的总结,他提供的解决方案是经过了在不同时间、不同项目中仿佛解决了相似的问题后才最终确定的。因此,模式提供了强大的可重用机制,避免了开发者和设计者的重复投资。 J2EE为我们提供了大量的模式,但是,没有一个模式是独立的实体,每个模式都存在着胆量的复杂关系,并且这些关系有时又被作为模式语言的一部分。所以,开发者不应仅仅了解存在于孤立环境中的离散模式,而应当去寻找最佳的实践方式,机如何将这些模式连接在一起形成更完善的解决方案。以这种最佳方式连接多个模式就是所谓的权衡J2EE模式构架。构架就是将模式连接在一起形成的满足一定需求的解决方案。所以,我们需要 ○ 确定场景并提供应用与每一层的模式 ○ 确定模式组合或者主题,以提供模式构架 与传统的二层体系结构相比,J2EE有两个特点: ○ 定义了一套标准化组件,通过为这些组件提供完整的服务。 ○ 使用多层分布式的应用程序模型。应用程序的逻辑根据其实现的不同功能被封装到不同的组件中。组件的位置取决于组件本身在J2EE中所处的层次,如图1所示。
这种多层结构使企业级应用具有很强的伸缩性,允许各层专注于某种特定的角色: ○ Client Tier用于显示。在典型的Web应用中,客户端机器上运行的浏览器负责实现用户界面。 ○ Web Tier用于生成动态显示。这一层采用WEB组件,它包括servlet和 JSP页面:Servlets是一个Java类,可以动态地处理请求并作出响应;JSP页面是一个基于文本的文档,以servlet方式执行,但是它可以更方便地建立静态内容。所以,通常用servlet 来控制流转,Jsp来负责显示生成的内容。 ○ Business Tier用于实现业务逻辑。 这一层采用EJB组件的Session Bean和Entity Bean。 Session Bean的主要目的是让程序开发者将逻辑层抽离,特别是复杂的逻辑可以放在其中; Entity Bean是持久数据的对象表示,持久数据存储在诸如数据库等持久数据存储中。 ○ EIS Tier用于数据库服务。 本文认为客户层和资源层不是J2EE平台直接关注的问题,所以重点介绍Web层和Business层。在Web层,本文采用了前端控制器模式和视图助手模式;在Business层,本文着重于Session Façade模式和值对象模式。
二、Business Tier的设计 值对象模式 首先,为数据库中的每个表创建一个实体Bean,并采用CMP(容器管理持久性)模式。 使用CMP的好处在于容器提供公共的服务,例如目录服务、事务管理、安全性、持久性、资源缓冲池以及容错性等,使开发人员不必维护将会集成到业务逻辑中的系统级代码,只需专注于商业逻辑。 J2EE应用程序把服务器端业务组件实现为会话Bean和实体Bean,在这里是Entity Bean。业务组件的一些方法可以向客户端返回数据。通常,客户端需要多次调用业务对象的get方法直到获得所有的属性值,而且每次调用都是一次网络调用,都会造成系统性能的退化,当调用次数增多时,系统性能下降的很厉害。如图2-1。
这就要求有一种方法使客户端可以一次调用得到所需的大量数据,这种方法就是Value Object(值对象)模式。值对象是任意的可串行化的Java对象,也被称为值的对象,它在一次网络传输中包含和封装了大量的数据并被保存在内存中。这样,当客户端需要再次使用数据的时候,不用再次到数据库中查询,而是直接在内存中读取值对象,节省了大量的时间和系统开销,如图2-2。
值对象模式有两种策略――可更新的值对象策略和多值对象策略。 可更新的值对象策略中,业务对象负责创建值对象,并且在客户端请求时把该值对象返回给客户端;同时,业务对象也可以从客户端接收数据,形成值对象,并使用该对象来完成更新。 例如,在银行系统的例子中,Account 中提供一个以AccountValue为参数的setAccountValueObject方法,这样客户端可以通过这个方法来设置值对象的值,而不采用实体bean--Account中设置每个属性的方法(setBalance()),因为后一种方法会导致大量的网络负载。由于值对象的易变性,所以值对象类必须给每个可以被客户端更新的属性提供设置方法。例如,AccountValue中的setBalance()方法。这样,一旦某客户端拥有来自业务对象的值对象,客户端就可以在本地调用必要的设置方法来更改属性值,然后调用业务对象的setAccountValueObject()方法更新业务对象。 多值对象策略 一些应用程序业务对象往往比较复杂,在这种情况下,根据客户端请求不同,有可能单个业务对象会产生多个不同的值对象。在这种情况下,可以考虑采用多值对象策略。这种策略的实现比较简单,就是在entity bean中增加不同的Get×××ValueObject()方法和set×××ValueObject()方法。 Session Façade 模式 有了实体Bean,客户端就可以直接调用它以获得数据。也就是说实体Bean封装了业务数据,并把他们的接口暴露给客户,因而也就把分布式服务的复杂性暴露给客户。在对J2EE 应用程序环境下,一般会产生如下问题: ■ 紧密耦合,这回导致客户端和业务对象的直接依赖关系 ■ 客户端和服务器之间的网络方法调用太多,容易导致网络性能问题 ■ 缺乏统一的客户访问策略,容易误用业务对象 ■ 如果实体bean的API改动,那么用户端的一些代码也要修改,扩展性很差 解决这些问题的方法就是把客户端和实体bean分割开。本文采用Session Facade模式,如图3-2所示。该模式通过一个Session Bean,为一系列的实体bean提供统一的接口来实现流程。事实上,客户端只是使用这个接口来触发流程。这样,所有关于实体bean实现流程所需要的改变,都和客户端无关。当实体bean改变时,我们不用改变客户端的代码,只要对Session Bean作出相应的改变即可,大大提高了系统的可维护性。
通过实体bean来表示业务对象是session façade的最常见用法。但多个实体bean参与某用例时,不必向客户暴露所有实体bean。相反的,用session bean 包装这些实体bean ,并且提供粗粒度方法来执行所需的业务功能,从而隐藏了实体bean交互的复杂性。 但是千万不要以为Façade模式就是简单的用Session Bean把Entity Bean的所有方法统统封装起来,而不提供任何额外的抽象。其实这是对Façade模式的滥用。这样做并不是降低整个系统的复杂性,而是把复杂性转移到另一个对象上。 正确应用Façade模式应遵循三条基本原则: ○ 他们自己不作实际工作,而是委派其他对象作实际工作。 ○ 他们提供简单的接口。 ○ 他们是底层系统的客户端接口。他们应该把特定于子系统的信息封装起来,并且不应该在不必要的情况下公开它。 例子代码 下面用一个简单的银行系统的例子来解释Façade模式和Value Object模式的具体应用。 √ 创建Entity Bean。其中对每个属性的get和set方法是自动生成的,我们不去管它。 public interface Account extends javax.ejb.EJBObject { java.lang.String getAccountNumber() throws java.rmi.RemoteException; double getBalance() throws java.rmi.RemoteException; void setBalance(double newValue) throws java.rmi.RemoteException; private AccountValue creaeAccountValueObject(); void setAccountVauleObject(AccountValue v); AccountValue getAccountValueObject(); …… } 其中 private AccountValue createAccountValueObject(){ AccountValue vo=new AccountValue(); vo. accountNumber=accountNumber; Vo.balance=balance; …… } public AccountValue getAccountValueObject(){ return createAccountValueObject(); } public void setAccountValueObject(AccountValue v){ accountNumber=v. accountNumber; balance=v.balance; …… } √ 用值对象封装Entity Bean数据。 public class AccountValue implements java.io.Serializable { private java.lang.String accountNumber; private double balance; void setBalance(double newValue) …… } √ 用Factory或者是Action类逻辑方法,涉及到数据的地方使用值对象。 public class AccountFactory { private static AccountHome accountHome = null; …… } public java.util.Vector getAccounts(String userid) throws FactoryException { try { Vector vect = new Vector(); AccountHome home = getAccountHome(); Enumeration accountRefs = home.findByUserid(userid); while (accountRefs.hasMoreElements()) { Account acc = (Account)accountRefs.nextElement(); AccountValue valueObject =acc.getAccountValueObjcet(); vect.addElement(valueObject); } return vect; } catch (Exception e) { throw new FactoryException(“Cannot generate accounts due to wrapped exception " + e); } } √ 在Session Bean的方法中调用Factory或者是Action对象。 public interface AccountSes extends javax.ejb.EJBObject {//Session bean 的远程接口 java.util.Vector getAccounts(java.lang.String userid) throws java.rmi.RemoteException,com.ibm.bankexample.domain.FactoryException; …… } public java.util.Vector getAccounts(String userid) throws FactoryException { AccountFactory fact = new AccountFactory(); Vector result = fact.getAccounts(userid); return result; } 正如代码所示,使用session façade模式,可以 ○ 提供统一的接口:会话外观抽象了业务组件交互的复杂性,并且向客户端提供一个更简单的接口。 ○ 减少耦合提高可管理性:会话外观分离了业务对象和客户端,这样可以减少紧密耦合,以及客户端对业务对象的依赖性。 ○ 提供粗粒度访问:会话外观减少客户端和服务器之间的网络负载。客户端与业务数据的说有交互都是通过会话外观以粗粒度的发拿过是进行的。 此外,本论文也采用其他的小模式来提高系统性能,比如服务器位器模式,在此不作进一步介绍。 三、Web Tier 在建立服务器端Web应用程序时,将表现层与业务逻辑分离可以更容易的创建动态的Web页面,同时也可以让没有应用开发经验的Web页面设计人员能非常容易的改变Web站点的外观。对于一个内容需要频繁更新的Web站点,这就意味着更新周期更短,可以以最快的速度带给本站点的访问者以最新的内容。 早期的Web应用程序结构都很简单,通常用户界面与业务逻辑都混合在一起。修改这种应用的任何一方,都将使维护整个应用程序变得十分困难。Servlet和JSP技术能够隔离用户界面与业务逻辑,从而简化应用程序的维护,使程序员可以更快更容易地改变应用程序。 从理论上讲,用户可以只使用Servlet来接收从Web浏览器发来的HTTP请求,Servlet动态地处理请求,然后直接发送HTML或XML文档的响应给浏览器。这种方法原理上可行,但是如果要改变页面的外观就必须重新编辑、编译Servlet。这不仅要求动态Web页面的设计人员需要有应用程序开发经验,而且使程序难以维护。 我们采用前端控制器模型和视图助手模型来解决这个问题。在前端控制器模型的实现上,我们采用了servlet前端策略。该策略把控制器实现为一个servlet。控制器负载与业务处理和控制流有关的请求处理。在后者的实现上,本文采用Jsp视图策略。就是使用Jsp作为视图组件,Jsp仅负责显示而没有任何业务处理。 继续采用银行系统的例子如下: √ 客户端浏览器发起的请求直接到了servlet。 <FORM action="/LoginServlet" method="POST"> <input type="text" size="30" name="UserId"> <input type=submit value=" Submit "> </FORM> √ Servlet调用Session Bean 方法查询结果,然后将结果放入session中,调用JSP来处理这个响应。 java.util.Vector rs=new Vector(); HttpSession session=request.getSession(true); String id=request.getParameter("UserId"); Context ic=new InitialContext(); Object obj=ic.lookup("AccountSes"); acc=(AccountSesHome)PortableRemoteObject.narrow(obj,AccountSesHome.class); rs=acc.getAccounts(id); session.setAttribute("rs",rs); getServletContext().getRequestDispatcher("/display.jsp").forward(req,res); JSP决定产生给用户响应的内容,它只包含如何格式化表现层的逻辑。 <TR> <TH>AccountNum </TH> <TH>Balance </TH> </TR> <% Vector rs=new Vector(); AccountValue valueObject = new AccountValue(); rs=( Vector)session.getAttribute("rs"); while(!rs.isEmpty()) { valueObject=rs.remove(0);%> <TR> <TD><%=valueObject.getAccountNumber ()%></TD> <TD><%= valueObject.getBalance()%></TD> </TR> 隔离Servlet开发与JSP显示为应用开发人员和Web页面设计人员彼此独立的工作带来了极大的方便。 总结 综上所述,本文系统地阐述了基于J2EE的3层B/S系统的构建方法, 综合应用了EJB,Servlet,Jsp技术,融合了几种不同模式,很好的实现了数据封装,合理分层以及显示与逻辑分离,大大提高了系统的可维护性和可伸缩性,也显著的简化了具有可伸缩性和高度负责的企业级应用的开发。 |
使用模式构建基于J2EE的企业信息系统
最新推荐文章于 2009-04-09 21:39:18 发布