您将了解刎ͼShale ?Struts 的背L一柄双刃剑。一斚wQShale 是经q精心设计的 Struts 的后代。Shale 的创立者综合考虑?Struts 的优点和不Q提出可与其前辈媲美的下一代框架。另一斚wQ正如您很快可以在q个pd中看到的一PShale ?一U完全不同于 Struts 的框Ӟ其中隐含着很多新的开发工?
Shale 不仅仅是 Struts 的又一个修正版Q它已扩展到出 Struts 所能达到的高度。它包含 Java Web E序设计中一些最重要的、最q的开发成果,包括 JSP Standard Tag Library(JSTL)?JavaServer Faces(JSF)Qƈ建立在这些开发成果之上。Shale 完全应该被看作是?Struts 不同的一U框Ӟ在这个系列中Q我还 Shale 框架以本来面目。在q个月的文章中,首先对 Shale ?Struts 之间的区别作一个概qͼ然后带您体验安装 Shale q测试安装情늚步骤。最后,我将l出一些思想Qo您能q一步参与到 Shale 目(它是开放源码的)中,q提供一些相关的信息。整个系列的目的是要向您展C如何安?Shale 以及如何使用 Shale 构徏和开发项目,同时很少涉及 Shale 的前辈,?Struts 框架?/p>
评h Shale
M新的 Web 开发框架要惛_q个竞争已经很激烈的领域占得一席之圎ͼ最好能够经受住巨大压力下的评测。好消息是,Shale 独力l受住了l致的考察。但是,坏消息是Q由?Shale 完全是对 Struts 重新构徏的物,因此必须重新~写和重新测试您所有基?Struts 的代码,以便实现q些代码。您花同样多的_֊来编写一个新?Shale 应用E序Q或一?Struts 应用E序转换?Shale 应用E序Q就好像 Shale ?Struts 完全无关一栗?/p>
所以接下来我们忍不住要问,Z么还要采?Shale ?Z得出{案Q我首先解释一?Shale 的伟大之?—?q在很大E度上是׃它的 Struts 血l,但这又不是惟一的原?—?然后讨论 Shale 之所以没?被发布ؓ Struts 框架的重要修正版的两大原因。这P您就会更好地理解?Shale w上可以得到什么,q将有助于评价用这U下一代的框架是否值得?/p>
Struts 血l?/strong>
Shale 重用了大量的 Struts 代码基,q声U?Struts 是它?“父?框架Q因此如果您要相?Shale 的h|得怿 Struts 的h倹{首先,Struts 作ؓW一个真正意义上?Web 开发框Ӟ拥有巨大的h倹{据 Shale ?Struts |站报道Q第一批代码是?2000 q?6 月提交给 Struts CVS 存储库的Q而Struts 1.0 是在 2001 q末才发布的。当很多开发h员正在艰隑֜使用 JavaServer Pages(JSP)和不断变化的 servlet 规范ӞStruts 提供了一U易于用的 Model 2 Ҏ来构建基?servlet ?JSP ?Web 应用E序。换句话_Struts ?Web 开发h员可以开发健壮的 Web 应用E序Q而不必精于日志记录、分布式计算、JDBC、Servlet、JSP、JNDI、RMI ?大量其他?API 和技术?/p>
接下来,Struts 要做的事情就是保持它的强大?从写出第一批代码开始,Struts q箋 6 q一直是最行?Web 开发框架之一。至今它仍然是h们口中的谈资Q笔下的素材Q用得不比M竞争Ҏ。由?Struts 是如此流行,如此长寿Q如今它已经有丰富的功能Q有良好的文档,被广泛地支持Qƈ且易于用,在它上面q行开发和理也很Ҏ。数千名开发h员对 Struts 邮g列表上的问题作出{复Q数万名开发h员试?Struts q报告问题,q得这些问题很Ҏ得到修复?/p>
最后,Struts 是不断发展的。很多框架一开始比较强大,然后停滞不?商业产品和开放源码项目都存在q样的现?Q?Struts L不断提供新的Ҏ。当您下?Struts Ӟ核心发行版中q包含一个健壮的认引擎(validation engine)Qƈ?Struts 已经?JavaServer Faces 集成Q拥有广泛的标记库和一个不断发展的 Model 2 架构Q其中引入了在分布式 n-层应用程序领域中最新的思想。而且告诉您,Struts q紧跟程序设计中出现的新模式Q例?IoC(Inversion of Control)。Struts ?WebWork ?Spring 框架自然地集成,后两者都是具有最佌l的、ؓ使用 Web 开发中的新Ҏ提供入口的框架?/p>
而言之,您很隑֏现在 Struts 中有做不?或不支持的事。所以显然我们就要问Qؓ什?Shale 要另L灶呢?Z?Shale 没有成ؓ Struts 的下一个版本呢?q有两个主要原因:一个原因与鲜ؓ人知的新软g发行惯例有关Q另一个原因则与众所周知?Struts 根基中的q有关。让我们分别来考虑q两个原因?/p>
发行版L?/strong>
要理?Shale Z么没有成?Struts 的一个新的发行版Q首先需要理解关于新软g发行的一两g事。对于一个新的Y件发行版Q大多数用户首先看的是一l新的特性。版本升U的q度大Q用户对新特性的期待p大。因此,如果软g版本?2.1 升?2.2Q就应该有一些新的特性,但是如果从版?2.2 升到版?3.0Q那么就应该有很?新特性。这是Z么当一些大型?例如 Microsoft Word)或操作系l?例如 Windows ?Mac OS X )Z新版本的时候,用户L对它们很挑剔。对于每一个新的发行版Q用hL期待有更多新的特性?/p>
׃大多数用户一呛_注意力攑֜Ҏ上Q他们没有意识到向后兼容?backward compatibility)才是真正最有h值的东西。虽然每个h都希?Excel 中加入新的、很好的选项Q希?Panther ?iTunes 有更好的集成Q希?Gnome 中对 XUL 有更好的支持Q但是如果那些用L有的E序和文件在新版本下H然不能q行的话Q相信他们会叫Q“这是血腥谋杀”。在q种情况下,新特性毫无h倹{?/p>
对于 Struts 也一栗一般来_Struts 的每个新版本都增加了新的Ҏ,同时保持了与之前版本的向后兼Ҏ。此外,新版本的 Struts q需要支持旧版本?Java q_?Servlet 规范Q因为已安装的旧?Struts 要在q些q_上运行。这两个需?—?向后兼容性和Ҏ版本?Java API 的支?—?对于 Shale 来说已经是一个严重的U束。尤其是Q至就 Java q_而言QJSF API 已经成ؓ Web 的中心组件。虽?Struts 也支?JSFQ但?Shale 却是完全依赖?JSF ?—?q对于需要维持向后兼Ҏ的 Struts 来说直是不可能的?/p>
最后,zZ个像 Shale q样的新目Q同时l在 Struts q种已有的项目上q行开发活动,q样做具有无与u比的优势。如?Struts 只是单地升?2.0(或?3.0 ?4.0)Qƈ在不考虑向后兼容性的情况下实?ShaleQ那么对于很多h来说Q由此造成的移植工作将是o人痛苦的Q可能有人干脆连 Struts 也不再用了。即使仍然维护更旧的代码基,也难于吸引开发h员花旉来修?bugQ他们也不愿意ؓ一?“遭到废弃?的或?“旧?版本的Y件增加特性?/p>
׃q些原因Q让 Shale 成ؓ一个全新的目Q其徏立在一个新的代码基之上Q是很有意义的?br />Shale 的面向服务架?/strong>
Shale 之所以没有成?Struts 的一个新的发行版Q其最后一个原因与逻辑没有关系:而是与该框架新Ҏ接纳?Web 开发中的能力有关系。Shale 在很多方面与 Struts 存在不同之处Q其中有两点最为突?
Shale ?JSF 的依赖性具有深q的、o人惊讶的意义Q而且大部分情况下是积极的意义。随着q个pd的深入,我将深入研究q些意义。在讨论其他斚w之前Q有必要寚w成 Shale ?Struts 之间差异的第二个斚wq行详细的讨论?/p>
如果您多ơ用过 StrutsQ那么会意识到它很大E度上可以看作一个请求处理器Q通过它可以接受请求,q指C框架如何处理请求。您可以指出采取哪种动作Q用什么模型,哪U视图显C给用户Q采用什么验证规则,昄哪种表单 ... Struts 是完全可以配|的。然而,所有这些的核心是一个请求处理器Qؓ每个h提供服务。这L处理器是 Struts 中最重要、也是最复杂的部分,因ؓ对于在处理一个请求的q程中涉及的所有工作,它都必须q行处理或托,?Struts 代码Z几乎没有哪一部分与这个请求处理器没有关系或不受它的媄响?/p>
因此QStruts 基本上难于作出改变。如果想修改处理h的方式,或改变处理请求过E中各部分的序Q那么将面巨大的困难。实际上Q不得不重新~写 Struts 代码基。更改请求处理器的行为倒是E微可行Q但是大部分是不容变动的。这?Shale 试图解决的关键问题之一(如果您需?Web 框架有那U程度的灉|性的??/p>
Shale 没有?Struts h处理器那L中心lgQ它只是一l数量很多的服务。可以自q合这些服务,每个服务与其他服务之间是松散耦合的。通过一l良好定义的接口(有时候实际上是 Java 接口c,有时候只是组件之间某UŞ式的契约)Q配|?Shale 的行为很Ҏ。这使得 Shale 是可扩展的、灵zȝQ甚x “聪明的”。很Ҏ更改它,不费吹灰之力可以扩展它Qƈ可以使它q速适应新的~程Ҏ。像q样松散地组装组件或服务通常被称作面向服务的架构Q或U?SOA。当Ӟq听h有点儿玄乎,不过您大可不必因此而觉?Shale 不好!
安装 Shale
可以从逻辑上和概念上理?Shale ?Struts 的不同之处,但是要想在脑里弄清楚这两种伟大的框架有什么不同,则需要亲自动手去实践一番。很自然Q每一U?Web 框架首先都需要下载和安装。不q幸好,在这个过E中通常可以了解到很多东ѝ那些安装和讄h比较困难的项目和产品Q通常也难于配|,难于在它上面q行部vQƈ?最坏的情况)难于长久q行。虽然安装过E很难作价一?Web 框架好坏的最可靠手段Q但是至肯定应该成个标准的一部分。在q一节中Q您学习手动地安装 ShaleQ对于一些难Ҏ一定的体会Qƈ了解Z?Shale q行Q系l上需要些什么东ѝ?/p>
注意Q我q提C Shale ?便安?选项Q但是我强烈您至试一试手动安装,了解它提供的较深层的信息?/p>
先决条g
Shale 的先x件和需求相当多。和大多C Apache ?Jakarta 相关的项目一PShale 的安装要依赖于一些其他的 Jakarta 目。下面是Z?Shale 得以q行所需的所有东西的完整列表:
Apache Ant 只是在构?Shale 时要用到Q但是无论如何,如果您要q行较多?Java 开发,那么pȝ上还是需?很可能已l有?一个版本的 Ant。如果想跟踪 Shale 中的 bugQ那么需?FindBugs 0.8.5 或更高版本和 JUnit 3.8.1 或更高版本。由于在W?1 部分中我只是讨论 Shale 的安装和使用Q因此您q不必关?FindBugs ?JUnitQ除非您x点儿装上q两个项目?/p>
附g和它们的依赖?/strong>
?Struts 一PShale q有一些附??Shale 中常被称作可?Shale lg)Q这些附件各自又有其依赖?
如果说这份列表显得有些长q且令h生畏的话Q那么没?Shale 使用大量较低U的库、helper cR实用程序组件和来自其他目的类。如果必逐个安装q些lgQ配|?Shale 使用它们Qƈ所有这些组件组装成可以部v的某UŞ式的话,即是最专业的开发h员也会对 Shale 望而生畏。此外,׃ Shale 在它的开发圈子内仍然相当q轻Q对于这些依赖项的获得和配置仍然有些E嫩;然而,Shale 实是非常可行的Q而且不需要花Ҏ惌中那么多的精力?br />
首先Q如果您正在用某U框架从事某U?Web 开发,那么应该已经有了一些作Z赖项的项目。所以这份列表比它初看上去要Ҏ理得多。例如,M Web 开发h员都应该已经?JRE ?JDKQ也应该有一?servlet 引擎Q例?Jakarta Tomcat。如果已l有一?servlet 引擎Q那么也应该?Servlet API ?JSP 引擎。另外,大部?servlet 引擎(臛_当前版本而言)都包括一?JSTLQƈ且很多都附带?JSF。最后,大多数开发h员很可能在他们的机器上已l安装了 Ant。因此,q䆾列表很快只剩下q些?
考虑?Tapestry 实际上提供了它的所有依赖项附带下蝲的选项Q因?“太多依赖项?的问题很快就不成为问题了?/p>
现在您已l知道了大概Q现在我们来看看下蝲、安装和配置 Shale 及其依赖的步骤?/p>
1. 下蝲 Shale
要下?ShaleQ可讉K Shale 目主页。您可以看到一?“Shale Download?区域Q其中有 Shale 框架的每晚构建的链接。单击这个链接,便可以进入如?1 所C的一个站?
?1. 来自 Shale CVS 存储库的每晚构徏
Z?Shale q行Q需要下?“framework?文g?“dependencies?文g。例如,在撰写本文时我下载了以下两个文g:
当然Q如果您需要或者更愿意下蝲 .tar.gz 版本Q那么可以不选择 .zip 版本Q而选择 .tar.gz 版本。由?Shale 的开发还在进行中Q目前还没有发行的构建,因此您应该尽量下载最q的每晚构徏(h最q的日期)?/p>
下蝲完这两个文g后,首先解压q两个归档文件。对于核心框Ӟ解压后可以得C个具有Ş?shale-framework-20060204/ 的名U的文g?对于 dependencies 归档文gQ解压后可以得到一个名?lib/ 的文件夹。将核心框架目录 shale-framework-20060204/ 转移到您希望保存 Java 目的地斏V例如,在我的系l上Q我?shale-framework-20060204/ Ud?/usr/local/java 目录中。接下来Q将 lib/ 目录Ud?Shale 目录中,所以最后的目录l构?shale-framework-20060204/lib/ cM?br />
2. d Shale 库到 Web 应用E序?/strong>
下一步是所?Shale JAR 文g和库d?Web 应用E序可以讉K和用它们的位置。步骤如?
1. 如果?servlet 引擎中没有包?JSFQ那么将 shale-framework-20060204/lib/jsf-ri/jsf-api.jar ?shale-framework-20060204/lib/jsf-ri/jsf-impl.jar 复制到应用程序的 WEB-INF/lib 目录中?/p>
2. ?shale-core.jar、shale-clay.jar、shale-tiles.jar ?tiles-core.jar ?shale-framework-20060204/dist/ 目录复制?Web 应用E序?WEB-INF/lib 目录?/p>
3. 以?Shale 依赖复制到 Web 应用E序?WEB-INF/lib 目录:
4. 如果要?Shale ?Spring 集成Ҏ,那么?shale-spring.jar ?shale-framework-20060204/dist/ 复制?Web 应用E序?WEB-INF/ 目录。要完成q个步骤Q还必须保 Spring 的打?JAR 文g也在 Web 应用E序?WEB-INF/lib 目录中。这?JAR 文g名ؓ spring.jarQ如果您q没有这个文件的话,可以?shale-framework-20060204/lib/shaleframework/ 目录中找到它?/p>
5. 如果正在使用 Java 5.0Q那么将 shale-tiger.jar ?shale-framework-20060204/dist/ 复制?Web 应用E序 ?WEB-INF/lib 目录中。只有在使用 Java 5.0 的时候才需要执行这一?否则Qservlet 引擎和?Shale ?Web 应用E序׃出问题?/p>
再往后走开始复杂v?是的Q这些复制操作是较容易的一部分)。接下来的事情未必都要用最隄方式dQ至我应该让您有机会选择 试试Ҏ的方法。 Shale 在系l上q行q个d的确存在捷径;您已l知道手动设|?Shale 的过E比较复杂,接下来有必要看看 “简侎?Ҏ?/p>
更容易的Ҏ
重新讉K Shale 下蝲站点Qƈ下蝲名称cM?shale-starter-20060204.zip ?“starter?应用E序。解压这个归档文Ӟ得C个名?shale-starter/ 的目录。这是一个基本上配置好的 Shale Web 应用E序Q用于帮助避免前一节详l介l的复制和配|工作。首先要做的是将 shale-starter/ 目录重新命名成应用程序以后要使用的名Uͼ例如可以它命名?first-shale/。进?first-shale/ 目录Q在q里可以看到一些文件和子目录?/p>
?first-shale/ 目录中,创徏一个名?build.properties 的新文g。通过q个文g可以定制如何构徏 Shale starter 应用E序Qƈ保该应用程序适合您的环境讄。清?1 展示了一个基本的 build.properties 文gQ可以根据自q环境对其q行定制?/p>
清单 1. Shale starter 应用E序的示?build.properties
# Basic project information # Java package and context path for servlet engine # Directory for Shale distribution - change this for your system # Directory for all your libraries - change this for your system |
Ҏpȝ讄好这些属性后Q便可以q行 ant。Shale starter 应用E序开始构?q?假设已经正确地设|了路径)创徏一个示例应用程序。如果有问题Q则构徏脚本输出错误消息;q些错误消息都描q得很清楚,所以您应该可以更正M错误?/p>
构徏q程的最后将生成一个名?target/ 的新目录。进入这个目录,可以看到一个名?first-app(x?build.properties 中ؓ目指定的名U?的子目录。大多数 servlet 引擎都允许将q个目录整个地复制到 servlet 引擎?webapps/ 目录。例如,我用的?TomcatQ于是我构本创建的整个 first-shale 目录复制?/usr/local/java/tomcat/webapps?br />
构徏 WAR 文g
如果使用?servlet 引擎要求提供 WAR 文gQ那么可以用相同的 Shale starter 应用E序的构建文Ӟ只需略微修改一下。由于还没有?Shale 应用E序~写M Java 文gQ当您请求一?WAR 文gӞ构徏脚本出现错??build.xml 中有查找文g?JavaScript 命oQ但是没有找CQ何文?。ؓ了修复这个问题,打开 build.xml 文gQ找C “javadoc?开头且如下所C的代码:
<!-- ===================== Generate Documentation ======================== --> <mkdir dir="${build.docs.dir}"/> <javadoc <copy todir="${build.docs.dir}"> </target> |
现在Q注释掉 javadoc dQ如下所C?
<!-- ===================== Generate Documentation ======================== --> <mkdir dir="${build.docs.dir}"/> </target> |
一旦开始ؓ Shale 应用E序开?Java 代码Q便不必q样做。不q对于现在,q样做可以解决上q问题。保存修改后?build.xml q运?ant dist。Ant ~译和装?starter 应用E序Qƈ?dist/ 目录中创Z个新?WAR 文g。例如,我运?ant dist 后得C?dist/first-shale-0.1.war 文g。现在可以将q个 WAR 文g复制?servlet 引擎?webapps/ 目录?/p> 试安装情况 如果完成了以上步骤,不管选择的安装\径是什么,都应该可以启?servlet 引擎q过地址 http://your.host.name/first-shale 讉K Shale 应用E序。例如,如果在本地机器上q行 TomcatQ那么最l可以访问的地址?http://localhost:8080/first-shale。如果一切正常,那么应该可以看到如图 2 所C的单页? ?2. Shale starter 应用E序证明一切没问题 看v来似乎做了这么多工作却所得甚,但是要考虑刎ͼ通过打开q编辑一个简单的 build.properties 文gQ可以避免大量繁杂的复制和配|工作。您发玎ͼ从空白的 Shale starter 应用E序开始L开发新?Shale 应用E序最Ҏ的方式。实际上Q当在下一文章中开始开?Shale 应用E序的时候,用空白的 starter 应用E序作ؓ开始的基础?/p> Shale 用例 关于 Shale 的下载和安装׃l到q里Q不q我们还是再q儿时间从 Shale M载站点下?Shale 的用?WAR 应用E序。找C个文件名形如 shale-usecases-20060204.war 的文件。下载该文gQƈ它攑օ servlet 引擎?webapps/ 目录Q然后进入到q个 WAR。在我的pȝ上,讉K http://localhost:8080/shale-usecases-20060204/ q得到如?3 所C的屏幕: ?3. Shale 用例应用E序 您应该花些时间来看看q个用例应用E序。它有关?Shale ?Validator 和远E报告等Ҏ的很好的演C,q有一个简单的 Ajax 应用E序。通过览q些用例Q您可以了解到即使是单的 Shale 应用E序也可以做许多事情?/p> 不过q里要提一个忠?有些用例仍在开发中Q取决于您何时下载每晚构建,可能发现有些用例不能正常工作。不qL可以晚些时候再下蝲q些用例应用E序Q看看有些问题是否已l被修复。虽然存在这些小问题Q但是用例应用程序仍然是取得?Shale 的基本印象的一U好途径?/p> 深入研究 ShaleQ?/strong> 大多?Web 开发h员向来只是用已有的框架(例如 Shale、Struts ?Spring)来开发他们的 Web 应用E序Q而没有做别的事情。当然这没有什么错Q但是如果想理解一U框架以及它所涉及的技术,那么只能Ҏ架本w做深入的研I?/p> 对于 Shale(当然也包?Struts)Q通过查看框架的内部,您可以学到大量关?servlet ?Web 开发的知识。如果想在自q目中用一?Shale 依赖,q样做还可以获得难以|信的帮助。如果您寚w过 Java 应用E序q行日志理感兴,那么通过 Shale 来熟?Apache Logging 目比阅MQ何文章都要有效得多。对?Jakarta Commons BeanUtils、Chain ?Digester 目也是一栗这些都是很好的工具Q对于开发h员很有用Q所以花几个星期或几个月的时间探索一?Shale 对于q些领域是一个很好的学习l历?/p> ׃本文是对 Shale q行深入探讨的系列中的第一期,因此如果我不对几个对?Shale 目入门来说臛_重要的方面进行讨论的话,是不负责Q了?/p> 亲密接触源代?/strong> 不幸的是Q关?Shale 中涉及的开发过E的文档q不多,所以如果您想直接?Shale 源代码的话,需要用点儿技巧。一般来_我这里给出的关于下蝲 Shale q将它作为框架用的说明也适用于下?Shale 的源代码。每晚构建包?Shale 的所有源代码Qƈ且代码的每个目录中都有一?build.xml 文g?/p> 需要将下蝲?Shale 的根目录下的 build.properties.sample 文g复制C个名?build.properties 的文件中(L原始文g名尾部的 ?sample?。清?2 展示了这个文件的一个示例,Zzv见,q里省略了其中一些注? 清单 2. CZ Shale 构徏文g
# This file contains example property settings that you would use to customize # Root directory into which you have unpacked the Shale Framework release. # Fully qualified pathname of the directory into which you have unpacked findbugs.outputFile=${root.dir}/find-bugs.html # The absolute or relative pathname of the Apache Struts spring.home=${lib.dir}/springframework |
Z与您的系l相匚wQ需要更改这个构建文件中大部分的路径。默认情况下Q?{basedir} 指向q行 Ant 时所在的目录Q因此如果是从下载的 Shale 的根目录下运?AntQ那么就刚好不用改\径了。但是对于其他\径,应该改ؓ适当的与pȝ相匹配的路径。例如,如果您的 JSF 参考实现在 c:/java/jsf-1_1_02 中,那么使用 jsfri.dir 目录所在的路径。大多数默认路径都适合于?MyFaces(请参?“MyFaces q是 JavaServer Faces?Q但是当然也可以使用 Sun ?JSF 实现Qƈ对这些\径作相应的更攏V另外还需要设|?Struts、Spring(q是可选的Q对于核?Shale 框架来说不必??FindBugs 目的\径?/p>
Ant d
讄好这些文件的路径后,可以在 Shale 的根目录中运?Ant。但是,首先应该q行 ant download-dependencies。您当然也已l注意到QShale 有很?依赖,而通过使用 Ant 自动下蝲q些依赖可以ؓ您节省很多时_也o您轻松不。Ant 脚本q负责设|\径,以便?Shale 与那些依赖项q接h。还应该q行 ant copy-jsf-ri 来处理一些特定于 JSF 的Q?具体l节不必兛_Q因?Ant 会ؓ您打点一??/p>
在构Z Shale 发行版之前,应该q行 ant clean 删除之前已有的构建后的代码。虽然这意味着整个构徏旉会更长,但是可以保所有代码将一致地构徏。最后,q行 ant releaseQ以便从头开始构?Shale。当q个 Ant 脚本q行完成?q要׃点儿旉)Q就可以得到一个完整的、从源代码构建的 Shale 发行版?/p>
关于邮g列表的只a片语
开发源码项目几乎完全是通过电子邮g(再加?Apache bug 跟踪数据库,?参考资?节中有q方面的内容)来运作的。Shale 在这斚w也是一LQ不q它仍然使用 Struts 的邮件列表。如果在使用 Shale 时有什么疑问,可以发送电子邮件到 user@struts.apache.org。但是当您开始开发真正的 Shale 内部lgӞ应该电子邮件发送到 dev@struts.apache.org。不将电子邮g发送到哪里Q都应该?“[shale]?开_q样别h一下子明白您是要问关?Shale 的问题,而不是关?Struts 的问题。预期在几个月后Q当 Shale 开始成为独立的目Ӟ它也会有它自q邮g列表?/p>
q里E微提醒一下,其是在发送电子邮件到开发列表的时?做好自己的工作,问题要有的放矢。那些飘忽不定、模׃可或~Z思想的邮件很可能不会收到回复。如果您到处发?“我惛_?ShaleQ请l我发送一些例子应用程序?之类的邮Ӟ甚至q可能得到粗鲁的回答。虽然这U提醒看上去有些傻,但事实就是这栗开发列表中L充斥着q一cȝ问题Q这些问题都是不受欢q?的。通常Q花点儿旉认真地斟酌您的问题,解释一下您使用的^台和软g的版本,q说明您已经试过了一些常用的步骤。这样一来,您的h才会得到重q受到欢q,也就更容易得到答案。开发h员的列表q不是o人生畏的Q但最Lq样做显得您重别h?/p>
l束?/strong>
q个关于 Shale 的系列中的第一期文章说明,Shale q不适合每一个h。Shale 没有提供一个打包好的、有~制好的文档q经q良好测试的产品Q也没有附带自动安装E序和优雅的理界面Q这些都是很?Web 开发h员期?Tapestry 时代能提供的东西。虽然在以后版本的框架中会体现这些东?除了完全打包)Q但目前 Shale(?2006 q初?仍在开发过E中Qƈ?Shale 站点也基本上它UCؓ处于 “alpha?状态的目。Shale 中用的很多lg是稳定和成熟的,?Shale 本n仍然很年轅R如果您不能接受一些麻烦和困惑Q那么可能会惌一q左叛_开始用它?/p>
另一斚wQ如果您是一名对 Web 开发的前沿技术感兴趣?Java 开发h员,那么真应该看?Shale 目。虽然安?Shale q之工作要p更多的精力,但是它完全有条g成ؓ特别行?Web 开发框架。Shale l承?StrutsQ同时也提供了一些全新的东西Q这本n值得作一番调查。对于有兴趣成ؓ开放源码项目中的一员的开发h员,Shale 也是值得投入_֊的一个项目?/p>归于此类Q因为shale与jsf有千丝万~的联系
了解如何利用 JSP 标记文g、JSF ?Oracle ADF Faces 重用 Web 内容?Java 代码?/span>
|
2005 q?10 月发?/span>
代码重用是提高开发h员生产效率和应用E序可维护性的一U非常好的方式。您应当LL设计良好的框架和可自定义的组Ӟ而不是从头重来。应用程序特有的代码也可以在模块甚至相关目间重用。后一U可重用性可使您快速修改,整体利用新特性,q减测试和调试的时间?/span>
虽然q些听v来像是针对程序员的不错徏议,?Web 开发h员也应当注意q些事情。许?Web 开发h员已l在使用诸如 Java Server Faces (JSF)、Oracle ADF Faces ?Apache MyFaces 之类的框Ӟq些框架提供了许多内|组件ƈ支持创徏其他可重用组件。然而,很多时候是许?HTML ?JSP 标记从一?Web 面复制_脓到其他页面中Q这意味着?Web 内容改变时将不得不修改这些页面中的重复标记。此外,如果没有更新某些面Q那么应用程序的外观会不一致。如果跨面重用 UI lg׃会发生这U情况了Q这是因为发生变化时只需在一个地方进行编辑就可以了?/span>
在本文中Q我提供一些在Z JSF ?ADF Faces ?Web 应用E序中重?UI lg的最佛_c您了解到如何创徏定义 Web 面布局的模板,以及如何重用表单、菜单和按钮栏。您q将了解到如何{换现有的 JSP 面以它们更易于维护,以及如何由 JSF ?Oracle ADF Faces 提供的组件与 JSTL 和现?JSP Ҏ(例如标记文gQ一起用?/span>
Java Web 可重用特?/span>
自从W一个版本vQJSP 已l提供了一些鼓励可重用的基本机Ӟ例如 JavaBeans 支持、基?Servlets API RequestDispatcher ?<%@include%> 指o?<jsp:include> 标记。JSTL 增加?<c:import> 标记Q它使您能够包含某个资源的内容,该资源可以位于同一个应用程序、服务器中,甚至也可以在q程服务器上。Struts Tiles 围绕着q种内容包含Ҏ构Z一个完整的框架。JSF 也支持这一Ҏ,允许您构Z?<f:subview> 标记的子表单。JSP 2.0 增加了一个称为“隐式包含”的新特性。这些特性?<include-prelude> ?<include-coda> ?web.xml 文g中声明。正如您所能看到的Q虽焉?片断包含U类各异Q但每一U都有其自己的用途和上下文?
对自定义标记的支持从 JSP 1.1 有了,它ؓ构徏标记库提供了一?API。JSP 1.2 对该 API q行了增强,但很多h认ؓ它太复杂了。因此,JSP 2.0 定义了一个具有相同功能的全新 API。这个ؓ标记库提供的?API UCؓ单标?APIQ旧 API 现在UCؓ标准标记 API。许?Web 框架Q如 Struts、JSF ?JSTLQ仍使用标准标记 APIQ以便可以与 JSP 1.2 以及 JSP 2.0 一起用。简单标?API 是另一U?JSP 2.0 Ҏ??标记文g ?的基Q该Ҏ您能够?JSP 语法构徏标记库。除了简单标记和标记文g之外QJSP 2.0 规范q定义了 EL 函数Q后者您能够?EL 语法?JSP 面中调用静?Java Ҏ?/span>
JSF 标准组件定义ؓ它的可重用单元。这些组件比自定义标记更强大Q但也更难设计和实施。因为有几个公司和开放源代码机构正在制作可供使用?JSF lg库,所以您可能不需要构q JSF lg。本文的CZ使用?Oracle ADF FacesQ它是基?JSF 标准的最先进的框架?/span>
创徏面模板?/strong>典型 Web 应用E序的所有页面共享一个公共布局Q该布局可以定义在一个地方,?JSP 标记文g中。该模板可以生成标题和正文标记、应用程序的菜单以及在所有页面中出现的其他部分。此外,它可以包含用于加载资源绑定、设|?JSP 变量{的讄标记。在应用E序的每?Web 面中重复该标记是没有意义的。在q一部分中,您将了解如何使用 Oracle JDeveloper 10g (10.1.3)Q撰写此文时为早期试用版Q基?JSF ?Oracle ADF Faces 构徏自定义模ѝ?
JDeveloper 提供了一个创?JSF 面模板的向对{从 File 菜单中选择 New ,打开 New Gallery H口。然后,转至 Web Tier 中的 JSF cdQ在右侧面板中选择 JSF JSP Template q单?OKQ?/span>
单击 Next 跌 Welcome 面Q然后选择您用的 J2EE 版本Qƈ再次单击 NextQ?/span>
为模板提供一个文件名Q?/span>
选择lgl定样式Q?/span>
指定是否要用错误页面:
选择要用的标记库:
提供面标题和其他页面属性:
单击 Finish。JDeveloper 创模板q在可视化编辑器中将其打开。您可以使用 Component Palette ?JSF ?Oracle ADF Faces lgd到该模板中。然后,您可以在 New Gallery H口中从 Template 中选择 JSF JSPQ基于您刚创建的模板创徏 JSF 面。这是从模板构徏面的一U非常简单的Ҏ。另一U方法是该q?JSF 标记Ud一个可重用的标记文件中。以下段落用了W二U方法?
创徏标记文g?/strong>?File 菜单中选择 New ,打开 New Gallery H口。然后,转至 Web Tier 中的 JSP cdQ在右侧面板中选择 JSP Tag File q单?OKQ?
JDeveloper 打开一个创?JSP 标记文g的向导窗口。单?Next 跌 Welcome 面Q在 File Name 域中输入 pageTemplate.tag q单?NextQ?/span>
现在您就可以定义模板标记的属性了。假定您正在构徏一个基?Web 的向|您希望每个页面都有一个步?ID 和一个标题。标记文仉要该信息来ؓ每个面自定义模板标记。单?Add 按钮Q输?step 属性名Qƈ?Required 设ؓ true。对另一个名UCؓ title 的属性执行同L操作Q?/span>
单击 Next ?Finish。JDeveloper 在 WEB-INF 目录?tags 子目录下创徏 pageTemplate.tag 文g。用 <%@attribute%> 指o定义q两个标记属性:
<%@ attribute name="step" required="true" %> <%@ attribute name="title" required="true" %>在创建标记文件之后,JDeveloper 打开它进行编辑。以下段落介l了本文通篇用到的示例应用程序的模板代码?/span>
在标记文件中使用 JSF ?Oracle ADF Faces?/strong>无论您是否想在普通页面的标记文g内用这些框Ӟ您都必须?<%@taglib%> 指o来对其进行声明。在 Component Palette 中选择 JSPQ然后单?Taglib。您需要在一个对话框中输入要使用的标记库?URI 和前~Q?
您还可以使用 Component Palette 来将M标记拖放?JSP 面上,如果它的 <%@taglib%> 指o不在面中,那么 JDeveloper 自动添加它。pageTemplate.tag CZ使用了四个标记库QJSTL Core、JSF Core、Oracle ADF Faces Core ?Oracle ADF Faces HTMLQ?/span>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %> <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %> <%@ taglib prefix="afh" uri="http://xmlns.oracle.com/adf/faces/html" %>在同时?JSF ?JSTL Ӟ或者在标记文g中?JSF 时有一仉帔R要的事情您必dl牢讎ͼJSF 框架不支持页面范_后者是 JSTL 标记的默?JSP 范围。因此,您应当显式指定与 JSTL、JSF ?Oracle ADF Faces 的标记结合用的 JSP 变量的请求范围。此外,标记文g的所有属性都可以通过保存在页面范围内?JSP 变量来访问。您必须复制h范围内的属性,以便它们可以?JSF ?Oracle ADF Faces 标记一起用:
<c:set var="pageTitle" scope="request" value="${pageScope.title}"/>您还可以属性的值存储在?JSF 理?Bean 内。假定您有一个名UCؓ WizardBean 的类Q该cd faces-config.xml 中被配置为受?BeanQ?/span>
<faces-config> ... <managed-bean> <managed-bean-name>wizard</managed-bean-name> <managed-bean-class>webreuse.WizardBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> ... </faces-config>CZ Bean 有一?currentStep 属性,您可以在该属性中存储 step 属性的倹{该操作可以利用 JSTL ?<c:set> 标记来完成,但您必须保?Bean 存在于会话范围中Q如 faces-config.xml 文g中所声明的那P。只有在 JSF EL 表达式中首次引用受管 Bean 实例ӞJSF 才会创徏该实例。如果在讉K受管 Bean ?JSF ?Oracle ADF Faces 标记之前执行 JSTL 标记Q那么,您应当?<jsp:useBean>以确保该 Bean 实例存在于会话范围中。如此,您就可以安全C?<c:set> 标记了:
<jsp:useBean class="webreuse.WizardBean" id="wizard" scope="session"/> <c:set target="${wizard}" property="currentStep" value="${pageScope.step}"/>以上代码可以手动输入Q或者可以?JDeveloper ?Component Palette。选择 JSPQ然后单?UseBeanQ添?<jsp:useBean> 标记。JDeveloper 打开一个对话框Q您必须在其中提供该标记的属性:
最后,您可以在 pageTemplate.tag 文g中添加模板代码:
<f:view> <afh:html> <afh:head title="#{pageTitle}"/> <afh:body> <af:panelPage title="#{pageTitle}"> <jsp:doBody/> </af:panelPage> </afh:body> </afh:html> </f:view>所有的 JSF ?Oracle ADF Faces lg都必ȝ?<f:view> 内部?tt><afh:html>?tt><afh:head> ?<afh:body> lg生成具有相同名U的 HTML 标记?lt;af:panelPage> lg昄面标题。然后,<jsp:doBody> 调用您攑֜使用模板标记?JSP 面中的 <tags:pageTemplate> ?</tags:pageTemplate> 之间?JSP 内容。这U?JSP 面类gQ?/span>
<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %> <tags:pageTemplate step="..." title="..."> ... JSP content executed by doBody ... </tags:pageTemplate>当执行该面Ӟ标记文g生?<html>?tt><head> ?<body> 标记以及面标题和其他标题标记。然后,模板标记文g调用包?<tags:pageTemplate> ?</tags:pageTemplate> 之间?JSP 内容。在此之后,标记文g可能会生成一个页脚,q用 </body> ?</html> 来完成页面。下一部分在Z Web 的向导的面中用模板标记?
重用表单、菜单和其他 UI lg
利用 JSF ?Oracle ADF Faces lg构徏?UI 面板可以使用 "subviews" ?JSP 标记文g在多个页面中重用。前一U解x案与旧的 JSP 1.2 版本兼容Q但标记文g更灵zR这一部分介l如何基?JSF ?Oracle ADF Faces 一?Web 表单分成多个面和标记文件。当用户W一ơ访问应用程序时Q他用包含后退和前q按钮的向导式界面来逐步览q些面。在此以后,他可能想更新最初提供的信息。在q种情况下,用户需要用基于菜单的界面直接讉K应用E序的页面?
可以在上q的两种情况下用同一U?Web 表单。此外,所有的表单可以l合在一个确认页面中Q该面可以让用户以只读模式查看信息。当用户必须修改其中一个表单时Q可以编辑单个文件。每一个修攚w显C在向导风格的界面(用于从用户那获取信息Q、确认页面(用于查看信息Q和Z菜单的界面(qL于更C息)中。如果不重用表单Q您必d三个不同的地方进行相同的修改?/span>
开?Backing Bean?/strong>您在前一部分中已l发玎ͼCZ应用E序使用了一个名UCؓ WizardBean ?Backing Bean。pageTemplate.tag 文g讄?currentStep 属性,该属性保存用户在览器中看到的当前表单的步骤 ID。示例应用程序在认面Q第四步Q之前用了三个表单Q确认页面的 ID ?MAX_STEP 帔R来定义。将该常量公开为名?maxStep ?bean 属性,以便可以?Web 面中?JSF EL 来访问它Q?/span>
package webreuse; public class WizardBean implements java.io.Serializable { public final static int MAX_STEP = 4; private int currentStep; private String connName, connType; private String userName, password, role; private String driver, hostName, sid; private int jdbcPort; public int getMaxStep() { return MAX_STEP; } public int getCurrentStep() { return currentStep; } public void setCurrentStep(int currentStep) { this.currentStep = currentStep; } ... }?currentStep ?maxStep 之外QWizardBean c还有几个其他的属性可用于保存用户提供的向导参敎ͼconnName?tt>connType?tt>userName?tt>password?tt>role?tt>driver?tt>hostName?tt>sid ?jdbcPort。该CZ应用E序与用h据无养I但在实际情况中,向导用它来配置数据库连接。WizardBean q实施了几个在用户单d导的按钮时将执行 JSF 操作。这些方法稍后将在本部分中进行介l?/span>
要创建您自己?BeanQ您可以?File 菜单中选择 New 来打开 New Gallery H口。然后,转至 General 中的 Simple Files cdQ在右侧面板中选择 Java ClassQƈ单击 OKQ?/span>
提供cd和程序包名称。然后单?OKQ创c:
在声明一个字D之后(例如 private int currentStepQ,右键单击其名Uƈ选择 Generate Accessors。单?OKQ生?get ?set ҎQ?/span>
创徏可重用表单?/strong>向导的主表单编写ؓ可重用标记文件。第一个表?(form1.tag) 包含一个文本域和一个下拉列表:
<!-- form1.tag --> <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %> <af:inputText id="connName" required="true" columns="40" label="Connection Name:" value="#{wizard.connName}" readOnly="#{wizard.currentStep == wizard.maxStep}"/> <af:selectOneChoice id="connType" required="true" label="Connection Type:" value="#{wizard.connType}" readOnly="#{wizard.currentStep == wizard.maxStep}"> <af:selectItem label="Oracle (JDBC)" value="Oracle_JDBC"/> </af:selectOneChoice>W二个表?(form2.tag) 包含三个文本域:
<!-- form2.tag --> <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %> <af:inputText id="userName" required="true" columns="40" label="User Name:" value="#{wizard.userName}" readOnly="#{wizard.currentStep == wizard.maxStep}"/> <af:inputText id="password" required="true" columns="40" label="Password:" value="#{wizard.password}" secret="true" readOnly="#{wizard.currentStep == wizard.maxStep}"/> <af:inputText id="role" required="false" columns="40" label="Role:" value="#{wizard.role}" readOnly="#{wizard.currentStep == wizard.maxStep}"/>W三个表?(form3.tag) 与其他两个表单类|
<!-- form3.tag --> <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %> <af:selectOneChoice id="driver" required="true" label="Driver:" value="#{wizard.driver}" readOnly="#{wizard.currentStep == wizard.maxStep}"> <af:selectItem label="thin" value="thin"/> <af:selectItem label="oci8" value="oci8"/> </af:selectOneChoice> <af:inputText id="hostName" required="true" columns="40" label="Host Name:" value="#{wizard.hostName}" readOnly="#{wizard.currentStep == wizard.maxStep}"/> <af:inputText id="sid" required="true" columns="40" label="SID:" value="#{wizard.sid}" readOnly="#{wizard.currentStep == wizard.maxStep}"/> <af:inputText id="jdbcPort" required="true" columns="40" label="JDBC Port:" value="#{wizard.jdbcPort}" readOnly="#{wizard.currentStep == wizard.maxStep}"/>正如您可能已l注意到的那P三个标记文g中没有一个包含了用于排列 Oracle ADF Faces lg的标记。不含Q何布局标记使得您可以独立地使用表单标记Q或在确认页面中l合它们。Oracle ADF Faces 提供了一个名UCؓ <af:panelForm> 的强大组Ӟ它将自动执行布局。除了主要的lg之外Q表单通常包含有其他的标记Q例?<h:messages globalOnly="true"/> ?<af:objectLegend name="required"/>。所有这些标记都可以集中在一个名?formTemplate.tag 的标记文件中Q?/span>
<!-- formTemplate.tag --> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %> <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %> <h:panelGrid columns="1" border="0" cellspacing="5"> <h:messages globalOnly="true"/> <af:objectLegend name="required"/> <af:panelForm> <jsp:doBody/> </af:panelForm> </h:panelGrid>使用 pageTemplate.tag ?formTemplate.tag ?JSF 面类gQ?/span>
<%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %> <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %> <tags:pageTemplate step="..." title="..."> <af:form id="..."> ... <tags:formTemplate> <tags:form123/> </tags:formTemplate> ... </af:form> </tags:pageTemplate>在前面的代码D中Q?tt><tags:form123/> 代表向导的三个主表单中的L一个(<tags:form1/>?tt><tags:form2/> ?<tags:form3/>Q。除了这些表单的lg之外Q?tt><af:form> 可能包含其他?JSF ?Oracle ADF Faces lgQ例如按钮)。下一D介l了使用 <tags:pageTemplate>?tt><tags:formTemplate>?tt><tags:form1>?tt><tags:form2> ?<tags:form3> 的应用程序页面。这些具体的例子充分说明了利用可重用标记文g构徏 JSF 用户界面的实际好处?
向导风格的界面?/strong>Z Web 的向导的面包含标Cؓ Back、Next ?Finish 的按钮。可以在一个名?stepButtons.tag 的标记文件中定义q些按钮Q?/span>
<!-- stepButtons.tag --> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %> <af:panelButtonBar> <af:singleStepButtonBar selectedStep="#{wizard.currentStep}" maxStep="#{wizard.maxStep}" previousAction="#{wizard.previousAction}" nextAction="#{wizard.nextAction}"/> <c:if test="${wizard.currentStep == wizard.maxStep}"> <af:commandButton text="Finish" action="#{wizard.finishAction}"/> </c:if> </af:panelButtonBar>WizardBean cd含当用户单击按钮时将执行的操作方法:
package webreuse; public class WizardBean implements java.io.Serializable { ... public String previousAction() { if (currentStep <= 1) return null; else { currentStep--; return "step" + currentStep; } } public String nextAction() { if (currentStep >= getMaxStep()) return null; else { currentStep++; return "step" + currentStep; } } public String finishAction() { currentStep = 0; return "finished"; } ... }操作Ҏq回的结果将?faces-config.xml 文g的导航规则中使用Q?/span>
<faces-config> ... <navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>step1</from-outcome> <to-view-id>/step1.jsp</to-view-id> </navigation-case> </navigation-rule> ... <navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>step4</from-outcome> <to-view-id>/confirm.jsp</to-view-id> </navigation-case> </navigation-rule> <navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>finished</from-outcome> <to-view-id>/index.jsp</to-view-id> </navigation-case> </navigation-rule> ... </faces-config>除了所有可见的lg之外Q向导页面还包含一个与 WizardBean ?currentStep 属性绑定的隐藏字段。您已经看到?pageTemplate.tag 在每一ơ执行页面时讄该属性。然而,用户可能单击览器的后退按钮。作操作的结果,在浏览器中看到的当前步骤与 currentStep 属性的gW,因ؓ览器将从其~存中检索到面Q而不是请求执?JSF 面?/span>
如果每一ơ用户单L钮时 currentStep 值都与表单数据一h交,则不会导致Q何问题。hiddenData.tag 文g?currentStep 作ؓ一个隐藏字D包含在内。此外,如果 currentStep {于 maxStepQ这意味着标记在确认页面中使用Q,那么该标记文件将为所?Bean 属性生成隐藏字D:
<!-- hiddenData.tag --> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %> <h:inputHidden id="currentStep" value="#{wizard.currentStep}"/> <c:if test="${wizard.currentStep == wizard.maxStep}"> <h:inputHidden id="h_connName" value="#{wizard.connName}"/> <h:inputHidden id="h_connType" value="#{wizard.connType}"/> <h:inputHidden id="h_userName" value="#{wizard.userName}"/> <h:inputHidden id="h_password" value="#{wizard.password}"/> <h:inputHidden id="h_role" value="#{wizard.role}"/> <h:inputHidden id="h_driver" value="#{wizard.driver}"/> <h:inputHidden id="h_hostName" value="#{wizard.hostName}"/> <h:inputHidden id="h_sid" value="#{wizard.sid}"/> <h:inputHidden id="h_jdbcPort" value="#{wizard.jdbcPort}"/> </c:if>所有的向导部分都可以在 JSF 面中组装,?step1.jspQ?/span>
<!-- step1.jsp --> <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %> <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %> <tags:pageTemplate step="1" title="Create Database Connection - Step 1"> <af:form id="form1"> <tags:formTemplate> <tags:form1/> </tags:formTemplate> <tags:stepButtons/> <tags:hiddenData/> </af:form> </tags:pageTemplate>step2.jsp ?step3.jsp 面?step1.jsp 非常cM。作为练习,您可以尝试ؓq些面构徏一个模板,从而将q些面都减ؓ四行代码。confirm.jsp 面一hC所有三个表单,但组件在只读模式下工作,从而占用的屏幕I间更少q且无需用户交互Q?/span>
<!-- confirm.jsp --> <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %> <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %> <tags:pageTemplate step="4" title="Confirm Connection Parameters"> <af:form id="form4"> <tags:formTemplate> <tags:form1/> <tags:form2/> <tags:form3/> </tags:formTemplate> <tags:stepButtons/> <tags:hiddenData/> </af:form> </tags:pageTemplate>Z菜单的界面?/strong>假定用户逐步览向导的页面,提供所有需要的信息。如果用户需要在以后修改某些地方Q那么他应当不需要再ơ浏览所有的向导面。相反,用户界面让用户直接转至必须修改信息的表单。menuTabs.tag 文g使用相同名称?Oracle ADF Faces lg来构单:
<!-- menuTabs.tag --> <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %> <af:menuTabs> <af:commandMenuItem text="Name and Type" action="tab1" selected="#{wizard.currentStep == 1}"/> <af:commandMenuItem text="Authentication" action="tab2" selected="#{wizard.currentStep == 2}"/> <af:commandMenuItem text="Connection" action="tab3" selected="#{wizard.currentStep == 3}"/> </af:menuTabs>菜单的导航规则在 faces-config.xml 中定义:
<faces-config> ... <navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>tab1</from-outcome> <to-view-id>/tab1.jsp</to-view-id> </navigation-case> </navigation-rule> ... <navigation-rule> <from-view-id>*</from-view-id> <navigation-case> <from-outcome>tab3</from-outcome> <to-view-id>/tab3.jsp</to-view-id> </navigation-case> </navigation-rule> ... </faces-config>当用户单击菜单的标签Ӟ表单数据被提交l?Web 服务器,在该服务器上 JSF 框架更?Backing Bean。如果用h更新表单而不改变当前的标{,那么需要用提交按钮。submitButton.tag 文g提供了提交按钮:
<!-- submitButton.tag --> <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %> <af:commandButton id="command" text="Submit" action="submitAction"/>tab1.jsp、tab2.jsp ?tab3.jsp 文g把菜单附加到向导的表单上。下面是 tab1.jspQ?/span>
<!-- tab1.jsp --> <%@ taglib prefix="af" uri="http://xmlns.oracle.com/adf/faces" %> <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %> <tags:pageTemplate step="1" title="Edit Database Connection"> <af:form id="form1"> <tags:menuTabs/> <tags:formTemplate> <tags:form1/> </tags:formTemplate> <tags:submitButton/> <tags:hiddenData/> </af:form> </tags:pageTemplate>使显C逻辑可重?/span>
如果您必M?JSF ?Oracle ADF Faces 从头开始构建新的页面,那么一切都没什么问题。但是,对于包含了以 Java 代码形式存在的显C逻辑的旧 JSP 面Q该如何处理呢?l护q些面困难重重Qƈ且,若不显CZ码从 JSP 面中分d来,则无法对其进行重用。此外,Java 代码?HTML 标记不应混合在同一个页面中Q因 Java 开发h员和 Web 设计人员无法L地展开q行工作?/span>
JSF ?Oracle ADF Faces lg解决了多数情况下的此U问题,因ؓ Web 面用这两个框架提供的标记来构徏Q同时将 Java 代码攑ֈ?Backing Bean 中。JSTL 和其他的标记库也非常有用Q但有时您必d能?Java 代码来动态生成内宏V?/span>
一U好的解x案是使用 JSP 2.0 提供?Simple Tags API 来构建标记库。该 API 使您能够开发标记处理器c,但您必须l护一个单独的 XML 文gQ称为标记库描述W?(TLD)Q,该文件定义标记名U、它们的属性等。如果这听v来太复杂Q那么您可以单地?Java 代码Ud使用 JSP 语法的标记文件中Q让应用服务器生成标记处理器cd TLD 文g。让我们看一下名?oldCode.jsp ?JSP 面Q它混合?Java ?HTML。该面读取一个文本文Ӟ其\径将作ؓ一个请求参数提供)q显C文件的内容Q包括行P。当您构建演C应用程序ƈxCZ码时Q这非常有用?/span>
重要注意事项Q请勿在生环境中用本部分的示例(oldCode.jsp ?newCode.jspQ,因ؓ它们可能会泄漏应用程序的源代码?/span>
oldCode.jsp 面使用 java.io API 来读取文本文件。它在 JSP 面范围中ؓ每一行文本创Z个名?lineText ?lineNo 的变量。行号将?JSTL ?<fmt:formatNumber> 标记来进行格式化Q文本将通过 <c:out> 标记q行昄Q?/span>
<!-- oldCode.jsp --> <%@ page import="java.io.*" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <HTML> <HEAD> <TITLE>${param.path}</TITLE> </HEAD> <BODY> <c:if test="${empty param.path}"> <P>The <CODE>path</CODE> parameter wasn't specified. </c:if> <c:if test="${!empty param.path}"> <P><B><CODE>${param.path}</CODE></B> <% String path = application.getRealPath( request.getParameter("path")); BufferedReader in = new BufferedReader(new FileReader(path)); try { int lineNo = 0; String lineText; while ((lineText = in.readLine()) != null) { lineNo++; pageContext.setAttribute("lineText", lineText); pageContext.setAttribute("lineNo", new Integer(lineNo)); %> <fmt:formatNumber var="fmtLineNo" value="${lineNo}" minIntegerDigits="3"/> <PRE>${fmtLineNo} <c:out value="${lineText}"/></PRE> <% } } finally { in.close(); } %> </c:if> </BODY> </HTML>来自前一个页面示例的全部 Java 代码都可以移C个名?readTextFile.tag 的标记文件中。只需q行许修改Q您必须使用 <%@tag%> 指o?jspContext 隐式对象Q而不?<%@page%> ?pageContext。您q必M?JSP 指o来声明属性和变量Q?/span>
<!-- readTextFile.tag --> <%@ tag import="java.io.*" %> <%@ attribute name="path" required="true" %> <%@ variable name-given="lineText" scope="NESTED" %> <%@ variable name-given="lineNo" scope="NESTED" %> <% String path = application.getRealPath( (String) jspContext.getAttribute("path")); BufferedReader in = new BufferedReader(new FileReader(path)); try { int lineNo = 0; String lineText; while ((lineText = in.readLine()) != null) { lineNo++; jspContext.setAttribute("lineText", lineText); jspContext.setAttribute("lineNo", new Integer(lineNo)); %> <jsp:doBody/> <% } } finally { in.close(); } %>您可以在M需要逐行处理文本文g?JSP 面中?readTextFile.tag 文g。每一个页面都可以Ҏ本行执行M需要的操作Q因标记文g使用?<jsp:doBody/>Q从而允?JSP 面处理当前行的代码放?<tags:readTextFile> ?</tags:readTextFile> 之间。newCode.jsp 面的功能与旧样式的CZ相同Q但它没有?Java ?HTMLQ?/span>
<!-- newCode.jsp --> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %> <HTML> <HEAD> <TITLE>${param.path}</TITLE> </HEAD> <BODY> <c:if test="${empty param.path}"> <P>The <CODE>path</CODE> parameter wasn't specified. </c:if> <c:if test="${!empty param.path}"> <P><B><CODE>${param.path}</CODE></B> <tags:readTextFile path="${param.path}"> <fmt:formatNumber var="fmtLineNo" value="${lineNo}" minIntegerDigits="3"/> <PRE>${fmtLineNo} <c:out value="${lineText}"/></PRE> </tags:readTextFile> </c:if> </BODY> </HTML>正如您所见,修改旧的 JSP 面以便可以重用代码q不是很难。维护也变得更加Ҏ?/span>
随意重用
重复的代码或内容是最令h头疼的事情。有时它可能是一U容易的解决ҎQ但修改应用E序变得更加困难Q这意味着从长q来看,您将不能适应新的用户需求或快速地修复问题。在理想情况下,应用E序不应包含相同代码的多个版本,Web 内容不应被复制和_脓?/span>