[翻译]JavaBean组件与JSP技术结合
到目前为止,在JSP最佳实践系列里,我们已经讨论了相当一部分基础知识。在前面的两个章节里,你应该学会了如何使用JSP的include机制将网站以外的内容包含在页面或应用程序里。共有两种不同类型的include:静态的include命令和动态的jsp:include标记。
同时,我们还没有涉及到在父页面(在我们的例子中是网站的首页)和被包含内容之间的通信问题。而实际上这种情况是非常普遍的。当你开始建立一个真正的网站或者web应用程序时,通常你会需要这样的通讯机制,举个例子,你的网站可能会在首页产生一些标题一类的小段文字,这些文字要出现在页眉或页脚页面中。在这一部分里,你将学到如何在页面间传递数据,以及如何在被包含的页面中使用这些数据。
注:这部分的全部例子都基于JavaServer Pages技术,为了能运行它们,你需要建立一个JSP兼容的web容器,在你本地计算机或某个用于测试的服务器上都可以。同时,你还要有一个文本编辑器或者集成环境来编写你的JSP页面。
用于保存数据的JavaBean
让我们假想这样的一个网站,在这个网站里每个页面有一小句“口号”(例如“书:装满知识的容器”或者“唱片:值得一听”)和一个标题。父页面(有时也被称作主页面)决定了每一页的口号,但真正产生html输出这个口号的是页眉页面,它是被包含的。要实现这个目的,父页面必须能够把口号传递给页眉页面,页眉页面得到这个口号,将它以页面标题的形式输出。
首先我们需要某种对象来保存被传递的数据,恰好(这并非偶然)JavaBean组件就是这样一个合适的选择,它与JSP技术配合得天衣无缝,简单的使用取值方法和赋值方法就可以控制你要的数据。稍有java编程经验的读者可能已经想到,get()就是一个取值方法,因为它读取数据;而set()是一个赋值方法,因为它改变数据。
列表1展示了我们所需要的一个JavaBean的代码,PageHeaderinfo包含了网站页眉信息。
package com.newInstance.site.beans;
import java.io.Serializable;
public class PageHeaderInfo implements Serializable {
/** The title of the page */
private String pageTitle;
/** The slogan of the page */
private String pageSlogan;
public String getPageTitle() {
return pageTitle;
}
public void setPageTitle(String pageTitle) {
this.pageTitle = pageTitle;
}
public String getPageSlogan() {
return pageSlogan;
}
public void setPageSlogan(String pageSlogan) {
this.pageSlogan = pageSlogan;
}
}
]]>
作为第一个练习,将这个文件保存为PageHeaderInfo.java并编译它。接下来,把得到的class文件PageHeaderInfo.class放在你的web应用程序的WEB-INF/classes目录下。请确保目录包含了包的结构,例如:
$<TOMCAT-ROOT>/webapps/$<WEB-APP-NAME>/WEB-INF/classes/com/newInstance/ site/beans/PageHeaderInfo.class
你可以使用类似这样的路径安排web应用程序中用到的类。按以上步骤做到这里,下面就可以向PageHeaderInfo里存入数据然后在不同的JSP页面中获取了。
传递已保存的数据
在我们的网站里,页眉页面包含向不同页面传递不同口号的代码。查看前面的章节就会发现,页眉页面(header.jsp)是通过jsp:include标记被包含的文件。列表2展示了网站首页如何通过标记把数据传递给header.jsp文件。
<%@ page language="java" contentType="text/html" %>
<html>
<head>
<title>newInstance.com</title>
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1" />
<link href="/styles/default.css" rel="stylesheet" type="text/css" />
</head>
<body>
<jsp:include page="header.jsp" flush="true">
<jsp:param name="pageTitle" value="newInstance.com"/>
<jsp:param name="pageSlogan"
value="Java and XML :: Turning theory into practice" />
</jsp:include>
<%@ include file="/navigation.jsp" %>
<jsp:include page="bookshelf.jsp" flush="true" />
<jsp:include page="/mt-blogs/index.jsp" flush="true" />
<%@ include file="/footer.jsp" %>
</body>
</html>
]]>
可以看出,标题是被传递过去作为口号的。
你可能已经注意到了,在你建立页面的时候,不一定需要JavaBean组件实际存在。我总是先写好JavaBean,有一个很好的理由:JSP参数名必须与JavaBean属性名匹配,先完成JavaBean可以保证你在编写JSP页面时使用合适的参数名称。
获得数据
当你完成了JSP参数和JavaBean属性的编码,一旦数据被传递给header.jsp,这个页面就可以开始获取数据了。列表3展示了header.jsp页面。它的大部分内容是html,请注意里面的JSP脚本,在你研究过这些代码后我会在后面向你解释。
<!-- Begin header section -->
<%@ page language="java" contentType="text/html" %>
<jsp:useBean id="pageHeaderInfo"
class="com.newInstance.site.beans.PageHeaderInfo">
<jsp:setProperty name="pageHeaderInfo" property="*" />
</jsp:useBean>
<table width="100%" border="0" cellspacing="0" cellpadding="0">
<tr>
<td width="91" height="50" align="right" valign="top"
bgcolor="#330066"><font color="#FFFFFF"><img
src="/images/header-lions.gif"
width="90" height="60"></font></td>
<td colspan="3" align="left" valign="top"
bgcolor="#000000"><table width="100%" height="60" border="0"
cellpadding="0" cellspacing="0">
<tr>
<td width="261" rowspan="2"><img
src="/images/header-title.gif" width="261" height="60"></td>
<td class="pagetitle" width="249" height="55" align="right"
valign="bottom"><jsp:getProperty name="pageHeaderInfo"
property="pageSlogan"/></td>
<td width="10" height="55"> </td>
</tr>
<tr>
<td height="5"><img src="/images/spacer.gif" width="1"
height="5"></td>
<td height="5"><img src="/images/spacer.gif" width="1"
height="5"></td>
</tr>
</table></td>
<td width="141" bgcolor="#000000">
<font color="#FFFFFF"> </font>
</td>
</tr>
<!-- End header section -->
]]>
第一行代码标识了该页面为一个JSP页面,然后通过jsp:useBean标记声明需要访问PageHeaderInfo这个JavaBean,id属性为这个bean指定了一个名称,通过该名称可以在JSP页面中使用bean;class属性指定了JavaBean类的全名。相邻的jsp:setProperty标记说明了JavaBean(通过id属性标识)的所有属性都以请求数据赋值,也就是说,为这个bean里的每个属性(例如pagetitle和pageslogan)寻找名称对应的请求参数来赋值。这些请求参数可以来自客户端的浏览器,也可以来自包含这一页的其他JSP页面。在这种情况下,唯一的请求数据是由父页面创建的。对于我们的网站,首页(index.jsp)发送pagetitle和pageslogan,其值分别为“newinstance.com”和“Java and XML: Turning theory into practice”。
一旦bean的属性被赋值后,页面就可以使用这些数据了。对于header.jsp,在后面的代码里可以看到,通过jsp:getProperty标记使用了这些数据。Jsp;getProperty标记通过name参数标识从哪个对象取数据,通过property参数标识取对象的哪一个属性。取得的数据值自动插入到页面的输出,也就是输出到父页面里,从而得到一个无缝的、动态的页面口号,在JSP页面间传递数据就是这么简单!你可以为一个JSP页面增加任意多的bean,每一个bean都可以有任意多的属性,足以应付任何复杂的请求数据。
处理需求变化
改变是软件开发者最大的烦恼,你写好了你的bean,在JSP页面里也写好了使用它们的代码,这时web应用程序的需求似乎不可避免的会发生变化。如果这个改变要求更多的属性(大多数情况都是如此),你不得不改写你的JavaBean源代码,重新编译它,还要确定JSP页面能够正确访问新生成的bean类。有些情况下,你可以不必对bean进行处理,如果保存或获取属性页面不再使用了(也就是说,即使那个页面还在站点的目录里,但你不再使用该页面),这时你可以不用去管原来的代码即可。实际上,我就遇到过这种情况!
我的个人网站的页眉页面header.jsp用于生成html的头部,以前有一段时间里,这一页向我的其他页面头部里的title标记里插入一个标题,后来我做了修改,在header.jsp里不再使用页面标题了。但我并没有从PageHeaderInfo里把pageTitle移除;实际上,我甚至在大部分JSP页面里连jsp:param标记都没有移除,这个标记的作用本来是为页面设置标题的。我认为花这些工夫不值得,因为我确信保留这些数据不会带来任何坏的影响(也许某一天我还会重新用到呢!)。因此,如果你遇到同样的情况,不用浪费时间了--有处理这些琐事的时间不如用来为你的web应用程序增加些新的、有趣的、实用的功能。
下一次
当你熟练掌握了在JSP页面间传递数据的方法后,试着自己写一些有用的JavaBean并且看看能不能把它们用在你的站点里。通过和这些bean的接触,还有jsp:useBean、jsp:param以及jsp:get/setProperty的使用,你应该能够做出一些很酷的功能了!在下次的最佳实践里,我将向你展示使用JSP向站点增加外部内容的方法,JSTL标记和我们熟悉的include标记差不多,它使得JSP更灵活和更高效。在这之前,请用功准备,到时再见!
(这篇文章是一年前翻译的,当时想找翻译的活做,也是未果。请参考英文原文。)