摘要
Struts是一个优秀的Java Web开发框架。Struts是Apache项目之一,现在Struts已经在Java开发团体中得到了广泛的支持。在这篇文章中Julien Mercay 和Gilbert Bouzeid将向我们介绍Struts的处理流程、描绘Struts框架,最后提出了Model 2x的设计方法。Model 2x用XML/XSLT替换掉了原来的表现层中的JSP页面。Model 2x通过这种方式更加明晰地划分了业务逻辑层和表现层。 正文 自从Servlet API发布以来,Java开发人员使用了不同的技术来开发Web应用程序。Web开发人员已经认识到了Model 2的优点。Model 2 指的是基于MVC模式的WEB开发框架。Model 2能把应用逻辑层和表现层较好地区分开来。Struts构建在Model 2 之上,它向Java开发人员提供了一个普通的控制器Servlet,还有集中式的资源配置、资源管理以及错误处理等功能。 这篇文章中我们引入了Model 2x。Model 2x可以将逻辑层和表现层更好地分离。我们先介绍Model 1和Model 2,然后讲述一下Struts是如何实现MVC架构的,最后向读者展示一下如何通过XML、XSLT来对现存的模式加以改进。Model 1
理解Model 1是十分重要的,如果不熟悉Model 1将很难理解我们在文章中介绍的其他几个架构。Model 1的基础是JSP文件。JSP从Http请求中取得参数,调用所需的业务逻辑,处理Http对话,然后生成HTML页面。 一个完整的Model 1项目包括一组JSP文件(这些文件大部分都是彼此独立的)、一组所需的Java类和其他组件。一些早期的Web开发技术比如ASP、PHP都使用类似的模式来开发的。 Model 1主要也许是唯一的优势就是简单。Model 1中业务逻辑和显示逻辑混合在JSP页面中没有进行抽象和分离,所以在进行快速和小规模的应用开发时,有非常大的优势的。但用Model 1 开发复杂的项目的开发时,将不可避免地导致项目管理的困难。 Model 2, MVC, and Struts/JSP
图1
图1说明的是MVC架构中的三个部分,以及它们在Struts/JSP中的实现。
控制器(Controller):Struts的最主要的部件就是一个普通的控制器Servlet。控制器是每一个发送到Struts的Http请求的入口点。控制器把所有的请求经解释后分发action。这些action是Struts Action类的子类。由开发人员负责实现它们。控制器也会自动的把Http请求中的参数加入到Form bean中。Action负责实现核心的业务逻辑,比如做EJB调用,通过Java Bean访问模型(model)。在Struts中可以通过定义一个XML文件来描述请求URI、具体业务逻辑处理、代表客户提供的数据的Form组件三者之间的映射关系。控制器的就是通过这个XML文件来定义的。
模型(Model):
Java beans就是模型的代表。这些bean分成3类: 窗体Bean(Form Bean)对象用来包装HTML表单数据,当然也包括通过URL请求传递过来的数据。举例来说,一个登陆页面可能有两个属性(Property)login和password。Form Bean由Struts的ActionForm类扩展而来。 请求Bean(Request Bean)拥有用来生成HTML页面的所需的信息。例如在一个表现银行账户状态的页面中,请求Bean就应该有账户相关信息以及近期的交易记录等等。 会话Bean(Session Bean)拥有同一用户在不同Http请求间共享的对话信息。 视图(View): Struts控制器会把Http请求转发到作为MVC视图的JSP文件。这个JSP文件可以访问窗体Bean、请求Bean、会话Bean,生成结果文档(通常是一个HTML文档),并发送到客户端。Struts提供了四组JSP标志库。 HTML:用来生成HTML标志,特别是用来自模型的数据填写HTML表单。 Bean: 操作Bean。 Logic: 根据Bean值实现逻辑结构。 Template:处理页面模板。 通过Struts标志的使用,你可以避免在视图中使用任何的Java代码。 Struts/JSP 缺点 虽然Struts/JSP较之其他模式有许多优势,但它也存在着一些缺点和不足: 程序员可以把应用逻辑放入JSP。遇到问题时,开发人员可以很快地加以修改,经验表明这实际上是陷阱。程序结构很容易变得复杂和难于管理。 JSP不能很好地支持XML,也就不能保证生成的结果文档(XML或HTML文档)将会是100%“格式良好”(Well-Formed)。 开发人员需要学习如何使用Struts的标志库。事实上要理解这些Struts的标志库,特别是Bean和HTML标志库的确要花费比较长的时间。 你不能在视图中用JSP来实现处理管道(processing pipeline)。只能做些简单的include和forward,这样很明显就会限制了视图的灵活性。例如,对布局计和风格的分离就会比较困难。 对JSP页面的任何修改都会导致JSP的重新编译,这样是非常耗费时间的。 上述问题的解决方法必须要具备以下要求: 限制视图对模式和一些定义明确的上下文环境信息的可见程度,比如项目资源的可见程度就应该受到控制。 强制使用格式良好的XML和HTML 能够对在现存的语言或API起到杠杆作用 降低对视图不同部分分离的难度,比如布局和风格的分离。 缩短开发周期 我们相信我们接下来讨论的这个基于未加修改的Struts和XSLT的轻型框架可以满足上述的要求。我们把这个新的架构叫做Model 2x Model 2x架构概观 Model 2x是 Struts和XSLT结合的产物。Model 2x基在视图部分用XSLT和最后会被串行化成XML文件的Bean替代了原来Struts视图部分的JSP文件,但原封不动地保留Struts的Controller和Model部分。 XSLT定义 作为一种W3C的正式标准,XSLT是用来对XML文档进行转化的一种语言。它是XSL(可扩展样式表语言)的一部分。XPath是用作对XML文档各部分进行定位的语言。在XSL样式中,我们可以利用XPath表达式以一种紧凑而高效的形式选取XML文档的一部分进行处理。 XSL/FO也是XSL的规范的一部分,用来描述显示给读者的页面外观。XSL/FO主要的一个应用就是生成PDF文档。 XSLT和Struts的合成 把Struts和XSLT结合在一起的方法一是在JSP页面中执行XSLT转换。我们可以用标志库来实现这一功能,比如你可以使用Jakarta项目中的一个XSL标志库项目来实现。如果使用这种方式,那么在JSP页面中生成的是应该是XML而不是原来的HTML。借助XSLT样式表,XML转化成HTML或其他格式。然而这种方式需要对Struts本身加以修改。 生成HTML最通常的方式是由Struts的HTML标签库来生成,但这类标志库与XML并不兼容,也就不能和XSLT结合起来使用。当然可以对HTML标志库加以修改让其输出XHTML,这并不困难,但这就要修改现有的Struts 1.0 代码。 此外,这一解决方案需要在四个不同的地方开发:Action 类(控制器)、模型Bean、JSP页面、XSLT样式表(视图)。JSP与标签库的作用也只限于把模式Bean转化成一个XML文档。 第二种方法就是我们提出的Model 2x。这种方法会自动执行这一任务,而且把JSP页面从我们的解决方案中删除了。图2向我们很好地展示了Model 2x设计的核心构成。
图2
我们从图2中可以看出来,Model 2x处理流程的最初部分和Struts的类似。请求被发送到Struts的控制器,然后又被分派给各自的业务逻辑处理单元(Action类的子类)。控制器创建ActionForm对象。请求的参数都保存到这个ActionForm对象中。Action类的子类生成结果Bean(Result Bean),然后把这些Bean交给视图来显示。
Model 2x和Struts处理流程不同的地方在于:Model 2x中用一个XSL Servlet配合XSLT样式表实现了原来在Struts中由JSP实现的视图部分。这个XSL Servlet首先根据Bean和上下文环境生成XML文档,然后调用XSLT进行转换。接下来我们会详细地讲述这个过程。由于我们可以把一个请求提交给任何一个已经在Struts配置文件中注册的URL,所以在这个过程中不用对Struts做任何的修改。 XML文档的生成 把一个对象转换到一个Stream的过程我们称之为串行化。在Java 1.1中引入了java.io.Serializable接口和相关的API。二进制串行化可以把一个Java对象转化到二进制流,在网络上传输或是保存到文件中。相比之下,XML串行化是把一个Java对象树转化到文本型的XML流中。 许多开放源代码的软件包,譬如Castor都可以用来执行XML串行化。在我们文章中提到的Model 2x案例中我们自行设计了一个简单的XML串行化方案。这个方案中假设Bean的所有属性是Java的基本类型或者java.util.Colleciton的子类。 这个方案会递归地对窗体Bean(Form Bean)、请求Bean(Request Bean)和Session bean进行自省,创建一个DOM树。同时,也串行化了资源和Struts配置数据也就是上下文环境信息。图3说明了这个过程图3 XML/XSL 工作流程
XSLT处理
在这个Model 2x案例中,XSLT转换只限于样式表对XML流的转换。为了提高性能XSLServlet会对这个样式表进行了缓存处理。XML流着由Struts的处理流程生成。你可以通过提供连续转化或者使用更高级的配置来改进这个简单的架构。Cocoon中你就可以看到这两种方式的使用。Cocoon框架使用XML和XSLT构建服务器端的应用程序。Cocoon基于管道(Pipeline)的架构使其能够更容易对内容和逻辑的加以分离、与大量不同的数据源交互也很方便。通过XSLT,Cocoon的输出可以与不同的设备兼容,比如HTML、WAP等等。 图3显示的是XSLT的处理流程。下面一节提供了一个将内容和版面设计分离的一个例子。 转化例子 TestForm是一个简单的窗体bean,它只有两个属性:public class TestForm extends ActionForm {
private String testString; private List testList; }
假设testString的值为My Test String,testList的值为One、two、Three,XML串行化代码会生成一下XML片断。在XML文档中的元素名是可以预见的,这样编写样式表的时候会简单一些。
<page name="TestForm">
<request> <TestForm> <testString>My Test String</testString> <testList> <item>One</item> <item>Two</item> <item>Three</item> </testList> </TestForm> </request> </page>
简单的XSLT模板把已经串行化的XML流转换到XHTML片断。
<xsl:template match="page">
<h2>Please enter some text and submit</h2> <br/> <form name="testForm" method="get" action="result"> <input type="text" name="testString" value="{request/TestForm/testString}"/> <br/> <select name="outSelect"> <xsl:for-each select="request/TestForm/testList/item"> <option><xsl:value-of select="."/></option> </xsl:for-each> </select> <br/> <input type="submit" value="Submit"/> </form> <hr/> </xsl:template>
经过转化和HTML串行化,结果应该是如下
<h2>Please enter some text and submit</h2>
<br> <form name="testForm" method="get" action="result"> <input type="text" name="testString" value="My Test String"> <br> <select name="outSelect"> <option>One</option> <option>Two</option> <option>Three</option> </select> <br> <input type="submit" value="Submit"> </form> <hr>
Model 2x主要的优势
这一节我们讨论一下Model 2x较之其他模式的一些优势。 业务逻辑和表现逻辑的分离 XML文件流根据模式(model)和上下文环境而生成,样式表对XML文件流再进行加工。虽然一些XSLT转换器可以支持一些扩展,通过这些扩展你可以在样式表中调用Java或是其他类型的语言。但这些扩展往往缺乏移植性,而且使用起来比较麻烦。这样就不能再把业务逻辑放入到XSL样式表。 标准技术的使用 XPath是一种强大的表达式语言,用来从XML流中抽取出数据。我们可以通过使用<xsl:for-each>、<xsl:if>、<xsl:choose>这类的XSLT元素配合上模板属性值,使用起来比Struts中的HTML、Logic、Bean这些标签库好很多。XSLT使用了W3C(World Wide Web Consortium)标准的语言,不但提供了和Struts标签库类似的功能,而且还有很大的提高。此外XSLT还有一些标签库没有的强大功能,比如XSLT支持函数和递归。 其他需要考虑的问题 这章中我们讨论在Model2x中的如何实现国际化、错误处理、当前的一些限制以及以后可能的对Model 2x的改进。 应用的国际化 本地化水平和目标语言的复杂程度会影响到了页面的布局,还有文本消息和图片。例如,阿拉伯文从右向左书写而旧式的中文从上到下书写。这些语言不但需要文本的翻译,同时还需要完全不同的页面布局。Struts开发人员通常把所有的文本信息和图像资源的链接根据不同的地区保存到一起。根据用户的地区,Struts就会调用适当的资源。 Model 2x通过自动把资源存储到DOM中,然后把他们传递给视图。为了提高资源访问的效率可以对资源的读取和DOM的建立缓存。Model 2x 只是简单地把资源DOM插入到最终的DOM树中。这个最终的DOM树也包括了从Form Bean生成的动态内容。你可以轻松地用XPath在XSLT样式表中访问资源。应用的国际化不再通过Java资源捆绑技术实现,而是通过纯XML技术——根据用户当前的地区动态地切换样式表来实现。 错误处理 验证错误通常指的是在HTML表单中输入的参数出现的错误。通常我们把这类错误和其他类型的错误区分开来。验证错误以外的错误将被认为是较严重的错误。 在Struts中,可以在Form Bean中进行表单的验证,出现错误时会返回ActionError对象。把这些对象存储在请求中,然后串行化到Dom树中。这样样式表就可以很容易地就把这些错误显示到窗体中。同样的,用户可以也可以在Action中识别错误,然后把它们存储到请求中,进一步地串行化,最后用样式表来处理。 样式表会依据错误的类型和错误的内容选择是简单的显示一个错误的消息,还是将先前的页面重新显示,让用户修改数据后再提交。 工作流程 在这个Model 2x架构中,struts-config.xml文件并不能像在原来的Struts架构中那样负责控制工作流程。不过,要说明的是这个问题实际上并不是Model 2x的问题,而是这篇文章我们这个实现方案的问题。你可以在你自己的实现方案中修正这个问题。 输出 Model 2x架构的一个重要的特性就是能够动态改变输出的内容类型以及用户接口的风格。比方说,同样的应用项目可以针对老版本的浏览器生成HTML 3.2代码而同时又为新版本的浏览器生成HTML 4.0的代码。利用XSLT可以方便地获得各种输出格式: XHTML、XSL/FO、WML、简单的文本、CSV、PDF、SVG等等。 XSLT处理流程 Model 2x标志着Web框架在分离版面设计与风格逻辑以及引导样式表这两方面有了提高。Apache的Cocoon框架就表现出s了这两个优点。例如,某个样式表可以定义在整个站点中某类特定表格的显示样式,比如下面这张样式定义了用户信息表格的显示样式。这张样式表可能输出如下一个表格:<xsl:template match="customer-info">
<table> <tr> <td>Name</td> <td><xsl:value-of select="name"/></td> </tr> </table> </xsl:template>
而另一个样式表也可以通过创建一个如下的嵌入表来设计这个表格。
<xsl:template match="table">
<table cellpadding="0" cellspacing="0" border="0" bgcolor="red"> <tr> <td valign="top"> <table cellpadding="4" cellspacing="1" border="0"> <xsl:apply-templates select="tr"/> </table> </td> </tr> </table> </xsl:template> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template>
性能
Model 2x当前的这个实现方案是动态的创建XML Dom树,然后把它交给XSLT转换器。通过使用SAX(Simple API for XML)显著地提高了性能,特别表现在等待时间和大文档的内存驻留上。此外,样式表的编译也提升了XSLT的处理次数。Xalan是Apache的一个XSLT处理器项目。它提供了一种把样式表编译成Java的class文件的机制,也就是是我们所说的translets。 客户端处理 目前,IE5/6、Mozilla这样的浏览器已经能够在客户端执行XSLT转换。如果要减轻Web服务器的负担,可以在客户端执行XSLT转换。XInclude(XML Inclusions)可以从服务器下载资源和其他的数据,并建立缓存。XInclude提供了一种通用的方法来识别和处理它的内含物,同时还能够提供很好的性能、更少的代码冗余。当然这种方法也存在问题。最大的一个缺陷就是开发人员必须确保传递给客户的XML、XSLT文档必须是客户有权看到的内容。 通过Model 2x来提升Struts的性能 总之,Model 2x中使用XML和XSLT来鼓励开发人员将业务逻辑和表现逻辑加以分离,这样web应用程序更接近MVC最基本的承诺。此外它还具有一些其它的优势比如输出的文档符合XML的文档格式、标准化的语言的使用、更好的表现层适应性,更短的开发周期。