Server-Side Includes是JavaServer体系结构的另一个高级特征,它使你可以在HTML文档中嵌入servlet。这样,你就可以利用一个或多个servlet来组成最终的HTML文档。
如前所述,Server-Side Includes使你可以在HTML文档中使用一个特别的servlet标记来嵌入servlet。这一功能是由一个特殊的内部servlet和一个特殊的后缀映射实现的。这个内部servlet处理servlet标记;而这个映射用来在某种类型的文件(典型的是.shtml文件)被请求时,调用Server-Side Includes。图5.1说明了Server-Side Includes的基本流程。下面列表进行了详细描述。
·客户端浏览器发出一个加载.shtml文件的请求。于是,Web服务器处理该请求并调用文件servlet以读入该文件。
·Web服务器检查其映射表的哪些前缀和后缀可以被匹配。在图5.1所示情况下。.shtml文件匹配了一个后缀映射,于是Web服务器就调用该Server-Side
Includes Servlet。
·这个Server-Side Includes Servlet(SSInclude)被加载和调用。Server-Side
Includes Servlet分析servlet读出的数据流并查找servlet标记。
·对每一个servlet标记,调用相应的servlet,被调用的servlet的输出与数据流合并并加入到其相应servlet标记处。
·Web服务器将组织好的HTML页面发送给客户。
下面的代码段就是Server-Side Includes Servlet要处理的servlet标记的语法:
<servlet name=SERVLET_NAME code=SERVLET.CLASS codebase=SERVLET_CODE_BASE INIT_PARAM1=VALUE1 INIT_PARAM1=VALUE2 INIT_PARAM1=VALUEn> <param name=PARAM1 value=PARAM_VALUE1 param name=PARAM1 value=PARAM_VALUE2 param name=PARAM2 value=PARAM_VALUEn> </servlet>
一旦Servlet-side Includes Servlet发现了servlet标记,它就会试图加载由“servlet name”域命名的servlet。如果找不到该servlet,或者没有设置name属性,就加载由“code”域和“codebase”域所确定的servlet类。所有其他的域都被传给servlet作为初始化参数。如果在servlet标记中还有一些param域,它们将会被解析并作为参数传给servlet。servlet可以在每一次被调用时通过servlet请求对象来访问这些参数。
为了说明servlet标记,我们来看一个十分简单的Server-Side Includes例子——Echo Sevlet标记。这个servlet会将所有在servlet标记中指定的初始化参数和参数返回给客户浏览器。图5.2列出了Echo Servlet标记servlet的源程序。
package javaservlets.samples; import javax.servlet.*; /** * <p>This is a simple server side include servlet that will * echo all of the initialization arguments and parameters specified * with the servlet tag of the .shtml document. */ public class EchoServletTag extends GenericServlet { /** * <p>Performs the servlet service * * @param req The request from the client * @param resp The response from the servlet */ public void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException { // Create a PrintWriter to write the response java.io.PrintWriter out = new java.io.PrintWriter(resp.getOutputStream()); // Get an enumeration of all of the initialization // arguments java.util.Enumeration initParms= getInitParameterNames(); // Process each argument while (initParms.hasMoreElements()) { // Get the initialization argument name String p = (String) initParms.nextElement(); // Get the value String s = getInitParameter(p); // Output the name and value out.println("Initialization argument " + p + "=" + s); } out.println(); // Get an enumeration of all of the parameters java.util.Enumeration parms = req.getParameterNames(); // Process each parameter while (parms.hasMoreElements()) { // Get the parameter name String p = (String) parms.nextElement(); // Get the value(s). Note that an array of Strings is // returned that may contain multiple values String s[] = req.getParameterValues(p); // Output the name and value(s) out.print("Parameter " + p + "="); if (s != null) { for (int i = 0; i < s.length; i++) { if (i > 0) { out.print(" AND "); } out.print(s[i]); } } out.println(); } // Wrap up out.flush(); out.close(); } /** * <p>Returns information about this servlet */ public String getServletInfo() { return "EchoServletTag - Simple Server Side Include example"; }图5.2 EchoServletTag.java代码清单
请注意,这里我使用的基类是GenericServlet类,而不是前面的例子所使用的HTTPServlet类。这更说明这个例子中我们不是执行任何HTTP相关的任务,而仅仅是取得初始化参数和输入参数并将它们输出到输出流中去由于我们并不知道这些初始化参数或输入参数的名字,所以我们只有用一个枚举对象来取得它们,然后一个一个地取得它们。
另外,还要注意这些输入参数的处理方式,星一个输入参数可能含有我个值,getParameterValue()方法会返回一个字符串数组。这可能会带来一些问题,因为你在访问一个数组元素的时候(如[0]),必须确保该值非空。
图5.3显示了EchoServleTag.shtml文件,我们用它来启用一个servet并指定servlet标记的值。
<html> <head> <title>Java Servlets Sample - EchoServletTag</title> </head> <body> <p>The following lists the initialization arguments and parameters specified in the servlet tag of the server side include file that generated this HTML page. <br><br> <hr> <br> <pre> <servlet code=javaservlets.samples.EchoServletTag myArg1=myValue1 myArg2=myValue2> <param name=myParm1 value=Hello> <param name=myParm1 value=World> <param name=myParm2 value=myParmValue2> </servlet> </pre> </body> </html>图5.3 EchoServletTag.shtml清单
与servlet不同,.shtml文件将被保存在你的文档的根目录中(比如wwwroot)。当我们从Web服务器上请求EchoServletTag.shtml文件时,该文件将会由文件servlet加载,而它的内容被返回给服务器。服务器能够识别出这些由.shtml结尾的文件,于是服务器调用Serve-Side Includes Servlet。Server-Side Includes Servlet分析数据流(也就是.shtml文件的内容),查找所有的servlet标记。一旦一个servlet标记被发现,相应的servlet就会被调用,而该servlet的输出被合并到输出流中,插入点就在这个servlet标记被发现的地方。值得注意的是servlet标记将不会存在于返回给客户浏览器的HTML页面中。图5.4显示了返回到浏览器的HTML源码,而图5.5显示了浏览器加载该页面后的显示结果。
<html> <head> <title>Java Servlets Sample - EchoServletTag</title> </head> <body> <p>The following lists the initialization arguments and parameters specified in the servlet tag of the server side include file that generated this HTML page. <br><br> <hr> <br> <pre> Initialization argument code=javaservlet.samples.EchoServletTab Initialization argument myarg2=myValue2 Initialization argument myarg1=myValue1 Parameter myParm2=myParmValue2 Parameter myParm1=Hello AND World </pre> </body> </html>图5.4 EchoServletTag.shtml最终清单
如果返回的初始化参数和输入参数没有保持它们在servlet标记中的顺序,你可不要奇怪,这是因为初始化参数和输入参数在内部是存储在一个哈希(Hash)表中,而哈希表对顺序没有做任何控制,也就是说,从哈希表中读出的数据的顺序总是随机的。
现在就来看看Server-Side Includes的很好的应用:为你的HTML文档格式化标准的页头和页脚。为了不在每一个HTML文档中都重复地编写页头和页脚,你可以很容易地为页头和页脚创建一个servlet,然后把它们用servlet标记嵌入到你的HTML文档中。这不但简化了HTML文档的编写,而且还使你可以集中地修改你页面的外观。
图5.6显示了标准页头servlet的源程序。
package javaservlets.samples; import javax.servlet.*; /** * <p>This is a simple server side include servlet that will * format the standard company HTML header. The title of the page * will be set to the value of the title property */ public class StandardHeader extends GenericServlet { /** * <p>Performs the servlet service * * @param req The request from the client * @param resp The response from the servlet */ public void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException { // Create a PrintWriter to write the response java.io.PrintWriter out = new java.io.PrintWriter(resp.getOutputStream()); // Get the title of the page. Set to empty string if // no title parameter was given String titles[] = req.getParameterValues("title"); String title = ""; if (titles != null) { if (titles.length > 0) { title = titles[0]; } } // Format the standard header out.println("<html>"); out.println("<head>"); out.println("<title>" + title + "</title>"); out.println("</head>"); out.println("<body>"); out.println("<img align=\"right\""); out.println(" src=\"images\\CompanyLogo.jpg\""); out.println(" alt=\"Company Logo\">"); out.println("<font size=\"4\" face=\"Arial\" color=\"red\">"); out.println("<br><br>"); out.println("<strong>" + title + "</strong></font>"); out.println("<br><hr><br>"); // Wrap up out.flush(); out.close(); }
请注意该页的标题将会通过“title”属性来设置,所以在.shtml的servlet标记中,必须给出“title”属性的值。
图5.7显示了标准页脚servlet的源程序。
package javaservlets.samples; import javax.servlet.*; /** *图5.7 标准页脚清单This is a simple server side include servlet that will * format the standard company HTML footer. */ public class StandardFooter extends GenericServlet { /** * <p>Performs the servlet service * * @param req The request from the client * @param resp The response from the servlet */ public void service(ServletRequest req, ServletResponse resp) throws ServletException, java.io.IOException { // Create a PrintWriter to write the response java.io.PrintWriter out = new java.io.PrintWriter(resp.getOutputStream()); // Format the standard footer out.println("<br><hr>"); out.println("<a href=\"mailto:karlmoss@mindspring.com\">"); out.println("<img src=\"images\\mailbox.gif\""); out.println(" alt=\"Mailbox\">"); out.println("</a>"); out.println("<font size=\"-1\">"); out.println("<i>Questions or Comments?</i></font>"); out.println("</body>"); out.println("</html>"); // Wrap up out.flush(); out.close(); }
现在就让我们用标准的页头和页脚servlet来工作吧。图5.8显示了一个使用了标准页头和页脚servlet的.shtml文件。
<!-- Standard Company Header --> <servlet code=javaservlets.samples.StandardHeader> <param name="title" value="Gollum's Fifth Riddle"> </servlet> <!-- end --> <dir> This thing all things devours;<br> Birds, beasts, trees, flowers;<br> Gnaws iron, bites steel;<br> Grinds hard stones to meal;<br> Slays king, ruins town,<br> And beats high mountain down.<br> </dir> <!-- Standard Company Footer --> <servlet code=javaservlets.samples.StandardFooter> </servlet> <!-- end -->图5.8 GollumsFifth.shtml清单
值得注意的是标准页头servlet的“title”属性的设置。我在servlet标记前后加入了一些注释,这样,我们就可以容易地看出那些HTML代码是由Server-Side Includes Servlet生成的了。图5.9显示的是浏览器中显示该HTML页面的效果。千万要牢记.shtml文件和任何图像都是从你的文档根目录(比如 wwwroot)中加载的。
图5.10显示了经过处理返回给浏览器的页面的HTML源代码。
<!-- Standard Company Header --> <html> <head> <title>Gollum's Fifth Riddle</title> <head> <body> <img align="right" src="images\CompanyLogo.jpg" alt="Company Logo"> <font size="4" face="Arial" color="red"> <br><br> <strong>Gollum's Fifth Riddle</strong></font> <!-- end --> <dir> This thing all things devours;<br> Birds, beasts, trees, flowers;<br> Gnaws iron, bites steel;<br> Grinds hard stones to meal;<br> Slays king, ruins town,<br> And beats high mountain down.<br> </dir> <!-- Standard Company Footer --> <br><hr> <a href="mailto:karlmoss@mindspring.com"> <img src="images\mailbox.gif" alt="Mailbox"> </a> <font size="-1"> <i>Questions or Comments?</i></font> <body> </html> <! --end-->图5.10 GollumsFifth.shtml的最终清单
在本章中,我们深入探讨了如何使用Server-Side Includes。Server-Side Includes为我们提供了通过使用特殊的servlet标记,在HTML文档中嵌入servlet的能力。servlet标记是由Server-Side Includes Servlet处理的。一旦一个servlet标记被找到,相应的servlet就会调用。而它产生的输出将会被合并到文档中并一起返回给浏览器。使用Server-Sicd Includes创建HTML文档更加简便,而且使你可以集中管理你的文档的一些共同的特性,比如页头和页脚。