站内搜索: 请输入搜索关键词

当前页面: 开发资料首页J2EE 专题J2EE体系结构设计(一)

J2EE体系结构设计(一)

摘要: J2EE体系结构设计(一)
google_ad_client = "pub-4944583547581781"; google_ad_width = 728; google_ad_height = 90; google_ad_format = "728x90_as"; google_ad_channel ="1429059098"; google_color_border = "FFFFFF"; google_color_bg = "FFFFFF"; google_color_link = "0000FF"; google_color_url = "000000"; google_color_text = "000000"; //

目前大多数企业采用J2EE技术的结构设计与解决方案。对于我们学习和研究J2EE体系结构来说,了解与掌握J2EE体系结构的设计方法及一些常用模式是必须的;模型-视图-控制(model-view-control,简称MVC)结构是目前最常见的J2EE应用所基于的体系结构,MVC主要适用于交互式的Web应用,尤其是存在大量页面及多次客户访问及数据显示;相比较而言,一个工作流体系结构更多应用于过程控制和较少交互的情况下;除了体系结构外,J2EE的设计模式对我们解决应用系统的设计也有很大的帮助。

一、J2EE的模型-视图-控制(MVC)体系结构

模型-视图-控制结构是交互式应用程序广泛使用的一种体系结构。它有效地在存储和展示数据的对象中区分功能模块以降低它们之间的连接度,这种体系结构将传统的输入、处理和输入模型转化为图形显示的用户交互模型,或者换一种说法,是多层次的Web商业应用;MVC体系结构具有三个层面:模型(Model)、视图(View)和控制(Controller),每个层面有其各自的功能作用,MVC体系结构如下:

图1 MVC 体系结构

模型层负责表达和访问商业数据,执行商业逻辑和操作。也就是说,这一层就是现实生活中功能的软件模拟;在模型层变化的时候,它将通知视图层并提供后者访问自身状态的能力,同时控制层也可以访问其功能函数以完成相关的任务。

视图层负责显示模型层的内容。它从模型层取得数据并指定这些数据如何被显示出来。在模型层变化的时候,它将自动更新。另外视图层也会将用户的输入传送给控制器。

控制层负责定义应用程序的行为。它可以分派用户的请求并选择恰当的视图以用于显示,同时它也可以解释用户的输入并将它们映射为模型层可执行的操作;在一个图形界面中,常见的用户输入包括点击按钮和菜单选择。在Web应用中,它包括对Web层的HTTP GET和POST的请求;控制层可以基于用户的交互和模型层的操作结果来选择下一个可以显示的视图,一个应用程序通常会基于一组相关功能设定一个控制层的模块,甚至一些应用程序会根据不同的用户类型具有不同的控制层设定,这主要是由于不同用户的视图交互和选择也是不同的。

在模型层、视图层和控制层之间划分责任可以减少代码的重复度,并使应用程序维护起来更简单。同时由于数据和商务逻辑的分开,在新的数据源加入和数据显示变化的时候,数据处理也会变得更简单。

二、J2EE设计模式

<table cellSpacing=0 cellPadding=0 width=0 align=right border=0> <tr> <td align=middle></td></tr> <tr> <td></td></tr></table>

一个设计模式描述了对于特定设计问题被验证的解决方案,它综合了所有开发者对这个问题所在领域的知识和见解;同时也是对于常见问题的可重用方案,它们一般适用于单个问题,但是组织在一起就可以提供整个企业系统的解决方案。下面我们列举八种常用于J2EE平台的设计模式,并对每种模式作简单的介绍,便于大家学习、理解与灵活应用。

1、前控制器

前控制器(front controller)主要提供一种可以集中式管理请求的控制器,一个前控制器可以接受所有的客户请求,将每个请求递交给相应的请求句柄,并适当地响应用户。

前控制器也是表示层的设计模式,它的出现主要是由于表示层通常需要控制和协调来自不同用户的多个请求,而这种控制机制又根据不同的需要,可能会集中式控制或分散式控制。换句话说,就是应用系统需要对于表示层的请求提供一个集中式控制模块,以提供各种系统服务,包括内容提取、视图管理和浏览,如果系统中没有这种集中式控制模块或控制机制,每个不同的系统服务都需要进行单独的视图处理,这样代码的重复性就会提高,致使系统开发代价提高;同时,如果没有一个固定模块管理视图之间的浏览机制,致使其浏览功能下放于每个不同的视图中,最终必将使得系统的可维护性受到破坏;本文中我们主要讨论的是集中式控制模块,而不是分散式控制,因为前者更适合于大型的应用系统。

基于上面所说的问题,研究人员提出了前控制器的设计模式。在这种模式中,控制器提供一个处理不同请求的控制点,这里的处理工作包括安全事务、视图选择、错误处理和响应内容的生成;通过将这些处理工作集中在一点进行,大大地减低了Java代码量,同时这种方法也可以减少在视图模块的程序逻辑,保证了在不同请求之间可以重用大量的逻辑代码。通常,控制器都是和一个分派组件联合工作的,分派组件主要是用于视图管理和浏览,也就是为用户选择下一个应该显示的视图,并同时提供对于相关显示资源的控制。分派组件可以包含在控制器之内,或是在另外一个单独的组件中;虽然前控制器模式推荐对于全部的请求使用统一处理,但是它也没有限制在一个系统中只能具有一个控制器,在系统中的每个层次都可以具有多个控制器,并且映射至不同的系统服务,下图2显示了前控制器的类图。

图2 前控制器的类图

图3显示了前控制器的序列图,表示一个控制器如何处理相关的请求。

图3前控制器序列图

下面我们来讨论一下图3的各个组件。

2、控制器

控制器(controller)是负责处理各种客户请求的控制点,并可以将一定的职能(如用户认证等)下放给帮助类。

(1)分派组件(Dispatcher)。一个分派组件主要是用于视图的管理和浏览,为用户选择下一个可以显示的视图,并管理相关的显示资源;分派组件可以在一个控制器内运行,或者作为一个单独的组件与控制器协同工作;开发人员可以在分派组件中实现静态的视图分派技术,或是复杂的动态分派。

(2)帮助类(Helper)。帮助类负责帮助一个视图或控制器来完成其处理工作,因此,帮助类具有多项职责,包括收集数据、存储中间数据模型等;另外,帮助类也可以在保证数据完整性和准确性的情况下,为不同显示需求修改数据模型;也就是说,根据用户的请求,帮助类可以向视图提供未经处理的原始数据,或是已经格式化后的Web内容,一个视图同时可以和多个帮助类协同工作,而后者通常是由JavaBeans和标签(tag)实现的。

3、视图

视图(view)负责向用户显示信息,而帮助类则负责支持视图的工作,即打包和建立相应的数据模型,下面我们介绍几种可以实现控制器的方法。

1)基于Servlet前控制器

这种方法建议使用servlet来实现一个控制器,尽管在语法上相差无几,但是它比使用JSP来实现要优越一些;因为控制器所进行的请求处理,多数都是与程序运行和控制流动相关的,这些处理工作虽然与显示模式相关,但是实际上是逻辑独立的,所以它们更适合在servlet中实现,而不是JSP技术中;使用这种方法也存在一些弱点,比如说servlet无法使用JSP运行环境的资源,如请求参数等,但是这个弱点也不是不能解决的,我们可以在servlet中建立相关的句柄来访问同样的资源,当然其代码会变得繁琐一点。

2)基于JSP的前控制器

这种方法建议使用JSP页面实现控制器,尽管语法上相同,但是Servlet方案要比其优越一些;因为控制器所处理的逻辑一般都不是有关显示模式的,所以在JSP页面中实现控制器似乎有点风马牛不相及;使用这种方法也不利于开发团队的角色和职责的分配,即软件开发人员需要在负责显示逻辑的JSP页面中修改请求处理的代码,通常,这种工作都是相当复杂的,尤其考虑整个JSP页面的编程、编译、测试和调试错误。

3)控制器之中的分派组件

如果分派组件没有较多功能,开发人员可以在控制器实现该组件。

4)基础前端

基于使用servlet实现前控制器,这种方案建议实现一个控制器作为基础类,这样其他的控制器可以在其之上扩展;这个基础类可以包含一些通用的逻辑实现,它的子类就会重载这些实现代码,这种方法也有一定的缺陷,当有许多子类继承这个基础类,并大量地重用代码时,那么就有可能出现一个类的改变会影响到所有子类的情况。

5)用过滤器实现前控制器

过滤器提供了与用户请求的中心处理相类似的功能,也就是说,控制器的一些功能可以由过滤器来实现,这种方案的过滤器主要负责处理请求的截取和解释,而不是请求的处理和响应的生成;通常可以为应用系统提供一个核心控制点,以处理所有的系统服务和程序逻辑,核心控制也就表明了所有的请求都可以简单地被跟踪和记录,从而方便各种服务功能的实施;当然,它也存在一些缺点,一个核心控制点的小问题可能会引发系统的崩溃,但在应用系统的实际开发中,这并不是个问题,因为通常我们都会在同一个层面上实现多个控制器,从而避免了这个缺陷;在控制器中,开发人员可以很方便地实现一个检查安全机制的组件,从而可以在最外层屏蔽对系统的恶意访问,另外使用控制器也会提高系统模块的可重用性,尤其在控制器同时使用帮助类的时候。

4、视图帮助

视图帮助(View helper)是属于表示层的设计模式,一个视图帮助可以包含相关视图中的数据访问和内容显示的逻辑,并可以精炼简化视图;显示逻辑主要是关于如何格式化页面上的数据,而访问逻辑则是关于如何取出数据,视图帮助通常用来显示数据的JSP标记(tag)或是读取数据的JavaBean。

这种设计模式的出现主要是由于目前的应用系统通常需要实时地开发显示内容,并且能处理动态的程序数据。如果这些程序数据的访问逻辑和显示逻辑的关系过于紧密,则系统的表示层就会经常需要改动,从而系统的灵活性、重用性会大大地受到破坏;同时在相同的模块中实现访问逻辑和显示逻辑将会影响系统的模块化,也会使得开发团队的任务划分不清。

一个视图通常包含格式化信息,并将其处理任务分发给自己的帮助类,后者通常是用JavaBeans或标记(tag)来实现的,帮助类同时可以存储视图的中间数据模型并实现数据适配器的功能,即适当地转化数据格式;开发人员可以采用多种方法实现视图组件,通常,开发人员可以使用JSP来实现,并且这也是一种值得推荐的方法。当然,相应地开发人员也可以使用Servlet来实现它,将视图中一定的程序逻辑植入到帮助类中,会有利于应用系统的模块化和可重用性。系统可以使用同一个帮助类为不同的用户显示不同的数据信息,并在不同的显示格式下显示;通常,如果开发人员发现视图的JSP页面中存在大量的脚本代码时,就可以考虑使用视图帮助这种模式了,因为在这种情况下,基本都是程序逻辑和显示逻辑具有过于紧密的联系;这时开发人员可以将一些适用于所有类型的请求的逻辑处理放置到一定的帮助类中,而根据需要,也可以将另外一些逻辑处理放置在视图层上的其他程序模块中,比如说以前讨论过的截取过滤器。

视图帮助这种模式的设计理念主要是分离应用系统的逻辑职责,下面我们提供一些图示,以方便大家更好地理解这种模式。

图4以类图(class diagram)的形式说明了视图帮助的系统结构。

图4 视图帮助类图

图5表示了视图帮助模式的序列图,它表明了这种模式中的主要成分及互相之间的运行情况;不过需要说明的是,在很多应用系统中,客户端和视图层之间会存在一个控制器加以适当的调节。

图5视图帮助序列图

在类图表中,大家可以发现,可能存在没有任何相关帮助类的视图,这种情况下,通常代表视图的JSP页面会有一些静态的或小数量的脚本代码。

这里我们对于序列图中的各个元素加以简单的介绍:

(1)视图(view)。视图负责向用户展示动态数据信息,而帮助类则负责支持视图的工作,即打包和建立相应的数据模型。

(2)帮助类(helper)。一个帮助类负责帮助视图或控制器完成相关的处理工作,包括收集数据、存储中间模型等;帮助类也可以在保证数据完整性和准确性的情况下,为不同显示需求修改数据模型,也就是说,根据用户的请求,帮助类可以向视图提供未经处理的原始数据,或是已经格式化后的Web内容;一个视图同时可以和多个帮助类协同工作,而后者通常是由JavaBeans和标记(tag)实现的。

(3)值bean(ValueBean)。值bean实际上是用于存储中间数据模型的帮助类的另一种叫法,例如在序列图5中,business service就根据请求返回了一个值bean。

(4)业务服务(business service)。业务服务是指用户试图得到的,应用系统可以提供的相关服务;通常来说,业务服务可以通过一个业务代表(business delegate)来访问,而后者主要是提供对于业务服务的控制和保护。

在应用系统的视图模块中使用帮助类可以将不同的程序逻辑很好地分离开来,并在视图模块之外为开发人员提供设计程序逻辑的空间;基于JavaBean和标记(tag)所开发的帮助类通常都可以被多个视图模块重用,因此也提高了组件的重用性和可维护性;把显示逻辑从数据处理逻辑分离出来,也有利于开发团队中角色及人物的划分;比如说,如果各种程序逻辑过于结合的话,软件开发人员可能需要在HTML,网页中修改代码而Web设计师则需要在处理数据访问的JSP中修改页面布置,这些情况都可能会导致系统设计和开发中由于不同技术人员的介入,而产生相关的问题。

5、会话面

会话面(session facade)模式在合作的企业对象间调节操作,并将应用函数合成一个单一简单的界面;它减少了类之间合作的复杂性,并使得类的调用者在该类变化的时候无需改动,这种模式通常以一个会话bean实现,以用来隐藏底层ejb的复杂交互。

这种设计模式出现的背景在于EJB通常既包括程序数据,又包括程序逻辑,而这些代码都会通过一定的界面作用于客户层,在多层次的J2EE平台应用程序中,就会造成一定的困难。

具体来说,在J2EE平台上的多层次系统中,通常会存在以下的问题:

(1)层次之间联系过于紧密,客户层和后端的业务对象具有较强的依赖关系;

(2)在客户和服务器之间有多次方法调用,因而导致了Web性能方面的问题;

(3)缺乏一定的客户访问机制,使得一些后台对象被随便访问。

一个多层次的J2EE应用程序通常具有很多由EJB实现的服务器端对象,它们通常负责提供系统服务、数据信息等,也就是说作为业务对象,它们既包括相关的程序数据,也包括其程序逻辑;在J2EE应用系统中,负责程序逻辑的对象通常由会话bean实现,而表示持久性存储,并在多个用户间共享的对象则由实体bean来实现;当然,应用系统的用户需要访问企业对象来满足自己的需求,如果企业对象向用户提供接口,用户可以直接地与相关对象通信,但是这样一来,用户必须负责管理所调用的企业对象之间的关系,并且能够处理其间的业务流程;然而,如果用户和业务对象之间存在过于直接的交互,两者的联系就会过于紧密,同时也使得用户过于依赖企业对象的具体实现,并负责管理与交互过程有关的业务对象查找和创建,以及不同的对象间相互调用的关系,甚至一些时候用户还需要管理多次调用之间的事务管理环节。

在用户需求不断增加时,这也是应用系统经常发生的情况,用户与不同的企业对象之间的交互也会变得越来越复杂,而企业对象可能需要一定内部的更新才能满足前者的需要,但是这样的话用户又需要根据企业对象实现的变化而做出相应的改变,这种情况将为应用系统带来相当大的麻烦;在访问EJB应用系统时,用户需要与远程对象进行交互。如果用户直接与所有相关的业务对象交互的话,将带来很大的Web负担;因为对于每一个ejb的激活,都将产生一次远程的调用,而如果存在大量的系统用户,用户与对象间的交互就将为Web通信带来很大的压力,使系统性能受到很大破坏;如果用户可以直接访问后端的企业对象,但是系统中又缺少一个统一的用户访问机制,那么这些访问很有可能变得杂乱无章,引起系统性能的下降,甚至导致一些安全问题。

为了解决以上的问题,开发人员可以采用会话面的设计模式,即使用会话bean来实现一个面(facade)来包含一个工作流中所有相关对象的交互;这个会话面负责管理业务对象,并向用户提供一个统一的服务访问层,会话面可以面向底层对象的交互过程,并提供一个仅仅包含必须提供的接口的服务层,由此它将复杂的对象交互和用户之间隔离开来; 会话面也负责管理企业数据和企业对象之间的交互,并表达其中需要的企业逻辑,因此会话面也可以管理企业对象之间的作用关系;同时,根据工作流的需要,会话面也管理对象的创建、查找、修改和删除。

在一个复杂的应用系统中,会话面可以将其生命周期的管理下放到一个单独的帮助对象去,比如说,会话面可以将管理会话和实体bean生命周期的工作交给服务定位对象; 同时,在应用系统中,检查业务对象之间的作用关系也是非常重要的,一些关系可能是暂时的,即只使用于一定的交互过程,而另外一些关系则是永久的,暂时的关系适合建模于会话面中的工作流,永久的关系则需要具体情况具体分析。

图6中的类图简要描述了会话面的设计模式,图7给出了会话面的序列表示,即参与组件及其交互关系。

图6 会话面类图

图7会话面序列图

这里我们对于图7的各个组件加以简要的介绍:

(1)客户(Client)。这表示会话面的客户,即需要访问相关企业服务的客户端应用程序,当然也可以是在同一层面或不同层面的另外一个会话bean。

(2)会话面(Session Facade)。会话面通常是用会话bean来实现的,它管理着多个企业对象的作用关系并提供一个高层次的抽象界面给用户。

(3)业务对象(Business Object)。业务对象是一个可以使用多个不同设计方案的对象,例如会话bean、实体bean和数据访问对象。在图6中业务对象负责提供数据和服务,而会话面则需要与多个业务对象实例交互而获得相应的服务。

会话面实际上就是业务层的一个控制对象,它负责控制用户与企业数据和企业服务对象之间的交互;在一个复杂的应用系统中,甚至可能会有多个会话面作为用户和对象模块之间的中介。

下面介绍两种实现会话面的常见方法。

(1)无状态的会话面

在实现会话面的时候,首先应该决定是用状态化还是无状态的会话bean来实现,这主要取决于会话面所建模的业务流程;如果一个业务流程只需要一次方法调用就可以实现其服务,那么就可以使用无状态的会话bean来实现它。

(2)状态化的会话面

当一个业务流程需要多次方法调用来实现其服务时,开发人员最好使用状态化的会话bean来实现这一流程,因为每次方法调用的状态信息都必须在会话bean中保存。

通过在应用系统中采用会话面的设计模式,将在系统中得到以下的收益:

①为用户提供一个简单的接口,并隐藏所有与系统组件复杂的交互过程;

②减少暴露给用户的企业对象,从而降低它们之间的依赖关系;

③向用户隐藏系统组件间的交互过程和依赖关系,从而使得系统更加容易管理,并提供相当的灵活性;提供一套统一的用户访问机制,便于管理用户对于系统服务的请求与访问。



↑返回目录
前一篇: J2EE体系结构设计(二)
后一篇: J2EE平台WEB组件开发中如何使用定制标签