Servlets和JSP Pages最佳实践

Java Servlet技术与JSP技术使Java服务器端技术,目前他们控制了整个服务器端Java技术市场,并且逐渐成为构建商业Web应用的标准。Java开发者喜欢这些技术是由于很多的原因,包括:这些技术很容易学习,一次编写,处处运行(Write Once, Run Anywhere)。更重要的是,如果更高效地采用了下面的实践,Servlet与JSP能够帮助分开Web的表示与内容。“最佳实践”是被证明为开发高质量、可重用与易维护的基于Servlet和JSP的Web应用的较好方法。与此相对应的是,将Java代码混合在HTML中,这样很容易产生低效率、不易重用、难于维护的复杂应用程序。最佳实践将改变这些弊端。

本文将描述为Servlets与JSP准备的最佳实践的重要性;这里假设读者已经了解两者的基本工作原理。这篇文章将涵盖以下内容:

简要介绍Java Servlet与JavaServer Pages (JSP)。
为开发Servlets 与JSP提供一些提示,技巧与规则。
为Servlet与JSP提供最佳实践。
Servlet和JSP Pages概述
类似于通用网关接口(CGI)脚本,servlets支持请求响应编程模式。当客户端给服务器发送请求时,服务器将请求发送给servlet。然后,servlet构建一个响应,服务器将该响应发送回客户端。然而,跟CGI脚本不同的是,servlets和HTTP服务器运行在同一个进程内。

当发出客户端请求的时候,调用service 方法并传递一个请求和响应对象。Servlet首先判断该请求是GET 操作还是POST 操作。然后它调用下面的一个方法:doGet 或 doPost。如果请求是GET就调用doGet方法,如果请求是POST就调用doPost方法。doGet和doPost都接受请求(HttpServletRequest)和响应(HttpServletResponse)。

最简单地说,servlets是能够使用print语句产生动态HTML内容的Java类。然而,有一点必须要提一下,那就是servlets是在一个容器内运行的,并且APIs 提供了对会话和对象的生命周期的管理。因此,当你使用servlets时,你就能获得Java平台的所有优势,它包括沙箱 (安全)、通过JDBC的数据库存取API和具有跨平台可移植性的servlets。

Java Server Pages (JSP)
JSP技术是Servlet技术的一个较高层次的抽象。它是Sun公司开发、开放的技术,是与Microsoft公司的ASP动态网页技术相似的一种技术,并且它是Java2 企业版(J2EE)的一个关键组件。目前,很多商业的应用服务器(例如BEA WebLogic, IBM WebSphere, Live JRun, Orion等等)都支持JSP。


JSP页面如何工作?

JSP页面实际上是一个带有传统HTML和Java代码的Web页面。JSP页面的文件扩展名是.jsp而并不是.html或.htm,该扩展名告诉服务器该页面需要特殊的处理,该特殊处理必须由服务器扩展或插件实现。

当一个JSP页面被读取时,他首先将被编译(JSP引擎来做这件事情)为一个Servlet。 这时候这个Servlet就像其他Servlet一样被交给Servlet引擎来处理。然后Servlet引擎读取那个Servlet对应的类(用ClassLoader)并且执行它,产生一个动态HTML页面(图1)。这个Servlet创建一些必需的元件,然后将这些元件作为一个字符串写入输出流(OutputStream),并显示在浏览器中。
调用JSP页面时,首先会将它编译成一个 (通过JSP引擎) Java servlet。这时,servlet引擎处理该servlet,就像处理任何其他servlet一样。然后,servlet引擎加载servlet类 (使用类加载器) 并执行它创建动态HTML发送给浏览器,如图1所示。Servlet创建所有必需的对象,并将所有对象作为字符串写入到输出流中,并在浏览器中显示。


图1: 调用一个JSP页面的请求/响应流程图

下次请求该页面的时候,JSP引擎执行早就装载的servlet除非JSP页面早就更改,在这种情况下,会将它自动重新编译进一个servlet中并执行。

最佳实践
在本节中,将描述在Servelt, 特别是JSP中的最佳实践。强调JSP最佳实践是因为JSP比Servlet得到更为广泛的应用(也许是因为JSP技术促进了表示与逻辑的分离)。一个集成Servlet与JSP的最佳实践是“模型-显示-控制器”设计模式(Model View Controller, MVC),将在本文的后面部分进行讨论。

在HTML页面中不要过多使用Java代码: 将所有的Java代码直接放在JSP页面中,对于小项目而言没有问题,但是过度使用将会导致意大利面条似的代码,难于阅读,难于理解。减少Java代码的方法是编写独立的Java类来实现计算等逻辑。一旦测试了这些类,就创建了实例。
选择合适的include机制: 最好将页眉、页脚和导航条内容存储在单个文件中,并且不要重新动态产生它们。一旦将这些内容存储在各个独立的文件中,使用下面include机制中的任何一个就能在所有的页面中引入它们:
Include 指令: <%@ include file="filename" %>
Include行为: <jsp:include page="page.jsp" flush="true" />

当JSP正在转换成Servlet时,第一种include机制将包含指定文件的内容(转换阶段),对于第二种include机制来说,当该页面执行后时,页面包含了用Response产生的内容。当被包含的页面不太改变的时候,我推荐使用第一种include指令方式,这种方式比较快,性能较好;当被包含的文件经常改变(其中也有动态内容)时,并且在执行主页的时候不能确定所要引入的的页面的时候,使用第二种include行为方式。

另一种include机制是使用JSP标准标记库(JSTL)中的<c:import> 行为标记。可以使用这种方式来包含本地的或者远程的文件,下面是一些例子:

<c:import url="./copyright.html"/><c:import url="http://www.somewhere.com/hello.xml"/>
不要将业务逻辑和表示混合起来: 在更为复杂的应用中,并且更多的代码被引入时,很重要的一点是不要将业务逻辑与表示混在同一个文件中。分开业务逻辑与表示使得当其中的任何一方需要改动是不至于影响到另外一方。JSP仅仅被作为前台的表示。那么,该如何实现业务逻辑部分呢?这就是JavaBeans的用武之地了。JavaBeans技术是轻便的、平台无关的组建模型,它使开发人员编写组件并且可以处处运行。在JSP环境中,JavaBeans组件处理业务逻辑并返回数据给JSP页面,这反过来格式化从JavaBeans组件返回的数据,以便在浏览器中显示。JSP页面通过调用JavaBeans组件的get方法和set方法来操作Bean的各项属性。使用JavaBeans技术的好处如下:
可重用:不同的应用可以使用同一个组件。
分离业务逻辑与表示:可以在JSP页面上改变数据的显示外观而不影响业务逻辑。换而言之,网页设计师只需要关注设计,Java开发人员只需要关注业务逻辑。
保持源代码的安全性,保护自己的知识产权。

如果在你的应用程序中使用了Enterprise JavaBeans (EJBs)组件,必须将业务逻辑保留在EJB组件中,提供生命周期管理,事务支持与对多客户端对域对象(实体Beans)的存取。可以在Enterprise BluePrints 获得更为详细的资料。

使用自定义标记: 并不是所有HTML内容开发者都喜欢将Java代码(或scriptlets)嵌入在HTML文档中的,可能是因为他们不了解Java语言并且也不乐意学习它的语法。但是不能使用JavaBeans组件封装很多Java代码,在JSP页面中使用它们仍然要求内容开发者具有Java语法的知识。

JSP技术允许你通过标记库设备引入新的自定义标记。作为一个Java开发者,你可以通过引入能够部署并在HTML这类语法中使用的自定义标记扩展JSP页面。自定义标记通过进一步分隔业务逻辑和表示逻辑也允许你提供更好的封装。另外,它们提供了自定义表示的方法,而使用JSTL却很难做到这一点。

自定义标记的好处:
他们能够消除JSP应用程序中的scriptlets。标记必需的参数可以作为属性或内容体传递,因此,不需要Java代码初始化或设置组件属性。
它们的语法非常类似。Scriptlets是使用Java代码编写的,但是可以在类似于HTML语法中使用自定义标记。
它们能够提高非程序员内容开发者的生产率,允许它们执行HTML不行完成的任务。
它们是可重用的。节省了开发和测试的时间。Scriptlets不是可重用的,除非你通过剪切粘贴 来“重用”。

简而言之,你可以使用和使用HTML创建表达式一样的方法来通过自定义标记完成复杂的任务。

编写自定义标记库的时候,可以使用下列编程指南:

保持简单性:如果需要在一个标记中包含多个属性,那么最好将它分为多个标记。
使它具有可用性:咨询标记的使用者(HTML开发者)从而获得高可用性。
不要在JSP页面中发明一种编程语言:不要开发自定义标记让用户编写显式的程序。
尽量不要重新发明轮子:目前有多个JSP标记库可用,如Jakarta Taglibs Project。查看这些标记库,看看是否有你所想要的东西。
不要重新发明轮子: 虽然自定义标记提供了重用宝贵的组件的方法,但是仍然要创建、测试和调试它们。另外,开发者仍然要不断地重新发明轮子,该解决方案的效率不是最高的。问题就是通过提供一组颗重用的标准标记解决JavaServer Pages Standard Tag Library (JSTL) 。JSTL定义了一组在任何地方都一样工作的标准标记库,这样你就不再需要使用scriptlet(或各类供应商提供的迭代标记)在集合中进行迭代。JSTL 包括各类标记,循环、不使用Java语法就读取属性、迭代各类数据结构、有条件地计算表达式、通过一种精确的方式设置属性和脚本变量并分析XML文档。
使用JSTL表达式语言: 使用JSP范围属性和请求参数将信息传递给JSP页面。表达式语言 (EL)是为页面作者特别设计的语言,将JSP范围属性提升为业务逻辑到JSP页面通讯的标准方法。然而,注意,然而EL是JSP技术的一个关键方面,它不是一个通用编成语言。而且,它是简单的数据存取语言,它不需要使用scriptlet或请求时间表达式值就能方便地存取(并操作)应用数据。

在JSP 1.x中,页面作者必须使用表达式<%= aName %> 来存取系统的值,如下面的例子所示:
<someTags:aTag attribute="<%= pageContext.getAttribute("aName") %>">
或自定义的JavaBeans组件的值:

 <%= aCustomer.getAddress().getCountry() %>
表达式语言允许页面作者使用简化的语法存取对象。例如,可以使用下面的语句存取简单变量:

  <someTags:aTag attribute="${aName}">
如果想要存取嵌套的JavaBeans属性,可以使用下面的语句:

 <someTags.aTag attribute="${   aCustomer.address.country}">
如果你使用JavaScript,你将会觉得非常熟悉,因为EL采用JavaScript语法存取结构化数据。

如果可能使用过滤器: 过滤器是JSP技术的一项新功能。如果你曾经遇到过这样一种情况,那就是有多个servlet或JSP页面需要压缩它们的内容,那么在这种情况下你就能够编写一个简单的压缩过滤器并将它应用到所有的资源上。例如,在Java BluePrints中,通过过滤器来提供SignOn。
使用可移植的安全模型: 绝大多数服务器提供服务器或供应商特定的安全功能,这样就将开发者局限于某台特定的服务器。为了最大化企业应用的可移植性,使用一个可移植的Web应用安全模型。然而,到最后,这归结成权衡问题。例如,如果你有一组早就定义好的用户,你可以使用基于表单的登录或基本认证来管理他们。但是如果你希望动态创建用户,你就需要使用容器特定的API来创建和管理用户。但是容器特定的API不具有可移植性,使用适配器(Adapter)设计模式能够克服这一点。
使用数据库存储持久信息: 可以使用HttpSession 对象实现会话,该对象提供了一个简单方便的机制来存储用户、识别用户的cookie。使用会话存储临时信息—所以即便这些信息丢失了,你也不用担心。(当会话过期或客户端改变浏览器的时候,会话数据就会丢失。) 如果你希望存储持久信息,使用数据库,在浏览器之间共享数据库中的持久信息更加安全更具有可移植性。
缓存内容: 永远都不要动态重新生成请求之间不会改变的内容。你可以在客户端、代理端或服务器端缓存内容。
使用连接缓冲池: 推荐使用JSTL进行数据库存取。但是如果你希望自己定制编写数据库存取的行为,推荐你使用连接池,这样能有效地让所有请求共享数据库连接。然而,注意,J2EE服务器在背后提供了该项功能。
缓存数据库请求结果: 如果你希望缓存数据库结果,不要使用JDBC的ResultSet 对象作为缓存对象。它和一个链接紧密结合,这个链接和连接池相冲突。将数据从ResultSet 复制到特定应用的bean,如Vector或JDBC的RowSets。
在必要的时候采取新的JSP XML语法:实际上这基于你希望你的应用程序如何遵守XML。然而,这需要进行权衡,因为这使得JSP成为更加强大的工具,但是对开发者却不怎么友好。
读取并应用Enterprise BluePrints:Sun的 Enterprise BluePrints 给开发者提供了指南、 模式、 和例子应用程序,如Adventure Builder和Pet Store。总的来说, J2EE BluePrints提供了最佳实践和一组设计模式,这些实践和模式是构建具有可移植性、强壮可扩展的Java应用程序中经常出现的问题的解决方案。
集成Servlets和JSP页面
JSP规范给出了使用JSP页面构建Web应用程序的两个方案:JSP模型1和模型2体系结构。这两个模型的区别在于处理的位置。在模型1的体系结构中,如图2所示,JSP页面负责处理请求并将响应发送给客户端。


图 2: JSP模型1 体系结构

模型2体系结构,如图3所示,集成使用了servlets 和JSP页面。在该模型中,JSP页面用于表示层,并且servlets负责处理各类任务。Servlet作为一个控制器,负责处理请求并创建JSP页面所需的任何bean。该控制器也负责确定将该请求传递到哪个JSP页面。JSP页面检索servlet创建的对象,并提取动态内容插入在一个模板中。


图 3: JSP模型 2 体系结构

该模型促进了模型视图控制器(MVC)体系结构风格设计模式的使用。注意,早就存在多个框架能够实现该有用的设计模式,并将内容和表示真正地独立开来。Apache Struts是MVC的形式化框架。该框架非常适用于复杂的应用程序,在这些复杂的应用程序中单个请求或表单提交会产生看起来截然不同的结果。

结束语
最佳实践—事实证明是经常重复出现的问题的解决方案—产生了高质量的应用程序。本文是开发servlet时需要遵守的多个指南和最佳实践-和基于JSP的Web应用程序。

请留心servlets和JSP技术,因为在这些技术中有许多激动人心的东西。例如,JavaServer Faces (JFC),是一个Java程序社区(Java Community Process),它的目标是定义一个标准的Web应用框架,这将很好地和Apache Struts集成。