Java实战之STRUTS第一章 - 图文

更新时间:2024-03-18 12:24:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

第一部分 Struts

第一章 MVC与Struts介绍

1.1 Model 2

在Servlet/JSP规范的0.92版本中描述了Model 2。当时,Model 2定义了如何在同一个应用程序中联合使用servlet和JSP的体系结构。在后面的版本中,Model 2这个术语没有再出现过,但是在Web开发者中还是使用该术语。

在Model2中,servlet处理数据存取以及控制流程,而JSP处理表现层。Model 2使得Java工程师和HTML开发者可以在同一个应用程序中都仅仅关注自己擅长的部分。对于一个符合Model 2要求的应用程序来说,其中一个部分的改变并不应当需要修改另外一个部分。HTML的开发者经常会改变页面的观感,但是这些修改并不影响后台servlet中的业务逻辑。

Struts框架就是建立在Model 2体系结构上的。在Struts中有一个控制器servlet,它负责处理流程,还有一系列的辅助类来负责数据存取工作。Struts提供了很多标签库,使得Struts可以很容易地使用JSP页面。

1.2 Struts概述

不要吃惊,我们现在仅仅是简述一些Struts的基本知识。这仅仅是一次关于Struts的快速介绍。在我们试图深入到Struts框架结构的具体细节前,我们从它的整体框架入手。

Struts使用Model 2体系结构。ActionServlet负责控制流程。另外一个Struts类:Action,负责存取业务逻辑类。当ActionServlet类从容器中获得一个请求时,它使用请求的URI(也称为路径)来决定是哪一个Action来处理该请求。一个Action可以检验输入的合法性、通过业务逻辑层来读取数据库或者其他数据服务中的信息。

要检验输入的合法性或者是使用该输入来来数据库中的内容,Action需要用户提交给它处理数据。为了避免每一个Action都要处理从请求中提取输入数据的操作。Struts将这些输入数据绑定到JavaBean中。每一个这样的输入Bean都是Struts中ActionForm类的子类。ActionServlet通过查看请求的路径就可以决定哪一个ActionForm用来包含输入数据。这些ActionForm类都应该是org.apache.struts.action. ActionForm的子类。

每一个HTTP请求都必须有一个HTTP响应。一般而言,Struts自己并不生成响应,而是将请求转发到其他的资源上去——例如JSP页面。Struts提供一个名为ActionFurward的类来将逻辑名保存到每个页面的路径。当业务逻辑部分已经执行完成后,Action会选择一个ActionForward,并且将它返回给总控servlet。总控servlet会使用在该ActionFurward中存储的物理路径来调用对应的页面来完成HTTP响应部分。

Struts将所有这些方面的细节信息都定义在ActionMapping对象中。每一个ActionMapping对象都对应于一个指定的路径。当该路径被请求时,总控servlet会获取对应的ActionMapping对象。该对象会告诉总控servlet该使用哪些Action、ActionForm、ActionFurward对象。

全部有关Action、ActionForm、ActionFurward以及ActionMapping的信息和其他的配置信息都在Struts-config.xml文件中声明。ActionServlet在启动的时候会读取该文件,并且创建包含所有这些配置对象的数据库。在运行时刻,Struts会使用这些对象,而不是文件本身。图1-1说明了这些组件是如何集成在一起的。

图 1-1 Struts 组件

1.3 Struts体系结构

现在,Web应用对于企业而言是非常重要的。如今,开发小组需要在有限的时间内构造出应用程序,但是,该应用程序也应当是正确的和可以长期运行的。

Java开发者有许多工具来开发表现层页面,这些工具包括JavaServerPage和Velocity模板。我们同时还有和数据库打交道的工具——例如JDBC和EJB。但是,如何将这些部分组合在一起呢?我们已经有了足够的组件了??还需要什么呢?

1.3.1 历史的回顾

在20世纪70年代后期,当图形用户界面刚刚出现时,软件架构师们发现应用程序可以被分成三个主要部分:处理数据的部分,生成屏幕图形和报表的部分以及处理用户和其他子系统之间交互的部分[Ooram]。在20世纪80年代初期,ObjectWorks/Smalltalk程序开发环境将这三层作为框架结构引入。在Smalltalk80中,处理数据的部分被称为模式(Model),表现层被称为视图(View),交互层被称为控制(Controller)。许多现代的开发环境,包括Java的Swing都使用Model/View/Controler(MVC)体系结构(参看图1-2)作为自己体系结构的基础。

图1-2 Model/View/Controler(MVC)体系结构

基于Java的Web开发人员可以使用JDBC和JSP这样的工具来处理模式层和视图层,但是如何处理控制层呢?

1.3.2 Struts介绍

Struts的中心部分是MVC中的控制层。Struts的控制层将模式层和视图层连接起来。该框架结构还包括一些其他功能,程序员可以利用这些功能来创建一个可伸缩的,足够酷的Web应用。Struts被称为是“不可见的组件”的集合,它帮助程序员将那些原始的素材,例如数据库和Web页面组合成一个真正的实际应用系统。

1.3.3 Struts控制层

Struts的控制层是一组可被编程的组件,程序员可以通过它们来定义自己的应用程序如何和用户打交道。这些组件可以通过逻辑名来隐藏那些很麻烦的,令人讨厌的细枝末节问题。开发人员可以通过配置文件来一次性地处理这些问题,然后就可以关注程序“干什么”而不是“如何干”。 用户通过超链接或者HTML表来和Web应用进行交互。超链接可以将用户导向显示数据和其他元素的页面,例如文本和图像的页面。而HTML表单则可以将用户数据提交给应用程序。

正如图1-3所示,Struts提供了可以用来定义超链接,表单和用户动作的组件。

Hyperlinks/ ActionForwards HTML forms/ ActionForms Custom actions/ Action classes 图1-3 主要的Struts组件

注意 Struts的组件是通过XML文件来进行配置。实际上,配置文件也是Struts框架结构的一个不可分割的组成部分。为了使得读者可以有一个全局的观念,我们将随着对每一个组件的介绍而介绍它们对应的XML元素。

1.超链接(Hyperlinks)

对于应用程序开发者而言,超链接是指向应用程序的某些资源。这些资源可能是Web页面或者是一个客户动作。同时,它也可能包含一些指定的参数。在Struts中,开发人员可以将一个超链接定义为一个ActionForward对象。这些对象同时还具有一个逻辑名和“Path”的属性。这使得开发人员可以设置Path属性,然后通过名字在ActionForward中使用它们。

ActionForward通常是定义在一个XML配置文件中,该文件是在Web应用启动时被Struts读取的。Struts使用这个XML定义文件来创建Struts的配置信息,其中就包含了一系列ActionForward对象。用来创建对应于起始页面的超链接的ActionForward对象的XML元素可能是如下定义:

这个元素将会创建一个“name”属性的属性值为“welcome”,而“path”属性的属性值为“/pages/index.jsp”的ActionForward对象。

JSP页面和其他组件都可以使用“welcome”这个对象。Struts框架结构会找到逻辑名为“welcome”的ActionForward,并且根据其中的path属性来转跳到相应的页面。这使得开发人员可以改变链接的目标页面而不需要改变使用该链接的那些组件。在大多数的Web应用程序中,一般都是在JSP和Java代码中对于目的页面的地址进行硬编码,这样经常会导致程序难于修改以及潜在的错误。在Struts应用程序中,这样的细节配置都可以在不需要改变页面或者Java类文件的情况下,在整个应用程序中被改变。

2.HTML表单

HTTP和HTML协议都提供了通过表单来提交数据的机制,但是如何处理这些数据就是程 序员的事情了。Struts框架结构提供了AetionForm类,它可以处理一个HTML表单中的输入数据,校验输入数据的合法性,在需要时通过提供给用户对应的提示或者消息来纠正错误的输入。

ActionForm具有一系列标准的方法来对数据进行校验和修改。Struts会自动地将JavaBean的属性和HTML表单的属性进行匹配。开发者定义ActionForm类,而Struts会自动处理其他的事情。

下面的类会自动地从HTML表单的元素中提取具有“username”名字的域,并且将其值赋给自己的username属性。

Public final class LogonForm extends ActionForm{ Private String username=null; Public String getUsername() { return(this.username);

} public void setUsername(String username) { this.Username=username; } } 对于表单中的其他域,也可以增加相应的属性。这使得其他的组件可以通过一个标准的JavaBean来获取需要的值。从而避免每一个组件都要和HTTP请求打交道。

ActionForm类是使用标准的Java类来创建的。Struts配置通过一系列的配置来查找ActionForm类。这些配置选项包括。Struts框架结构使用是来定位和实例化AcfionForm对象。例如:

Struts配置文件中包含了全部可用的ActionForm对象,其他组件只使用其对应的逻辑名就可以了。

3.定制action参数

一个HTML表单可以使用其action参数来通知浏览器向何处来提交数据。Struts框架结构也提供了相对应的Action对象来获取这个数据。框架结构会自动地创建、填充、校验并且将最后恰当的ActionForm对象传递给Action。Action对象然后就可以直接从ActionForm中提取数据。下面就是一个例子: public final class LogonAction extends Action { public ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) Throws IOException,ServletException { MyForm myForm=(MyForm) form; //其他操作 return mapping.findForward(“Continue”); } } Action最后将ActionForward对象返回给控制器。这使得Action可以通过例如continue或者cancel这样的逻辑名,而不是实际的系统路径名来定义目的页面。

为了保证可控制性,控制层同时还会传递当前的HTTP请求和响应。实际上,一个Action 对象可以做Java的Servlet能够做到的任何事情。

除了ActionForward、ActionForm和Action对象外,Struts控制层提供好些其他组件,例如ActionMapping和ActionServlet对象。Struts还在控制层中就应用程序的本地化提供支持。

● ActionMapping

在一个Web应用程序中,每个资源都必须通过统一资源标识符(URl)来引用。这包括HTML页面,JSP页面和其他动作。为了给予custom Action一个URl或者路径,Struts框架结 构提供了一个ActionMapping对象。类似于ActionForward或者ActionForm对象,该对象也是定义在一个XML格式的配置文件中。 </action-mappings> 这使得同一个Action对象可以在不同的映射中被定义。例如,一个映射可能要求进行验证,而另外一个就可能不这么要求。

● ActionServlet

Struts的ActionServlet在幕后安静地工作,它可以将其他的组件联合在一起。尽管它可以被子类化,大多数Strutsl.0开发者将ActionServlet看成是一个黑盒:他们配置它然后就不管它了。我们会在第4章详细介绍Struts的配置。

在Strutsl.1中,ActionServlet就相对容易地控制了。 ● 本地化

Web应用程序通过提示和消息与用户进行交互。Struts组件中就内置了本地化的特性, 因此Struts应用程序可以被编写成一个国际化的程序。 1.3.4 使用Struts开发Web应用程序

为了使用Struts开发一个Web应用程序,开发者要把那些需要的超链接作为ActionForward对象,将那些HTML表单看作ActionForm对象,以及把那些客户定义的服务器端动作看作Action对象。

那些需要存取EJB或者JDBC数据库的开发者可以用Action对象实现这些功能。这样使得表现层页面不需要和模式层打交道。

Struts中的Action对象会收集视图需要的所有数据,然后转发给表现层页面。Struts提供了一个JSP标签库来简化编写HTML表单以及访问Action转发其他数据的工作。其他表现层的工具,例如Velocity模板也可以存取Struts框架结构,来生成动态Web页面。该过程如图1-5所示。 JavaServer page JSP标签 Action对象 为显示进行 收集数据 数据格式化 图1-4将数据返回给视图层的过程

在更深入地探讨Struts体系结构前,先让我们看看Web应用程序框架在设计时要面对的问题。

1.4 为什么需要框架结构

我们已经介绍了应用程序框架结构并且简单地讨论了框架结构的重要性。但是要真正地认识一个解决方案,你需要理解它解决的问题。开发一个Web应用是有其自身挑战的。因此要先了解那些开发Web应用面对的挑战。 1.4.1 Web——麻烦的源头

Web开发者受到Web带来的两重限制:第一,我们希望客户端使用Web浏览器。其次,我们必须使用Web协议来通信。

Web浏览器使用HTTP协议来通信,并且显示由HTML语言编写的页面。Web浏览器发送HTTP请求,而且处理接受到的HTML页面。对于那些不经常被改变的内容而言,这样的平台是很不错的。但是我们中的大多数人都需要编写动态的应用程序来给不同的用户提供定制化页面。尽管现在有一些有用的方法来编写动态效果,Web应用还是局限于HTTP/HTML范围中。

正如表1-1所示,Web协议和Web客户所带来的限制决定了如何编写Web应用。

表1-1 Web应用面临的困难以及Web框架结构的解决方法

限 制 HTTP协议

结 果

在默认状态下,HTTP可以接受从网络上来的任何客户的连接请求 HTTP主要用来传送简单的文本。传送二进制的数据要求使用复杂的扩展协议

HTTP是无状态的,因此需要特别的方法来跟踪使用Web应用程序的用户

HTTP是可信的,它期望客户端发回正确的信息

浏览器是处于Web应用程序控制之外的独立应用程序

所有的浏览器都不是完全等价的,并且支持正式标志的不同子集 通过浏览器的输入可能是不正确或者不完整的。有些输入甚至可能对应用程序进行攻击

标准的Web格式语言——HTML不能完全构造出桌面应用程序所能提供的界面元素

应用程序通过默认数据来创建HTML控件

客户端

可悲的是,这种情况并不会立刻改变。Web开发者不得不面对这些缺点,并且想办法来解决这些问题。正是因为编写一个具有鲁棒性的Web应用不得不面对这么多困难,因此使用 框架结构来编写Web应用是至关重要的,它可以使你免于陷入无穷无尽的工作和麻烦中。 在开发一个Web应用时,我们面对巨大的挑战。但是这些挑战也会带来巨大的收益。HTTP协议和HTML客户端使得Web应用程序可以被世界各地用户访问。从来没有其他的平台可以实现这一点。

1.4.2 servlet解决方案

Java的Servlet平台[Sun,JST]作为一个基本的平台来为基于Java的Web应用程序提供了很多重要的功能。Servlet类提供了基本的接口来处理HTTP的请求和响应。在HTTP协议的基础上,servlet提供了session的上下文,应用程序可以使用该上下文来跟踪用户。应用程序也可以使用servlet提供的其他上下文来将数据传递给浏览器或者应用程序中的其他servlet。Web应用程序还可以通过使用统一的方法来使用Java服务器提供的基本安全特性,而其他的HTTP服务器使用不同的方法来处理安全问题。

总而言之,servlet规范描述了一个“容器”来管理servlet。该容器可能还提供了其他服务,例如对JSP的处理服务。servlet容器可以将其包括在自己的Web服务器中或者仅仅是将其作为现有的web服务器的一个插件。

对于操作数据库,Java应用程序通过另外一个通用的框架结构:JDBC来进行处理。开发者使用标准的SQL语句并由插入的接口来处理那些和数据库打交道的细节。这使得在不改变源程序的情况下很容易改变后台数据库。

对于那些使用后台远端数据库的高性能应用程序而言,开发者可以使用EJB平台。大多数的Java框架结构(包括Struts)在需要时候都可以使用EJB。

一般而言,这些特性使得那些使用servlet技术的Web应用程序具有可移植性并简化了开发和维护工作。servlet和JSP已经提供了一个完全不同的方法来编写应用程序。类似于Struts这样的用Java语言实现的框架结构都是建立在servlet平台基础上的。它们主要是向程序员提供一个无缝的,简单的编程平台。 1.4.3 Servlet框架结构

如果不是全部的话,那么大多数Java语言编写的框架结构都使用Sun的Servlet平台作为基础。这些框架结构都捆绑了一个或者多个预先写好的servlet来供编程人员在自己的应用程序中使用。同时,它们还提供了一套类库,在自己的应用程序中,编程人员可以实现相应的接口或者继承其中的类来使用框架结构的功能或者实现自己的定制化。

一般而言,Web应用程序的框架结构的主要目的是帮助你从浏览器中获取数据,同时将这些数据传递给自己的应用程序,这样应用程序就可以处理这些数据。最后,它们还负责将数据从应用程序的数据结构中取出传递给浏览器,使得用户可以通过浏览器看到结果。

一些框架结构(例如Turbine[ASF,Turbine]),同时还提供了辅助类和JDBC打交道。另外一些框架结构(例如Struts),是和模式层无关的。它们既不妨碍和数据库打交道也不提供辅助函数。但是还是有一些其他的框架结构(例如doForms[dbForms]),专注于数据库的存取,而将其他的工作留给程序员来解决(或者是其他的框架结构,例如Struts)。

框架结构的常用策略

图 1-5 通常使用配置文件,控制器和表现系统的框架

正如在图1-5中所示的那样,Java语言编写的Web框架结构都使用一些通用技术来保证产品更加容易设计、编写和维护。这些技术包括:

外部配置文件。提供那些程序员不愿意在其源程序中包含的实现细节。

中心控制器。作为一个通道来将HTYP请求放入更容易管理的队列中。该设计有时候被称为前沿控制器(Front Controller)[G03]。

外部表现层系统。使得不同角色人员在编写应用程序的时候,可以在同一时刻并行工作。例如,Java程序员可以编写那些和中心控制器相关的类而页面设计师可以同时使用JSP编写页面。除了JSP以外,其他和表现层相关的系统,例如Volocity模板或者XSLT,都可以和Struts协调工作。

框架结构一般都由多个组件组成,但是它们都具有上面这些特点。这些常用的策略在《设

计模式:可复用面向对象软件的基础》①[Go4]和《J2EE核心模式》②

[Go3]中是作为基本的程序范例进行说明的。许多程序员很乐于讨论和使用这些设计模式,但是还没有真正在Web环境中使用这些模式。

使用这些已经被证明有效的设计模式例如MVC来构造应用程序可以更容易地保证自己

的应用程序设计的正确性。在开发桌面应用程序时,使用这些设计模式带来的好处是显而易见的,但是如何在Web环境中使用这些模式还不为大多数程序员所了解。 1.4.4 黑盒和白盒系列

框架结构有些时候被分类为黑盒和白盒[Fayad]。“白盒”框架结构依赖于面向对象语言提供的继承和动态绑定特性。“黑盒”框架结构趋向于为可插入组件定义接口,并基于这些接口提供基本起步组件。这些接口和基本组件通常会提供热点(hotspot)方法,程序员可以直接使用或者重载这些热点方法来提供特别的行为。

定义hotspot方法,也被称为变形(flexible)点或者是扩展(extension)点。这些方法处于那些可以被加入定制化代码的地方。热点(或者热点系统)描述了框架结构可以支持的应用程序的各种特性。本质上说,热点系统表现了框架结构能够解决的问题。大多数的具有面向对象特性的框架结构都是由一个核心子系统和一个热点子系统组成的。

和其他框架结构一样,Struts也是混合使用白盒和黑盒技术。但是总而言之,框架结构应该更加倾向于黑盒。

“黑盒”框架结构一般是更加依赖于设计模式。Struts当然也不例外。实际上,设计模式经常用来提供对框架结构的高层次描述[Johnson]。顺着这个思路,我们下面要介绍一下设计模式以及如何在Struts框架结构中使用这些设计模式。

1.5 Struts、Model2和MVC

Struts是这样说明自己:

??一个MVC(模式—视图—控制)设计模式的变异,基于Model2方法,封装了应用程序的特性结构。

这样的说明可以使得一些Web设计人员很放心,但是对于另外一些不了解Model2或者MVC的人来说,却感到困惑。没有对MVC或者Model2的深入了解作为基础,的确是很难理解上面关于Struts的说明。 1.5.1 MVC的演变

我们说过Model/View/Controller原来是作为构造Smalltalk应用程序的框架结构。该框架结构支持三层结构的类:表现应用程序状态层、屏幕表现层和控制流层。这三层被称为模式(Model)、视图(View)和控制(Controller)。见图1-6。

图 1-6 Smalltalk的MVC三层结构

Smalltalk的MVC框架结构在著名的《设计模式:可复用面向对象软件的基础》(Design Patterns:Elements of Reusable Object-Oriented Software[Co4])中作为一个例子进行

了研究。该书的四位主要作者逐渐被人称为“Gang of Four”。关于设计模式的更多细节问题。

在设计模式一书中的MVC例子,着重说明了如何在其中使用通知/定购协议以及观察模式。当时的例子是对于同样的数据如何用不同的方法进行表现,例如使用饼图、条状图和电子表格。它是一个对应用程序进行模块化的极好例子,所以直到现在,也经常被使用。

在图1-7的例子中,不同的视图可以在同一个时刻以不同的格式显示给不同的用户。应用程序一定要在后台数据(也被称为模式)改变时更新视图。为了更新数据,用户向控制器提交请求,而控制器会协调对数据据的改变。而视图必须同时进行更新来瓜数据的最新状态。

图 1-7 模型数据可以被用于不同的视图中

Smalltalk的MVC解决方案使用了“观察/通知”设计模式。在该模式中,每一个视图需要把自己注册为数据的观察者。而当数据改变时,每一个注册的观察者都会收到一个通知消息。

将Smalltalk的MVC框架结构归纳成为Model-View-Controller体系结构,可以使用该体系结构在任何平台上构造应用程序。 1.5.2 Model2的产生

JSP的出现简化了动态页面的编写。JSP最初是作为servlet的替代品,同时也是微软ASP的竞争者。这样,程序员可以很轻易地创建具有servlet能力的服务器端页面。

但是,更大的能力也意味着更大的责任。许多开发小组都发现,如果他们一不小心,则不同页面间的相互关联关系会带来的无穷维护工作,最后导致项目崩溃。那些高级特性都要求在页面中使用很复杂的嵌入脚本。但是这些嵌入脚本是很难被重用的——除非将代码在不同页面间进行拷贝和粘贴。虽然页面中也可以包含辅助页面,但是这些辅助页面很难进行组织,也不容易在源代码树上进行组织。有时候甚至在源代码树中显得格格不入。

许多程序员立刻意识到JSP和servlet应当联合使用来开发Web应用。servlet用来应付处理流;JSP应当专注于那些和输出HTML相关的繁琐工作。换句话说,使用JSP和servlet就被称为Model2(仅仅使用JSP就被称为Model1)。

当然,对于Sun而言,这没有什么新鲜的。许多人迅速地指出Model2类似于经典的MVC体系结构。虽然有争论说使用Model2的应用程序仅仅是MVC构架,但并不支持“观察者/通知”模式,但是在大多数情况下,Model2和MVC就是一个意思。不具有“通知”模式的

MVC模式也有时候被称为MVC2或者Web MVC。 1.5.3 应用程序层——独立的视图层

另外一个使得Model2不同于MVC的原因就是在Web环境下“观察者/通知”模式并不合适。HTTP是一个“拉”的协议:客户端发出请求而服务器进行响应。如果没有请求,也不会有响应。而“观察者”模式则需要一个“推”协议来发出通知:服务器可以在数据改变时向客户端推出一个消息。尽管有模拟向Web客户端推数据的方法,但是它们并不符合Web应用程序的本质而且很难处理。

图1-8描述了通常情况下MVC的例子:三个互相交互的组件构成了一个三角形。对于一个基于Web的应用程序而言,是很难维护该三角形的“改变通知”这一部分。对于那些全部资源在同一个服务器上并且客户端也保持着和服务器的连接的应用程序,这样的设计模式工作很好。但是对于那些资源是分布在好多服务器上,并且客户并不维护和服务器的连接的应用程序而言,这样的设计就不合适了。

图 1-8 MVC 通常表示为3 个互相连接的组件

包括基于Web的应用程序在内,许多分布式系统都不能够使视图对应用程序状态进行查询。因此更普遍的情况是分布式的应用程序进行分层设计[Layers pattern POSA]。简单而

言,分层的思想就是将在同一层或者是临近层的类进行交互。在复杂系统中,这种设计可以保证臣系统的复杂性不会随着模块的增长而成指数增长。对于分布式应用程序而言,“分层”模式是设计中的核心模式。

从MVC的上下文,在中心控制器以及改变通知上引入分层思想要对状态改变和状态查询责任。

正如图1-10所示,分层的Web应用程序是使用平面设计而不是传统的MVC设计方式。控制层处于表现层(视图)和应用程序逻辑(模式)之间。

应用程序表现层 控制层 逻辑 数据库 后端资源 图1-19 Web应用层次

每一个模块的主要功能都没有改变。对于流程的一点改变就是状态查询以及改变通知都必须通过控制器。另外一个改变就是当视图(表现层)生成动态内容时,它使用由控制器提供的数据而不是直接由模式提供的数据。这个改变使得视图更加和模式无关,从而使得由控制器来选择数据并显示数据的视图。

1.5.4 Struts如何实现Model2、MVC和分层

Struts通过提供一个控制servlet来实现了Sun的Model2。该servlet可以被用来控制JSP页面和其他表现层设备之间的流程。通过使用ActionForward和ActionMapping来保证控制使用的逻辑名和表现层无关。Struts实现了MVC和分层模式。JSP页面可以用逻辑名进行引用。而控制器组件在运行时刻提供实际的URL。

表1-2显示了Struts的核心类和经典MVC模式中的功能的对应关系。

表1-2 核心Struts类以及MVC的关系

类 ActionForward ActionForm ActionMapping ActionServlet Action classes 用户指示器或者视图选择器 要求改变状态的数据 要求改变状态的事件 控制器的一部分,负责接受用户指示以及状态改变,还负责选择下个视图进行显示 控制器的一部分,负责和模式打交道。通过模式改变状态数据,同时告知ActionServlet选择哪一个视图显示 描 述 除了这些核心类以外,Struts使用很多的配置文件和视图辅助函数来解决控制器和模式之向的差距。表1-3显示了Struts配置文件和它们在体系结构中相应的功能描述。

表1-3 Struts配置文件

文 件 目 的 ApplicationResources.properties 存储本地化的消息和标签,应用程序可以通过 使用它们实现国际化。 Struts-config.xml 存储控制器对象的默认配置信息。包括模式支持的用户指示、状态改变和状态查询 为了将数据从Struts配置文件中表现在视图中,Struts框架结构提供了一系列的辅助函数作为JSP的标签使用,我们在表1-4中列出它们。

表1-4 Struts视图帮助函数

标签库描述符 Struts-html.tld Struts-bean.tld Struts-logic.tld 目 的 对于HTML表进行扩展的JSP标签 对于JavaBean进行扩展的JSP标签 对于属性值进行测试的JSP标签 将这些合在一起考虑,表1-5分层说明了Struts操作。

表1-5 分层说明的Struts组件

视 图 层 JSP标签控制 JSP页面,Velocity模板以及由开发者提供的其他表现方法 控制层 ActionForward ActionForm类 ActionMapping ActionServiet Action类 ActionErrors MessageResources 各种各样的辅助函数,例如Commons-Digester和Commons-BeanUtils 模式层 GenericDataSource 开发者提供的其他数据服务和API 要注意的是,根据分层模式的定义,每一个模块都仅仅可以和与它同层或者相邻层的模块进行交互。也就是说,处于模式层的模块不可以直接和视图层的模块打交道。

实际上,控制器和视图通过servlet平台提供的request对象、session对象和应用程序上下文进行交互。为了创建一个和JDBC数据库的连接,控制器和模式通过文件和内存(在加载XML文件和属性文件的情况下)或者其他服务(例如TCP)进行交互。

1.6 Struts控制流

因为Web应用程序是动态的,因此很难用“全能的控制流”来控制。根据环境的不同,不同的事情可能以不同的方式发生(特别是Web应用程序)。但是它们还是有一个大致的顺序。

如果你是Struts的,或者是应用程序框架结构的,甚至是Web应用程序的新手,该流程可能第一次很难看清楚,因此也就不容易看出Struts在尽力解决的问题。在本书中,我们会详细地解说这些问题。但是,此时我们在介绍每一个细节前,先看看整个流程的概况。当你阅读本书时,我们建议你经常回来看看本节,从而了解每一个细节问题是如何组成整个框架结构的。 1.6.1 流程概述

图1-10通过可视的序列方式说明了Struts的请求——响应处理流程。让我们顺着该流程走一遍。在括号中的数字就是指代图1-10中的动作。

图1-10 Struts的请求——响应处理流程(UML图由Jean-Michel Garnier) ·客户请求和Action对应的URl模式匹配的地址(1)。 ·容器将该请求传递给ActionServlet。

·如果该应用程序是一个模式化应用程序,则ActionServlet选择恰当的模块。 ·ActionServlet查看该路径对应的值。

·如果该路径对应的值指向一个form bean,则ActionServlet检查是否已经创建一个bean。

·如果已经存在一个bean,则ActionServlet重置该bean,并且用HTTP请求中的数据来填充该bean。

·如果该路径对应的值已经将validate属性设置为真,则调用form bean对应的validate方法。

·如果validate方法失败,则servlet将控制转向input属性指定的路径,控制流结束。

·如果该值指定了一个Action类型,如果该Action已经存在,则重用它。否则就重新创建一个。

·该Action对应的perform或者execute方法被调用,同时会被传递给实例化的from bean(或者空对象)。

·Action对象可能会生成form bean,调用具有业务逻辑的对象以及做任何其他需要做的事情。

·Action返回一个ActionForward对象给ActionServlet。

·如果ActionForward对象是另外一个Action对应的URI,则我们从头开始一次。否则,显示页面和其他资源。在大多数情况下,是显示JSP页面。在使用Jasper或者其他等价物(而不是Struts)时,生成页面(2.3)。

·如果JSP页面使用了Struts提供的HTML标签,并且这些标签在HTTP请求中发现了合适的ActionForm,则它们从ActionForm中的数据来生成自己的控制。否则,标签创建一个对应的ActionForm。从Struts1.1开始,如果ActionForm自己要创建一个

对象,则该标签同时也会调用ActionForm的reset方法。

如果你仅仅需要创建一个空白的form(1.1),则你可以使用一个标准的ForwardAction并且通过Action传递给控制器,然后再传送给页面。 1.6.2 更微小的细节

真正可怕的是细节问题。上一节中的大纲和图表很好地概述了全局的流程,但是也省略了一些重要的细节。让我们更深入地挖掘,并说明更加底层的细节。正是因为我们使用HTTP协议,因此所有的事情都要从HTTP请求开始。

1.容器收到HTTP请求

Struts框架结构的关键组件是ActionServlet。和其他servlet一样,它在类似于Tomcat、Resin或者WebLogic的容器中运行。当这些容器启动时,它读取配置文件(web.xml)。该文件告诉容器装载哪些servlet。

标准的servlet设置是sevlet mapping。容器使用这些配置信息来决定哪些请求会被发送给该servlet。 action</servlet-name> /do/*</url-pattern> </servlet-mapping> 这个配置使得容器使用ActionServlet来处理任何符合模式/do/*的请求。这包括/do/This,或者/do/That和/do/something/Whatever。

许多应用程序愿意使用后缀的方式进行映射:

action *.do 该模式同样和this.do,或者that.do或者/something/whatever.do进行匹配。任何有效的扩展或者前缀都可以使用。而.do仅仅是最常用的选择。当请求对应的路径匹配servlet指定的上下文时,容器会将该请求发送给我们的ActionServlet。而不匹配我们模式的请求就不会发送给ActionServlet。例如,一个匹配.jsp的请求会被发送给容器处理JSP的服务(对于Tomcat或者WebSphere而言就是Jasper)。在你的应用程序中也许还有其他的servlet,它们会处理其他模式。而并不匹配任何模式的请求会被发送给容器对应的默认web服务器。

2.ActionServlet收到请求

当我们的ActionServlet收到请求时,它进行一系列的处理来定位、映射、生成bean和最终处理action。其中的某些步骤仅仅适用于Struts1.1应用程序。

处理多部分请求。如果该请求具有多部分(一个具有MIME附件的表),则servlet会使用一个特别的处理器来包装该请求,从而可以避免在后面的处理中出错。

处理path标志。ActionServlet检查该路径是不是对应应用程序的模块,如果是,则选择合适的模块配置信息[struts1.1]。

处理locale标志。默认的,ActionServlet会检查用户在session中是否存在标准的local对象。如果不存在local对象,则ActionServlet将会创建一个并且将其放人用户的session中。该对象会被用来对表现层进行本地化操作。

处理content和NoCache标志。默认的MIME类型和可选的请求头被加入到响应中。 处理Mapping。AetionServlet检查ActionMappings,将要处理的路径映射到对应的Action对象。如果没有对应的Action对象,则ActionServlet会将请求转发给默认的(或

者是未知的)Action(如果设定了该Action),或者生成“不正确请求”的错误返回。如果找到对应的Action,则将Action放入请求中来进行进一步的处理。

处理Roles标志。AetionServlet检查用户是否有权限来访问该action。[Struts1.1] 处理ActionForm。AetionServlet检查是否指定了一个ActionForm。如果指定了ActionForm,则servlet会检查在指定的范围内是否存在一个ActionForm(默认的范围是session)。如果不存在ActionForm,则ActionServlet会创建一个。

进行数据填充。ActionForm的reset方法会被调用,然后通过反射机制对其进行自动填充。那些符合ActionForm属性的参数会被填充到ActionForm中去。其他的参数和属性则被忽略掉。

合法性校验。ActionForm的validate方法被调用。如果该方法返回false,则控制会被转移到在ActionMapping中input属性指定的值上,同时Action也不会被处理。

处理Forward或者Include。如果ActionMapping指定了forward或者include属性,控制就会被转移给另外的资源。否则,ActionServlet将请求给Action对象处理。

处理Action。如果ActionMapping指定了Action类型,则ActionServlet会检查是否已经实例化了一个该类。如果没有找到实例化的类,则会创建一个Action对象。对于每一个Action类而言,仅仅存在一个实例(Singleton模式)。该实例会通过多线程来处理全部的请求。Servlet会调用该Action类的perform或者execute方法,同时会将相应的请求、响应、ActionMapping以及任意的formbean传递进去。

Action可以进行任意需要的操作,这些操作包括: ·访问数据系统,例如JDBC数据库。

·在HTTP请求中创建要被视图使用的对象。 ·如果需要,在用户的session中创建对象。

·如果需要,修改用户session中的对象,例如用户的locale。 ·执行应用程序需要的任意其他业务逻辑。 ·处理异常或者任意其他的错误情况。

·直接发送HTTP响应,或者(也是更常见)返回一个ActionForward对象给servlet。 其中某些动作,例如访问数据库,经常是由Action调用的业务对象进行处理(业务代理模式)。Action是用来处理那些和Web相关的任务,但是任何和业务逻辑相关的代码都应该放到业务对象中去。Action是控制器对象,因此应当用来处理应用程序的核心业务逻辑。

3.Action返回ActionForward 当Action完成其工作后,它返回一个ActionForward对象。如果ActionForward为空,则ActionServlet就假定响应已经生成了而不会做进一步的处理。否则,ActionServlet会读取ActionForward对象,然后将HTTP请求进行重定向或者转发给恰当的资源。

4.Jasper(或者等价者)生成JSP页面

当ActionServlet将请求发送给JSP时,该请求会被另外一个服务例如Jasper,进行处理。典型地,程序员使用Struts或者其他标签库来生成页面中的动态部分。有些时候,也使用JSP模板来使用其他模块构造页面。

更普遍的方法是将要传递给页面的动态内容保存在请求对象上下文的某个JavaBean中。这也被称为视图辅助模式[G03]。标签库只要简单地调用JavaBean中的方法就获得被格式化的数据。如何在页面上放置这些数据是表现层的一个艰巨任务。对数据进行格式化工作一般是业务逻辑的一部分,因此由bean代理进行。

Struts提供的标签也可以使用框架结构提供的视图辅助类。这包括本地化标签和提示,错误信息和超链接路径。另外,Struts标签也可以计算表达式,在列表中进行遍历以及将控制填充到HTML表中。

5.其他servlet产生响应

在处理完Action后,HTTP请求也可以被发送给应用程序中的其他servlet或者服务。其他表现层系统,例如Velocity模板,也可以通过servlet上下文来访问Struts框架结构的资源。

1.6.3 Struts的性能

在详细讨论了Struts处理过程后,你可能想知道Struts要多长时间才能完成这些操作。通常而言,对于大多数正确设计的Web应用程序,Struts可以提高其性能。在本章中,我们要研究一些特定的可以提高框架结构性能的设计方法。

定义Performant起源于法语,意思是有能力的。软件工程师通常用它来描述一个过程或设备在实际中运行良好。

Struts不仅是线程安全的,而且是依赖线程的。Struts使用轻量级的Action对象,而不是单独的servlet来处理HTTP请求。Struts对于每一个Action类仅仅实例化一次,该实例会生成多个线程来处理后面的请求。这个基本策略不仅仅保证了使用少量的资源,而且保证最好的吞吐量。一个设计正确的应用程序就可以通过使用一个Action对象派发相关操作来利用Struts的这个特点。

ActionForm类减少了子类以及子类层次。在Struts框架结构中,一个关键点就是可以自动地填充来自任何HTTP请求任意的ActionForm。如果没有这个组件,则对每一个bean都要编写很多代码来填充其中的字段。反射机制的恰当使用使Struts节省了很多资源。

Struts标签库提供了通用功能。Struts提供的bean和逻辑标签库可以满足大多数情况下JSP标签的需求。这减少甚至避免编写其他标签的工作。JSP规范中包含了如何在JSP中复用标签。在JSP中三次使用同一个通用的标签比使用三个不同的标签性能要好得多。

Struts模块可以被应用程序重用。大多数应用程序都会使用Struts提供的那些工具类。BeanUtil.populate方法就是一个极好的例子。尽管该方法是填充来自HTTP请求的formbean,但是它也可以用来填充来自任何类型map的任意bean。通过重用组件可以降低管理费用以及资源开销。

Struts的本地化策略降低了重复的JSP文件。通过在运行时获取本地化消息,应用程序就可以通过一个JSP文件来提供国际化功能。否则可能就要对每一种语言编写一个JSP文件。同时,同一个消息系统也被用来处理错误信息,使得同一个对象可以被使用两次。

Struts具有开放的体系结构。应用程序可以对Struts的模块进行子类化从而提供附加功能。这使得程序员可以通过继承已有的类来提供功能,而避免了创建和集成新类的工作。同时,Struts还和应用程序共享其资源。这使得程序员可以充分利用已有的组件,而不需要再重新编写和实例化。

Struts是轻量级的。同样的框架结构可能会提供几百个类、标签以及几十个包。而Struts核心仅仅由五个标签库和五个核心包组成。

Struts与标准兼容。许多容器在使用标准的组件时的运行效率最好。JSP和JavaBean就是其中的例子。

Struts是开源软件,同时具有良好的文档。这意味着开发人员可以很容易地通过研究其代码来定位以及解决任何潜在的问题。

Struts和模式无关。因为Struts并不对后台的模式做任何假设,因此一个应用程序可以用最有效率的方法来实现其模式层。Struts的Action类可以调用许多辅助函数来访问需要的数据。一旦该数据被获取,Struts就将其保存在JavaBean中,这使得Struts可以在很容易地保存数据的同时还减少了调用模式层的次数。

1.7 Struts的强项和弱点

类似任何复杂的系统,Struts也是一个整体。它因此也有自己的优点和弱点。其中一些是我个人的观点,但也希望它是有启发的。

1.7.1 Struts的缺点

尽管喜欢Struts,但是指出Struts框架结构到底是什么以及其缺点也是很重要的。在Struts1.1中已经解决了其中一些问题。表1-6列出了Struts1.1解决的1.0的问题。如果你原来研究过Struts,则你会为这些提高而喝彩。尽管Struts并没有好到你期望的那样。表1—7列出了Strutsl.1的缺点。

在下一节中,你可能会发现这些缺点又出现了,而且是作为优点进行说明。总而言之,是好还是坏就看你的需要和选择了。

表1-6 版本1.1解决的版本1.0中的缺点 缺点 日志(Logging) 注 释 Struts使用容器提供的默认日志系统而没有提供自己的日志功能给应用程序(版本1.1实现了Commons-Logging接口) 大项目需要使用多个配置文件,这样可以避免整个组都共享一个文件(版本1.1支持多个配置文件) 大项目需要使用多个资源文件,这样可以避免整个组都共享一个文件(版本1.1支持多个资源文件) ActionServlet必须被子类化来提供附加的功能,例如日志和认证(版本1.1提供好几个控制点和模块来满足这个需要) Strutsl.0期望程序员创建定制化的JavaBean来处理HTML的输入表格(版本1.1支持Maps和DynaBeans来替代定制化的JavaBean) 表1-7 Struts1.1的缺点 缺 点 没有事件机制 调试 投有默认的数据模式和推荐数据模式 唯一的ActionServlet 要求理解Struts模块 没有技术支持 邮件列表非常丰富 正式版本发布很慢 注 释 Struts仅仅适用于HTTP的请求一响应机制。这限制了程序员使用低粒度的事件 Struts并不支持自动调试。程序员不得不通过向容器的日志或者标准输出中写入信息来手工创建检查点 访问具有持久性能数据模式的工作都留给程序员去完成 一个应用程序中仅仅有一个ActionServlet。这可能导致配置冲突 要使用Struts,程序员必须理解一些特定的类以及它们如何进行交互工作的 Apache软件基金会的软件都是由志愿者开发的。因此问题不能保证会得到响应 Struts的邮件列表非常大。这使得在其中寻找合适的建议非常困难 相对其他项目而言,Struts的版本发布的很慢。程序员不得不经常通过使用“每日发布”的版本来获取最新的版本。对于什么时候发布下一个版本,并没有确定的时间表 每一个应用程序使用一个配置文件 每一个地区仅仅支持一个资源文件 没有服务管理功能 ActionForm很难变化 国际化支持有限 以JSP为中心 JSP异常的本地化 标签的属性太复杂 perform方法和execute方法的特征签名 尽管Struts的消息资源方式可以很好地支持标签和错误信息的国际化。但是不能提供对于大量文字的国际化支持 尽管使用MVC体系结构使得其资源可以被任何表现层系统使用,但是Struts一直以来还是倾向于使用JSP 许多系统级的错误信息,例如JSP的异常,都不能被本地化,是以英语方式显示的 Struts提供的标签库有很多参数,因此使用起来比较笨拙 Struts体系结构的一个关键点就是将请求交给Action类或者分派者进行处理。而Struts支持的唯一分派者就是Action类。Struts通过调用其perform方法来处理请求。这就限制了应用程序只能使用传递给perform方法的数据。尽管有方法来绕过这个限制,但是perform方法的签名终究是体系结构的一个瓶颈。一个最常见的需求就是要求ActionServlet可以填充多个ActionForm。但是因为perform方法仅仅接受一个ActionForm对象作为参数,这使得在不对整个体系结构进行大的修改情况下该请求是无法实现的 Strutsl.1增加了execute方法,但是该方法仅仅解决了perform方法的另外一个问题:perform抛出的异常信息。而其他的流程还是一样 Struts是逐渐发展起来的。一些应用程序选项的命名和某些类的类名会让使用者觉得糊涂。例如,在web.xml中的“validate”选项和Action的validate方法一点关系都没有,而是指示如果解释该配置文件。同样,“null”选项是说明在一个消息的值没有找到时是否返回一个错误消息 这类层次结构中,一般趋向于在类名中包含组件名。在Action包中的每一个类都具有“Action”的前缀,这既多余又容易使人糊涂。同时,在Struts的配置中,ActionMapping定义的元素名是“Action”而不是“ActionMapping”。如果程序员引用一个“action”,真的很难分辨是要引用一个Action类还是配置该类的AetionMapping 在Struts配置文件中,是使用name域来定义ActionForward和ActionForm。Path域来定义ActionMappings。在action-mapping元素中的name域是用来定义使用哪个ActionForm的。在ActionForward中的URl也被命名为path,但同时也可以包含一个查询组件。指向ActionMapping的path并不包含类似于.do这样的servlet模式,而ActionForward的path域则要包含.do这样的扩展。应用程序资源(ApplicationResources)实际上是消息资源(MessageResources)。这样的例子还很多。尽管不是很严重的错误,但是这些不一致的地方还是会使得那些初学者感到困惑,也增加了学习该框架结构的难度 不恰当的命名 1.7.2 Struts的优点

正如我们已经提到的那样,许多列在表1—8中的Struts的优点,其实就是表1-6和1

—7中所列不足的反面。

表1-8 Struts的优点 优 点 以HTTP为中心 标准日志功能 可选的调试日志功能 模式无关性 在一个中心配置系统中保存实现细节 允许每一个地区使用不同的消息资源 轻量级 开源软件 活跃的开发者社区 良好的产品支持 注 释 Struts是围绕着标准的HTTP的请求——响应模式设计的。很多程序员都熟悉该模式 Struts可以使用容器默认的日志功能,而不需要配置和了解另外的包 在运行时,Struts可以在其日志中记录很多状态信息。这对于调试应用程序很有帮助的 Struts并不假定任何特定的具有持久性功能的存储层 Struts配置系统封装了应用程序或者应用程序模块(Struts1.1)的全部实现细 节。因此可以对这些配置信息进行浏览和管理 负责不同地区语言的翻译者可以工作在不同的消息资源文件上。要增加对一个新地区的支持仅仅需要增加一个消息资源文件 开发者仅仅需要了解Struts为数有限的几个核心类 Apache软件协议提供了完全开源软件。使得使用者可以自由使用 在Struts后面有一个活跃的开发者社区。邮件列表相当活跃。可以联系很多的开发者 Struts有专业的JGuru论坛。Struts邮件列表可以通过至少两个门户网站浏览。而且现在已经有数十本书和文章介绍Struts。同时好几个组织都有StNts的专业研讨会 有超过30名的开发者参与了Stmts1.1的开发。Stmts开发团队现在包含了九个活跃的参与者,他们对struts的源代码负完全的责任 Struts的正式发布版本是经过了长期的测试。因为没有发布时间的限制,所以开发团队可以保证发布高质量的产品 Struts从开始设计就考虑支持本地化 struts致力于百分百地和公开标准兼容,同时这也是struts开发团队的一个标准 除了处理自己的特殊资源以外,Struts还提供一系列具有通用功能的扩展标签库。这些功能可以满足编写JSP的要求,从而不需要在JSP中使用脚本语言 Struts的JavaDoc已经提供了很多细节资料,因此你基本不需要去查阅源代码。同时Struts还提供概述性的使用手册来向程序员介绍Struts框架结构 struts框架结构在其体系结构中实现了程序员们熟悉的好多经典设计模式 所有默认设置都是可以被配置的。Struts的核心类都是可以被重载或者子类化的。开发者可以定制像ActionForm和Action这样的关键类 强大的开发者团队 稳定的发布版本 国际化支持 高度兼容性以及与主流产品兼容 具有完全功能的扩展标签库 具有良好文档的源代码 基于设计模式 扩展性

本文来源:https://www.bwwdw.com/article/3q28.html

Top