Servlet 技术和JSP(JavaServer Pages)是利用Java语言开发Web应用程序的两种主要技术。Sun公司于1996年首次推出Servlet技术时,人们认为这种技术远优于当时占主导地位的公共网关接口(Common Gateway Interface,CGI)。这是因为某个servlet(服务器端Java程序)在应用户的请求而首次调入内存执行之后将一直驻留在内存里,对同一个servlet的后续请求不用再对这个servlet的类进行实例化,因此响应速度更快。.
可是,servlet也存在一个严重问题,因为所有的HTML输出必须像下面这段代码那样封装在String对象里,所以servlet将HTML标签发送给浏览器时既繁琐又容易出错:
这大大增加了编程的难度,而且即便是对网页在表现方面的细微改动,例如改变网页的背景颜色,也需要重新编译整个servlet。
Sun公司意识到了这个问题,并提出了JSP技术。JSP允许Java代码和HTML标签混杂在一起以简化页面的编辑工作,所有的改动无需重新进行编译:某个页面修改后,将在第一次被调用时自动编译。JSP里的Java代码段称为scriptlet 。
允许Java代码和HTML混杂在一起的办法乍看起来很实用,但实际上并不好,原因有以下几点。
把Java代码和HTML混杂在一起会让应用程序变得难以阅读和维护。
把代码编写在JSP页面里的做法使代码很难重用。当然,你完全可以把所有的Java方法都放到一个JSP页面里,并把这个页面包含到需要用到这些方法的其他JSP页面里,但这种做法显然偏离了面向对象范型。别的先不说,至少继承机制的好处是无从谈起了。
在JSP页面里编写Java代码要比在Java类里编写Java代码困难得多。我们必须面对这样一个事实:各种各样的IDE都是为了分析Java类(而不是JSP)里的Java代码而设计的。
如果把代码封装在Java类里,调试起来会更容易。
封装在Java类里的业务逻辑更容易测试。
Java类里的Java代码更容易重构。
事实上,把业务逻辑(Java代码)和页面表现(HTML标签)分开是非常重要的,JSP的设计者从JSP的第一个版本开始就一直在鼓励程序员采用这种做法。
在JSP 1.0中,程序员就可以使用JavaBeans 技术来封装Java代码,从而支持把有关代码和页面表现分离开来。程序员可以通过[jsp:useBean]和[jsp:setProperty]来创建JavaBean并分别设置属性。
令人遗憾的是,JavaBeans技术并没有为代码和页面表现的分离问题提供一个完美的解决方案。首先,因为对方法的命名必须遵守JavaBeans的命名约定,所以偶尔会出现某个方法的名字非常冗长难记的情况。其次,在使用JavaBean时,如果不求助于scriptlet,我们就无法把有关的参数传递给相应的方法。
为了使代码和HTML标签分离更容易实现,JSP 1.1定义了几个自定义标签库,它们比JavaBeans更加灵活易用。但这又引出了一个新问题:自定义标签很难编写,而JSP 1.1中的自定义标签又都有非常复杂的生命周期。
后来,人们开始给有关的标签添加一些特定的常用功能。这些标签编译为几个库文件,统称为JSTL(JavaServer Pages Standard Tag Libraries,JSP标准标签库)。例如,JSTL的标签可以处理各种作用域对象,遍历集合,进行条件测试以及解析和格式化数据等。
尽管有了JavaBeans、自定义标签和JSTL等多种选择,还是有不少人因为以下几个理由仍在JSP页面里使用scriptlet。
贪图方便。把所有的东西都集中放在JSP页面里非常方便。如果应用程序只包含一两个页面并且不可能再变得更加复杂,这种做法还算凑合。
目光短浅。乍看起来,把代码和HTML集中编写在JSP页面里似乎是一种能够缩短软件开发周期的好办法。但从长远看,采用这一思路开发软件是要付出代价的。代码的维护和可读性都成问题。
无知。
. 此外,如果参与项目开发工作的程序员水平参差不齐,将难以保证把全部的Java代码都井井有条地收录到有关的Java类里。JSP 2.0增加了一项能够让软件架构师在JSP页面里统一禁用scriptlet的功能,而这意味着架构师可以强制要求程序员把代码和HTML分开,从而更容易达到不在JSP页面里掺杂scriptlet的目的。另外,为了简化自定义标签本身的编写工作,JSP 2.0还提供了一个更简单的自定义标签生命周期,可在标签文件里设立标签。
为什么说Servlet还有生命力
随着JSP的到来,有许多人认为Servlet已行将就木,可实际情况并非如此。JSP并不能完全取代Servlet。事实上,许多新近推出的应用程序都同时使用了Servlet和JSP。为什么Servlet在JSP出现之后仍有生命力?要想了解其中的奥妙,必须先把用Java语言开发Web应用程序时的两种设计模型弄清楚。
第一种设计模型简称为Model 1,它紧随着JSP的出现而诞生。在这种模型中通常不用Servlet,从一个JSP页面导航到另一个页面是通过点击页面里的链接完成的。第二种设计模型简称为Model 2。下面马上就会介绍为什么Model 1不值得推荐,而Model 2才是应该遵循的设计思路。
Model 1设计模型的弊病
Model 1设计模型是以页面为中心的。按照这一模型实现的应用程序由一系列JSP页面构成,用户就在这些页面间转来转去。这种模型既易于理解又易于实现,所以很多程序员在刚开始学JSP时都会采用它。按Model 1模型开发的应用程序的主要弊病是:既难以维护又很不灵活。这种架构不利于网页设计师和Web开发人员之间的劳动分工:开发人员既要参与页面的开发,又要参与业务逻辑的编码。
Model 1设计模型不值得推荐的原因主要有以下几点。
导航问题。如果你打算改变某个JSP页面的名字,就必须在所有引用了这个页面的其他页面里把这个页面的名字全都改过来。
在这种模型下,因为JavaBeans的能力有限,而自定义标签又比较难以编写,所以在JSP页面里嵌入scriptlet的做法对程序员更有诱惑力。但我们刚才已解释过,让Java代码和HTML混杂在JSP页面里并不是件好事。
如果不让Java代码出现在JSP页面里,那么你必须为大多数业务逻辑编写自定义标签,因此你需要花费更多的时间开发应用程序。相比之下,把Java代码编写在Java类里更节省时间。
Model 2设计模型
第二种设计模型简称为Model 2。如果打算使用Java语言开发Web项目,这一模型应该是首选的架构。Model 2是MVC(Model-View-Controller,模型—视图—控制器)设计模式的另一个名字。按照Model 2模型开发的应用程序由3个主要部分组成:模型、视图和控制器。第1章将对MVC模式进一步讨论。
注意Model 2这个术语最早出现在0.92版本的JSP规范里。
在Model 2模型里,所有的页面都有一个共同的入口点,通常是由一个servlet或过滤器来充当主控制器,JSP页面负责实现页面表现。与Model 1相比,基于Model 2的应用程序主要有以下几个优点:
开发速度快;
易于测试;
易于维护;
易于扩展。
Struts概述..
前面我们解释了为什么说Model 2是用Java语言开发Web应用程序时的首选设计模型,接下来的问题将是怎样才能提高Model 2应用程序的开发速度。
这也正是Servlet专家Craig R. McClanahan在决定搭建Struts框架之前所想到的问题。在完成了一些基础性的构件之后,McClanahan于2000年5月把他的发明捐献给了Apache Software Foundation(Apache软件基金会),后者在2001年的7月份发布了Struts 1.0。它迅速成为人们在使用Java语言开发Web应用程序时首选的框架,这种潮流一直持续至今。Struts的官方网站是http://struts. apache.org。
就在同一时期,还有另外一些人在研究另一种基于开源的Java开发框架WebWork。虽然它一直不如Struts那样流行,但WebWork在体系结构方面却要优于Struts 1.0。例如,在Struts 1框架下,把用户请求中的参数转换为Java对象必须使用一个被称为“form bean”的“中介”对象,而这种转换在WebWork框架下无需使用任何中介对象。这意味着采用WebWork框架的开发工作更高效——因为需要用到的Java类较少。再例如,在WebWork框架里,插入一个拦截器对象相当简便易行,这可以给该框架增加更多的处理功能,而要想在Struts 1.0框架里做这种事就没那么容易了。
WebWork具备但Struts 1.0缺乏的另一项重要功能是可测试性。这对软件开发效率有着重大影响。在WebWork框架下对业务逻辑进行测试要比在Struts 1框架下容易得多。之所以会如此,是因为在Struts 1框架下对业务逻辑进行测试时,通常都需要使用一个Web浏览器才能从HTTP请求参数当中把用户输入检索出来。WebWork不存在这个问题,它不用浏览器也可以对那些用于业务的Java类进行测试。
一个是优秀的产品(WebWork),一个是流行的方法(Struts 1.0),两个阵营很自然地融合起来。根据Don Brown的个人博客(www.oreillynet.com/onjava/blog/2006/10/my_history_of_struts_ 2.html),这一切始于2005年JavaOne大会,一些Struts开发者和用户在研讨Struts的未来时提出了一个关于Struts Ti 的计划。如果Struts团队按照那个计划开展工作的话,Struts 2将只是对Struts 1里缺少的某些功能进行些补充而已,包括提高其可扩展性和完善对Ajax技术的支持。但在WebWork开发者Jason Carreira的建议下,那些人修改了最初的计划,决定向WebWork靠拢。这是一个明智的决策,因为当时的WebWork已经具备了拟议中的Struts Ti所缺少的绝大多数功能。与其重走长征路,“兼并”WebWork显然可以节省大量的时间。
结果,Struts 2从本质上讲已不是从Struts 1扩展而来的了,说它是一个改换了品牌标签的WebWork 2.2版本更恰当。WebWork本身是基于XWork的,而后者是Open Symphony公司推出的一个开源的命令模式的框架(http://www.opensymphony.com/xwork)。因此,如果大家在本书里遇到那些属于com.opensymphony.xwork2包的Java类型时,请不要感到惊讶。
注意在本书后面的内容里,如果没有特别注明,Struts将代表Struts 2。
那么,Struts到底提供了什么呢?Struts是一个用来开发Model 2应用程序的框架。这个框架可以提高开发工作的速度,因为它提供的下面这些功能解决了Web应用程序开发过程中的一些常见问题:
对页面导航活动进行管理;
对来自用户的输入数据进行合法性验证;
统一的布局;
可扩展性;
国际化和本地化;
支持Ajax技术。
因为Struts是一个Model 2框架,所以在使用Struts时还应该遵守以下几条不成文的规定。
不要在JSP页面里嵌入Java代码,应该把所有的业务逻辑包含在一些被称为“动作类”(action class)的Java类里。
在JSP页面里使用Expression Language(OGNL)去访问有关的模型对象。
尽量避免编写自定义标签(因为自定义标签的代码比较难以编写)。
升级到Struts 2
你也许用过Struts 1编程,本节提供了一个关于Struts 2新功能的简要介绍。如果你没有这方面的经验,跳过这一节也无不可。
在Struts 1里需要使用一个像ActionServlet类这样的东西作为servlet控制器;Struct 2使用了一个过滤器来完成同样的任务。
在Struts 2里没有任何动作表单。在Struts 1里,每个HTML表单都对应着一个ActionForm实例,你可以从动作类访问这个动作表单,并用它来填充数据传输对象。在Struts 2里,HTML表单将被直接映射为一个POJO,而不再需要你创建一个数据传输对象,因为没有动作表单,维护工作变得简单容易了,你不再需要与那么多的类打交道。
问题来了:没有了动作表单,怎样才能在Struts 2里通过编程对用户输入进行合法性验证呢?答案是把验证逻辑编写在动作类里。
Struts 1通过几个标签库提供了一批定制标签供程序员在JSP页面里使用,其中最重要的是HTML标签库、Bean标签库和Logic标签库。Servlet 2.4里的JSTL和EL(Expression Language,表达式语言)经常被用来代替Bean和Logic标签库。Struts 2为程序员准备了一个应有尽有的标签库,你不再需要JSTL,但在某些场合你可能仍需要EL。
在Struts 1里,你还需要用到一些Struts配置文件,其中最主要的是存放在各Web应用程序里的WEB-INF子目录里的struts-config.xml(默认文件名)。在Struts 2里,你仍需要用到多个配置文件,但必须把它们存放在WEB-INF/classes子目录或它的某个下级子目录里。
要想使用Struts 2,你的系统里必须有Java 5和Servlet 2.4(或更高版本)。之所以需要有Java 5,是因为Java 5里新增加的注解在Struts 2里扮演着重要角色。我们撰写本书时,Java 6已经发布,Java 7也指日可待,你很可能已经在使用Java 5或Java 6了。
在Struts 1里,动作类必须扩展自org.apache.struts.action.Action类。在Struts 2里,任何一个POJO都可以是一个动作类。不过,我们将在本书第3章说明,在Struts 2里最好对ActionSupport类进行扩展。在此基础上,可用一个动作类来完成相关的动作。
Struts 2在JSP页面里使用OGNL来显示各种对象模型,而不再是JSP EL和JSTL。
原本是Struts 1组件之一的Tiles现在已经发展为一个独立的Apache项目。它在Struts 2里仍可以作为一个插件使用。
各章内容概述
本书的目标读者是那些希望学习开发Struts应用程序的人。不过,我们并不只是介绍如何开发,我们还会花一些篇幅介绍编写高效Struts应用程序的方法。为了使本书名副其实,我们在编写本书时一直力求做到清晰易懂、可读性强。
下面是本书各章内容的概述。
第1章:Model 2应用程序
该章对Model 2模型的体系结构进行解释,并提供了两个Model 2应用程序示例,其中一个使用的是servlet控制器,另一个使用的是过滤器调度程序。
第2章:初识Struts
该章对Struts进行了简要的介绍。我们将学习Struts框架的各主要组件和如何配置Struts应用程序。
第3章:动作与结果
Struts解决了Web应用程序开发工作中的许多常见问题,如页面导航、输入验证等。因此,你可以把精力集中在最重要的开发任务上:在各个动作类里编写各种业务逻辑。这一章将介绍怎样才能编写出高效的动作类,还有其他相关主题,如默认结果类型、全局异常映射、通配符映射、动态方法的调用技巧等。
第4章:OGNL
该章对用来访问各种动作对象和页面内容对象的OGNL(一种表达式语言)进行讨论。OGNL是一种功能强大又易于使用的语言。除了访问各种对象,OGNL还可以用来创建各种列表和映射关系。
第5章:表单标签
Struts自带了一个标签库来提供UI标签和非UI标签(通用标签)。这一章将介绍表单标签,它们是一些用来输入表单数据的标签。你将了解使用这些标签的好处以及如何使用每一种标签。
第6章:通用标签
该章对非UI标签进行解释。非UI标签分为两大类:控制标签和数据标签。
第7章:类型转换
HTTP没有“类型”的概念,在HTTP请求里发送的值都是字符串。在把表单字段映射到非String类型的动作属性时,Struts会自动对这些值进行必要的转换。这一章将解释Struts如何完成这类转换,你还将学到如何为更加复杂的情况(当Struts 2内建的转换器帮不上忙时)编写你自己的转换器。
第8章:输入验证
该章对输入数据进行验证的细节进行探讨。
第9章:消息处理与国际化
该章讨论消息的处理问题,这也是应用程序在开发过程中最重要的任务之一。发展到现在,能够显示国际化和本地化的消息已逐渐成为应用程序必须具备的一项功能。Struts 2从研发之初就已经考虑到了国际化和本地化的问题。
第10章:Model Driven和Preparable拦截器
该章对两种用来把动作和模型分离开来的重要的拦截器进行讨论。你将发现有许多动作都需要这些拦截器。
第11章:持久层
该章研究存储各种数值对象所需要的持久层。持久层可以让它的客户(就本书讨论的问题而言,持久层的客户主要是有关的Struts动作对象)无需关心访问数据库时的复杂性。持久层可以实现为entity bean、DAO(Data Access Object,数据访问对象)模式,或者是使用Hibernate等。这一章将详细演示如何实现DAO模式。这类模式有许多种变体,你可以根据有关项目的具体情况和要求进行选择。
第12章:文件的上传
该章对现有的Web编程图书普遍没有给予足够重视的一个重要问题进行了讨论。Struts通过与Jakarta Commons FileUpload库的无缝集成来支持文件上传操作。本章将讨论如何在Struts框架下实现这个编程任务。
第13章:文件的下载
该章讨论文件的下载问题,并演示如何把二进制数据流发送给浏览器。
第14章:提高Struts应用程序的安全性
你将在这一章学习如何配置部署描述文件,才能对应用软件里的部分或全体资源的访问操作做出限制。“配置”在这里的含义是,你只需要修改部署描述文件,不需要进行任何编程。此外,你还将学到如何在Struts配置文件里使用action元素的roles属性。这一章还将介绍一些如何编写Java代码来保护Web应用程序的技巧。
第15章:防止重复提交
该章解释了如何利用Struts的内建功能防止用户重复提交请求。重复提交有时是由于用户的错误操作,有时是因为表单处理时间过长而让用户不知如何是好。
第16章:调试与性能分析
Struts应用程序的调试工作很容易进行。这一章将讨论怎样才能用好这个功能。
第17章:进度条
这一章介绍Execute和Wait两个拦截器,它们可以用来为那些响应时间比较长的任务提供一个进度条。
第18章:定制拦截器
这一章介绍如何编写你自己的拦截器。
第19章:定制结果类型
Struts支持各种各样的结果类型,我们甚至可以自行编写出各种新的类型。这一章将演示怎样才能做到这一点。
第20章:Velocity
Velocity是一种比较流行的模板语言,这一章简要介绍Velocity以及如何用它来代替JSP。
第21章:FreeMarker
这一章简单介绍Struts里默认使用的模板语言FreeMarker。
第22章:XSLT结果类型
该章讨论XSLT结果类型以及如何把XML转换为另一种XML、XHTML或其他格式。
第23章:插件
该章讨论如何把Struts模块打包成插件,这可以让它们更便于发行和传播。
第24章:Tiles插件
该章简要介绍Tiles 2。Tiles是一个开源项目,主要用于对Web页面进行布局。
第25章:JFreeChart插件
该章讨论如何使用流行的JFreeChart插件轻松地创建Web图表。
第26章:零配置
该章解释怎样才能开发出一个不需要配置的Struts应用程序,以及CodeBehind插件是如何使这个功能更加强大的。
第27章:Ajax
Ajax技术是Web 2.0的精髓所在,随着时间的推移,它正变得越来越流行。这一章将介绍Struts框架为Ajax技术提供的支持,并解释如何使用Ajax定制标签来构建Ajax组件。
附录A:Struts配置
附录A提供了一个关于如何编写Struts配置文件的指南。
附录B:JSP EL语言
当OGNL语言和Struts定制标签无法提供最佳的解决方案时,这种语言也许可以帮上大忙。
附录C:Java注解
附录C对Java 5里的这个新功能进行讨论,这个功能在Struts里用途很广。
系统最低配置要求和文件下载途径
要想使用Struts 2,你的系统至少需要安装Java 5、Servlet 2.4和JSP 2.0。这本书里的所有示例程序都基于Servlet 2.5,它目前是Servlet的最新版本(在我撰写本书时,Servlet 3.0正处于草稿阶段)。你需要Tomcat 5.5或更高版本;如果选用其他的Java EE容器,它们必须能够支持Servlet 2.4或更高版本。
Struts的源代码发行版本和二进制发行版本可以从以下地址下载:
http://struts.apache.org/downloads.html
有多种不同的ZIP文件可供下载。struts-VERSION-all.zip文件(其中的VERSION是Struts的版本号)包括了所有的库、源代码和样板程序,其长度大概有86 MB;如果你有足够的带宽,我建议你下载这个文件。如果你的带宽不足,那就试试struts-VERSION-lib.zip文件好了(只有4 MB左右),该文件只打包了必要的库。
选择并下载了某个ZIP文件之后,将它解压缩。你将发现lib子目录会多出几十个JAR文件。属于Struts 2的JAR文件的名字都以“struts2”开头。在每一个Struts JAR文件的名字里都包含版本信息。例如,core库被打包在struts2-core-VERSION.jar文件里,VERSION部分给出了相应的主版本号和次版本号。以Struts 2.1.0为例,它的核心库的文件名是struts2-core-2.1.0.jar。
要想使用Struts,还需要安装一些来自其他项目的库文件。常用的JAR文件来自Apache Jakarta Common项目。你的系统里必须有这些常用的JAR文件。此外,ognl-VERSION.jar文件包含OGNL引擎,这是一个重要的必备品。freemarker-VERSION.jar文件包含FreeMarker模板引擎。即使你选用JSP作为页面显示技术,FreeMarker模板引擎也是必需的,这是因为FreeMarker是Struts定制标签的配套模板语言。xwork-VERSION.jar文件包含XWork,而Struts 2就建立在这个框架的基础上,千万不要忘记把这个JAR文件安装上。
那些包含各种插件的JAR文件不是必不可少的。它们的文件名都有着如下所示的格式:
struts2-xxx-plugin-VERSION.jar
其中的xxx是插件的名字。例如,Tiles插件被打包在struts2-tiles-plugin-VERSION.jar文件里。
如果你的应用程序没有用到Tiles插件所提供的功能,就不需要安装与Tiles插件有关的JAR文件。
样板程序
这本书里的所有示例程序代码都可以从图灵网站www.turingbook.com本书网页免费注册下载。
这些示例程序的组成文件都按以下格式命名:
appXXy
其中XX是一个用两位数表示的章节数,y代表那个示例程序对应那一章里的第几个示例程序。例如,第1章里的第2个示例程序的文件名是app01b。
本书中的所有示例已经在Tomcat 6环境下通过了测试。在我们的机器上,它们都运行在8080号端口上。因此,所有的示例程序都需要这样启动:先写出http://localhost:8080,然后写出示例程序的名字和相关servlet的路径。...