servlet最佳实现.doc_第1页
servlet最佳实现.doc_第2页
servlet最佳实现.doc_第3页
servlet最佳实现.doc_第4页
servlet最佳实现.doc_第5页
已阅读5页,还剩17页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

44第三章Servlet 最佳实践Jason Hunter自1996 年引入以来,Servlet 已经占据了服务器端Java 领地,并且成为Java 介入Web的一种标准方法。作为一种基本技术,Java 开发人员以此为基础来构建Web应用以及(更进一步的)Web服务。这一章将讨论基于Servlet 的开发和部署的最佳实践。有效使用Servlet首先来介绍Servlet 框架。框架(如,Apache Strut)越来越普及,这是因为通过提供一种“骨架”(skeleton,应用可在此基础上构建),框架能够提高程序员的效率。在第一节中,我们将分析Servlet框架提供了什么并对最流行的框架作一个简要概述;然后,将讨论使用预编码字符可以如何优化Servlet 的性能。接下来,我们将处理加载配置文件这一棘手的问题,并提供一些代码从而使这个任务更容易完成; 其后,我将提供一些有关何时应当使用( 以及何时不应当使用)HttpSession和SingleThreadModel特性的技巧。本章结尾部分,我将解释如何可靠地控制缓存,以提高用户的体验。然后将解决一个常见问题:“如何将文件下载到客户,从而让客户看到一个保存为(Save As)弹出对话框?”,你将看到,答案就在于设置合适的HTTP 首部。Servlet 最佳实践45选择合适的Servlet 框架编写Web 应用时,应当记住Servlet 是一种功能强大的技术。这一点很容易被遗忘,因为最初Servlet API 即为服务器端Java Web 编程的全部。至于Servlet API不包括的某些内容,就必须由我们自行构建。这有点类似于西部片中的情景,当时情况相当恶劣,真正的程序员需要自己动手来编写Servlet,而且有关规范尚未编写。仅仅有out.println()就令我们颇感欣慰了。时光荏苒,斗转星移。正如西部原野上出现了大批牛羊,在这一领域中也涌现了许多新的内容,我们看到,大量基于Servlet 的技术被设计出来,从而使Web 应用开发更加容易而且更为有效。发生变革的第一个领域为表示层。较之于以前应用很广的out.println(),诸如JavaServer Pages(JSP)、WebMacro和Velocity等技术则提供了输出更为丰富的方法。与以往相比,利用这些技术使得快速地开发、部署和维护动态Web内容更加容易。有关这些技术以及其他模板技术的全面讨论,请参见我所著的Java Servlet Programming第二版(OReilly 出版)。今天,我们发现在表示层之下出现了一个产生变革的新领域,即框架层(如图3-1 所示)。这些新的框架提供了一个可靠的支架,基于这个支架可以构建新的Web应用,从而由快速构建页面转移到快速构建完整的应用。框架采纳了专家们最优秀的设计,使你能够重用这些设计。好的框架有助于改善应用的模块性和可维护性。框架还将多种单独的技术集合在一个捆绑的包中,并提供了基于这些技术构建的组件以解决常见的任务。如果你选择了合适的Servlet框架,不但可以大大地提高你的工作量,并且可以利用他人的工作。因此,我建议你考虑使用一个框架,而且我在这一节中还将提供一些关于选择合适的框架的有用技巧。选择框架的技巧选择一个Servlet框架时,考虑其特性表相当重要。下面列出了框架提供的一些特性。并非所有框架都支持以下特性,另外这一简短的列表也并非详尽的(注1)。注1: 这实际上是一个正在进行的研究项目,其目标是跟踪Servlet框架特性,并且在各个框架上实现同样的演示Web 应用。更多的信息请参见http;/。46 第三章Servlet 框架JSP WebMacro VelocityMVC 类Servlets等等安全性表单验证数据库错误处理集成图3-1:Servlet、模板技术和框架与模板语言的集成有些框架集成了一个特定模板语言,另外一些框架则有一个“可插拔”的模型以支持多个模板,不过它们通常都针对一种模板语言加以优化。如果你偏好某种特定的模板语言,请确保所选框架能够很好地提供相应的支持。支持(理想情况下则可增强)设计人员/ 开发人员的分离Web应用开发的一个通常目标是将开发人员的责任从设计人员的责任中有效地分离出来。对于这一目标,选择适当的模板语言可以提供帮助,但是选择合适的框架则会有更大的影响。有一些框架可以支持这种分离,还有一些甚至可能增强。安全集成默认的Servlet 访问控制和安全模型可以用于简单任务,但未能针对更为高级的需求做相应扩展。有些框架提供了另外的安全模型,而且许多框架还支持“可插拔”的安全模型。如果你希望获得更为高级的安全控制,那么选择合适的框架将有所帮助。表单验证框架通常提供了验证表单数据的工具,例如,在Servlet 看到数据之前允许框架对参数进行全面检查。有些框架使得我们可以很容易地开发“表单范例”(带有“向后”/“向前”按钮和所维护的状态)。Servlet 最佳实践47错误处理有些框架包括高级或定制的错误处理,如发送警告电子邮件、将错误记录在一个特殊数据存储中,或者是自动将错误格式化并提交给用户和(或)管理人员。持久/ 数据库集成框架的一个最强大的特性可能是它们与后台数据存储(如数据库)的紧密而良好的集成。这些框架使用户能够以对象而不是SQL 的方式来考虑问题。国际化国际化(Internationalization,i18n)往往是一个难题,但是有些框架拥有一些特性和习惯用法,可以简化这个过程。IDE 集成有些框架为开发提供了IDE,而且(或者)具有允许插入到第三方IDE 的特性。支持Web 服务的机制由于人们对Web 服务的兴趣日渐增长,若发现有关Web 服务的新框架,或者原有框架引入了新的Web 服务特性,这都是很常见的。除了特性以外,分析框架时的第二条重要原则是其授权许可。我的建议是要坚持采用开放源代码的项目,或者由多个开发商实现的标准。这样可以保护你的投入。开放源代码和通用标准都能够避免单一开发商问题,而且,可以确保任何一方都不能终止对你的应用所基于的框架的支持。第三个需要考虑的是框架所面向的目标(例如,新闻网站、门户网站、商业网站等等)。不同的网站有不同的需求,而一个框架往往针对某一市场环节加以优化。你可能会发现,调查其他人在实现类似应用时采用了哪种框架将很有帮助。声名鹊起的框架尽管在此对框架做全面的比较将很有意思,但这并不是本书的主旨。我们将简要地讨论4 种目前最为流行的Servlet 框架,它们分别是:J2EE BluePrints、ApacheStruts、JavaServer Faces 和Apache Turbine。48 第三章你可能会暗自思量:“直截了当地告诉我哪一种最好吧!”。遗憾的是,并没有一个包罗万象的答案;这完全取决于你的应用和你的个人喜好。使用服务器端Java时,正好可以沿用Perl 的名言“条条大路通罗马”。J2EE BluePrintsJ2EE BluePrints(/blueprints/enterprise)与其说是一个框架,不如说是一个指南更为确切。这本书由Sun 工程人员编著,提供了准则、模式和代码示例,从而展示了如何最佳地使用J2EE 及其组成技术。例如,这本书说明了如何实现一个MVC框架,该框架将后台Web 操作封装为3部分,分别为:表示核心数据的模型(Model)、处理数据显示的视图(View)以及处理数据调整的控制器(Controller)。为了支持此MVC模型,BluePrints建议以“命令” (Command)模式风格使用Action 类:示例应用定义了一个抽象类Action,它表示一个应用模型操作。控制器可以通过名字查找具体的Action 子类,并将请求委托给这些子类。这本书对于如何实现一个Action提供了代码示例,但是没有提供任何可供发布的代码。若需要发布代码,J2EE BluePrints 建议读者参考ApacheStruts。Apache StrutsApache Struts(http:/J/struts)可算是最流行的Servlet 框架。它与BluePrints 所述的MVC 模式非常接近(在我看来,这两种想法有着同样的思路):Struts 具有高可配置性,而且有相当多的特性(其特性仍在增加),其中包括Front Controller(前端控制器)、动作类及映射、面向XML 的实用类、服务器端JavaBean 的自动填充、带有效性验证的Web 表单以及一些国际化支持。它还支持一组定制标记以访问服务器端状态、创建HTML、完成表示逻辑以及模板化。有些开发商已经开始采用并宣传Struts。Struts 得到了大量的关注,可以认为它是适于大型应用的具有工业强度的框架。在Struts 中,请求通过一个控制器Servlet 传输。Action 对象控制请求Servlet 最佳实践49的处理,而且这些动作使用诸如JavaBeans 等组件来完成业务逻辑。对于有外部配置的Servlet,Struts 在其之上极好地创建了一个完全分派机制,从而消除了URL 和在线行为间的人为联系。几乎所有请求都通过同一个Servlet进入,客户请求作为请求的一部分,指示了其希望采取的动作(即,登录、加入购物车、结账等),Struts 控制器将把请求分派给一个Action处理。JSP被用作表示层,尽管也可以使用Apache Velocity 和其他技术。Struts 是一个开放源代码项目,是在Apache 的开放协作开发模型下开发的。JavaServer FacesJavaServer Faces(JSF)是Sun 领导的一个Java Community Process(JCP)项目(JSR-127),目前仍处在早期开发阶段。在写这本书时,它刚刚达到Community Review 的最早阶段,但是已经得到了相当的关注。JSF 立项文档中原计划定义一个标准Web应用框架,但是目前提交的文档所关注的目标却较为受限,仅仅为请求定义一个包括多个阶段请求处理生命期(即一个表单向导)。与Struts 很好地集成也是JSF 的一个目标。Apache TurbineApache Turbine 可能是最古老的Servlet 框架之一,它自1999 年就已经存在了。此框架拥有处理参数解析和验证、连接池、作业调度、缓存、数据库抽象以及XML-PRC 等服务。其许多组件都可以独立使用,如对数据库抽象的Torque 工具。Turbine 将这些组件“捆绑”在一起,从而为构建Web 应用提供了一个可靠的平台,这与J2EE 为企业应用所采用的方式完全一致。Turbine 与其他框架类似,也基于MVC模型和动作事件抽象。不过,不同于其他框架,Turbine在视图(View)层提供了额外的支持,而且自称是“Model2+1”,即优于标准的“Model 2”MVC。Turbine Views 支持许多模板引擎,不过Apache Velocity 为其首选。如果篇幅允许,我们还可以讨论更多的框架。如果你有兴趣学习更多的内容,可以用以下关键字在Google 上查找:TeaServlet、Apache Cocoon、EnhydraBarracuda、JCorporate Expresso 和Japple。50 第三章使用预编码字符编写Servlet 时你了解到的第一件事就是要用PrintWriter 写字符,而用OutputStream来写字节。尽管这在形式上是一个很好的建议,但稍有些简化。需要全面地看待这一事实,因为输出字符并不意味着必须使用一个PrintWriter!PrintWriter有其缺点,具体地说,它必须在内部将char中的各个字符编码为一个byte序列。如果有已知编码的内容(如,文件、URL 或数据库,甚至是内存中保存的一个String中的内容),那么通常最好采用流。这样就可以完成一个直接的字节- 字节传输。除了所保存编码和所需编码之间偶尔会存在字符集不匹配这种少有的情况外,没有必要先将内容解码至一个String,然后再在发送至客户时将其编码为字节。使用预编码字符则可节省大量开销。作为演示,例3-1 中的Servlet 使用了一个读取器(reader)来读取一个文本文件,并使用了一个书写器(writer)向客户输出文本。尽管它遵循了对于文本使用Reader/Writer 类的原则,但是却存在非常浪费而且毫无必要的转换。例3-1:读入字符,输出字符import java.io.*;import java.util.prefs.*;import javax.servlet.*;import javax.servlet.http.*;public class WastedConversions extends HttpServlet / 随机文件,仅用于演示String name = content.txt;public void doGet(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException String file = getServletContext().getRealPath(name);res.setContentType(text/plain);PrintWriter out = res.getWriter();returnFile(file, out);Servlet 最佳实践51public static void returnFile(String filename, Writer out)throws FileNotFoundException, IOException Reader in = null;try in = new BufferedReader(new FileReader(filename);char buf = new char4 * 1024; / 4K 字符缓冲区int charsRead;while (charsRead = in.read(buf) != -1) out.write(buf, 0, charsRead);finally if (in != null) in.close();例3-2 中的Servlet 更适于返回一个文本文件。此Servlet 识别出文件内容是由字节开始的,而且只要其编码与客户所需的编码相匹配,就可以直接作为字节发送。例3-2:读入字节,输出字节import java.io.*;import java.util.prefs.*;import javax.servlet.*;import javax.servlet.http.*;public class NoConversions extends HttpServlet String name = content.txt; / 要发送的演示文件public void doGet(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException String file = getServletContext().getRealPath(name);res.setContentType(text/plain);OutputStream out = res.getOutputStream();returnFile(file, out);public static void returnFile(String filename, OutputStream out)throws FileNotFoundException, IOException InputStream in = null;try in = new BufferedInputStream(new FileInputStream(filename);52 第三章byte buf = new byte4 * 1024; / 4K 缓冲区int bytesRead;while (bytesRead = in.read(buf) != -1) out.write(buf, 0, bytesRead);finally if (in != null) in.close();通过使用预编码字符对于性能有多大的改善呢?这取决于服务器。对一个2MB文件进行本地访问,以此来测试这两个Servlet,在Tomcat 3.x 下性能有20% 的提高;Tomcat 4.x 下则有50% 的大幅提高。尽管这些数字让人印象非常深刻,但它们当然有一个假设,即应用除了传输文本文件之外不做其他工作。实际的性能提高则取决于Servlet 的业务逻辑。此技术(如图3-2 所示)对于带宽受限或服务器CPU 受限的应用尤其有用。字节字节字符浏览器服务器文件字符字节字节图3-2:充分利用预编码字符只要大多数源内容都是预编码的(如,文件、URL,甚至数据库内容),那么“使用预编码字符”这个原则就适用。例如,使用ResultSet getAsciiStream()方法而不是getCharacterStream()可以避免对于ASCII 字符串的转换开销,无论在读取数据库还是写至客户时都适用。还有可能将服务器和数据库之间的带宽减至一半,因为ASCII流的规模大致为UCS-2流的一半。具体能够得到多大收益,当然还要取决于数据库以及它在内部如何保存和传输数据。实际上,一些Servlet开发人员用String.getBytes()对其静态String内容进行预编码,这样只需对它们进行一次编码。采用这种极端做法所得到的性能收益是Servlet 最佳实践53否值得,这仍取决于个人喜好。我建议,只有当性能是一个很重要的问题,而且再没有更简单的解决方案时才采用这样的做法。输出混合字节和字符实际上比想象得要容易。例3-3 展示了如何使用Servlet-OutputStream及其组合方法write(byte)和println(String)来混合输出类型。例3-3:ValueObjectProxy.javaimport java.io.*;import java.sql.*;import java.util.Date;import javax.servlet.*;import javax.servlet.http.*;public class AsciiResult extends HttpServlet public void doGet(HttpServletRequest req, HttpServletResponse res)throws ServletException, IOException res.setContentType(text/html);ServletOutputStream out = res.getOutputStream();/ServletOutputStream 中包含println()方法用以写字符串/println()调用仅适用于单字节字符编码/ 如果需要多字节,请确保在Content-Type(内容类型)中设置了字符集/ 而且使用了相应的方法,例如,对于日语使用out.write.(str.getBytes(Shift_JIS)out.println(Content current as of);out.println(new Date().toString();/ 在此获取一个数据库结果集(ResultSet)try InputStream ascii = resultSet.getAsciiStream(1);returnStream(ascii, out);catch (SQLException e) throw new ServletException(e);public static void returnStream(InputStream in, OutputStream out)throws FileNotFoundException, IOException byte buf = new byte4 * 1024; /4K 缓冲区int bytesRead;54 第三章while (bytesRead = in.read(buf) != -1) out.write(buf, 0, bytesRead);尽管混合字节与字符将带来性能的大幅提升,因为字节可直接传输,我仍建议你少用这种技术,因为这不仅会让读者感到混乱,而且如果你对字符集如何工作并没有搞得滚瓜烂熟,那么就很容易导致错误。如果还需要ASCII 字符集以外的字符,就要确保你明白自己要做什么。初学者不要尝试向输出流写非ASCII 字符。从类路径加载配置文件从Servlet API 1.0 到Servlet API 2.3,Servlet 显然一直缺少一种获取外部配置文件的标准机制。尽管许多服务器端库需要配置文件,但Servlet 却没有一种大家公认的定位配置文件的方法。Servlet 在J2EE 下运行时,可得到对JNDI 的支持,而后者可以提供一定的配置信息。不过,常见的Web服务器配置文件问题仍然存在。最好的解决方案(我也许应当称之为“危害最小的”解决方案)是利用查找类路径和(或)资源路径定位文件。这要求服务器管理人员将服务器端配置文件置于Web 服务器的类路径中,或者将每个应用的配置文件置于从资源路径找到的WEB-INF/classes 中。对于定位置于WAR文件中的配置文件以及(或者)定位已部署在多个后台Servlet 容器上的配置文件,此方法同样有效。实际上,即便可以使用JNDI,使用用于配置的文件仍有诸多好处。一个配置文件可以在整个服务器上都有效。而且最后一点,配置文件对于开发和部署人员都很容易理解。例3-4 展示了利用一个名为Resource 的类的查找技术。给定一个资源名,Resource构造函数将搜索类路径和资源路径以定位资源。一旦找到资源,则资源内容以及其目录位置和最后修改时间均可用(如果可以得到这些信息的话)。最后修改时间有助于应用了解一些信息,例如,何时再次加载配置数据。此类使用了特残代码将file:URL 资源转换为File对象。这的确很方便,因为URL(甚至file:URL)通常并不提供诸如修改时间等特殊特性。通过查找类路径和资源路径,该类则可以找到服务器端资源和各个应用的资源。此类的源代码还可以从http:/www. 下载。Servlet 最佳实践55例3-4:一个标准Resource 定位器import java.io.*;import .*;import java.util.*;/* 用于定位资源、获取其内容并确定其最后修改时间的类。* 为了找到资源,此类首先查找CLASSPATH,* 然后再执行Resource.class.getResource(/+name)。* 如果Resource 找到了一个“file:”URL,则此文件路径将被当作一个file 处理。* 否则,此路径将被作为一个URL 处理并且最后修改信息有限。*/public class Resource implements Serializable private String name;private File file;private URL url;public Resource(String name) throws IOException = name;SecurityException exception = null;try / 使用CLASSPATH 查找。如果找到,设置“file”,并且调用返回true。/ 可能会出现一个SecurityException 异常。if (tryClasspath(name) return;catch (SecurityException e) exception = e; / 保存留用try / 使用classloader getResource()查找。如果作为文件找到,/ 则设置“file”;如果作为URL 找到,则设置“url”if (tryLoader(name) return;catch (SecurityException e) exception = e; / 保存留用/ 如果到此,说明存在问题,报告异常。String msg = ;56 第三章if (exception != null) msg = : + exception;throw new IOException(Resource + name + could not be found in +the CLASSPATH ( + System.getProperty(java.class.path) +), nor could it be located by the classloader responsible for the +web application (WEB-INF/classes) + msg);/* 返回传递给构造函数的资源名*/public String getName() return name;/* 返回一个输入流以读取资源内容*/public InputStream getInputStream() throws IOException if (file != null) return new BufferedInputStream(new FileInputStream(file);else if (url != null) return new BufferedInputStream(url.openStream();return null;/* 若此资源为最后修改的,则返回。如果发现资源使用了一个URL,* 那么仅当URL 连接支持最后修改信息时此方法才可正常工作。* 如果不支持,将返回long.MAX_VALUE。* 也许这应当返回1,不过基于以下假设应当返回MAX_VALUE,* 即如果你无法确定时间,那么* 它就是最新的。*/public long lastModified() if (file != null) return file.lastModified();else if (url != null) try return url.openConnection().getLastModified(); / 成功catch (IOException e) return Long.MAX_VALUE; Servlet 最佳实践57return 0; / 不可能发生/* 返回包含资源的目录,* 或者如果资源不能在文件系统中直接得到,则返回null。* 这个值可用于定位磁盘上的配置文件,或者在同一目录中写文件。*/public String getDirectory() if (file != null) return file.getParent();else if (url != null) return null;return null;/ 如果找到则返回trueprivate boolean tryClasspath(String filename) String classpath = System.getProperty(java.class.path);String paths = split(classpath, File.pathSeparator);file = searchDirectories(paths, filename);return (file != null);private static File searchDirectories(String paths, String filename) SecurityException exception = null;for (int i = 0; i = config.lastModified() try 60 第三章return loadCache(new FileInputStream(cache);catch (IOException ignored) / 如果没有缓存文件或者它已经过期,则走到此进行,/ 要整个重新加载。从配置文件中找到原数据文件的名字,/ 并使用Resource返回其内容。Resource data = new Resource(props.getProperty(data.file);return loadData(data.getInputStream();private ConfigData loadCache(InputStream in) / 读取文件,可能将其读作一个串行化对象return null;private ConfigData loadData(InputStream in) / 读取文件,可能将其读作XMLreturn null;class ConfigData / 保存配置数据的一个示例类加载代码本身无需关心资源可能置于何处。如果需要,Resource类将查找类路径和资源路径,并且从WAR 找出。将会话看作一个本地缓存由HttpSession 实现的Servlet 会话提供了一个简单而且方便的机制来保存有关用户的信息。尽管会话是一个很有用的工具,但了解其局限性很重要。在实际应用中,尽管将其用作后台存储很有诱惑力,但这并不是一个好的选择。实际上,最好将会话看作是一种方便的本地缓存,即一个保存信息的地方,而且这些信息如果丢失也可以恢复或者可以安全地忽略。想要理解为什么,需要简要地回顾一下会话是如何工作的。会话通常使用cookie来识别用户。在客户首次请求服务器时,服务器会在客户上设置一个特殊的Servlet 最佳实践61cookie,其中包含服务器生成的惟一的ID。在以后的请求中,服务器可以使用此cookie 识别出请求是否来自于同一个客户。服务器保存有一个服务器端散列表,其中cookie ID 键与HttpSession对象值相关联。当一个Servlet 调用request.getSession()时,服务器得到cookie ID,查找匹配的HttpSession,并返回。为了保证内存受控,若一段时间没有操作(通常为30 分钟),会话将到期而且其保存的数据将作为垃圾回收,这一工作也可基于程序员的请求来完成。会话数据有着本质的临时性和脆弱性。当出现以下情况时会话数据都将丢失,即会话到期、客户关闭了浏览器(注2)、客户改变了浏览器、客户改变了所用机器或者Servlet验证会话不合法从而导致用户登出。相应地,会话最适于保存可以被忘记的临时信息,即要么是非持久性的,要么信息处理已得到一个实际存储库的支持。当信息需要持久存储时,建议使用一个数据库、数据库所支持的EJB 或者另外一种正式的后台数据存储。它们更为安全、可靠,也更具可移植性,而且更适于备份。如果数据必须与用户有长时间的关联,即便用户改换了机器也要保持关联,那么应使用一个真正的登录机制从而允许用户重新登录和重新关联。Servlet会话对于这些情况均有帮助,但是其作用应当仅限于本地缓存,对此我们将在下一节讨论。购物车的架构下面来看如何构造会话来跟踪一个购物车应用(可以考虑A)。以下是购物车的一些需求: 已注册用户有一个定制的应用界面 在浏览器关闭之前登录保持有效 用户可以登出而不会丢失购物车内容注2: 当然,浏览器关闭时会话数据并非立即丢失,这是因为并没有向服务器发出任何通知。不过,由于浏览器将丢失其会话cookie,因此会话数据也将丢失,而且一旦到期,服务器将把不用的会话数据都进行垃圾回收。62 第三章 增加到购物车中的商品可保留一个月 允许Guest 将商品放入购物车中(不过对于Guest 可在长时期内访问其内容或者从另一个浏览器进行访问则不作要求) 购买购物车中的商品需要口令以保证安全单由Servlet会话本身并不能充分满足这些要求。利用合适的服务器,你可能可以将会话保存一个月,但是当用户改换机器时,信息就会丢失。如果试图将会话用作存储机制,那么在一个月之后必须逐个地将各个商品置为到期(而不是整个会话),而同时要确保会话中未置入任何不应无限期保存的内容,这是很令人头疼的,另外你还需要一种登出用户而不会使其购物车内容失效的方法。在API 2.3中这是无法做到的!以下是此应用的一个可行的架构,其中充分利用了将会话用作本地缓存的好处:如果用户未注册,则是一个Guest,而且会话将保存其购物车内容。只要会话存在,则商品就一直保留,对于一个Guest 这已经足够了。不过,如果用户已经注册,就要更安全地保存购物车内容,并置入一个后台数据库以作半持久存储。数据库将定期“清扫”从而删除一个月以前添加的商品。为了保证性能,用户的会话应当作为数据库的一个本地缓存,从而对以后显示购物车信息的请求提供支持,而不必在每次请求时都连接数据库。可以用一个手工设置的cookie(带有一个很长的到期时间)跟踪用户登录。在一个基于表单的登录之后,cookie 保存用户ID 的散列;该散列对应于数据库记录。当每次访问时,用户可以自动被识别,而且其购物车内容将被加载到会话中。为保证安全,结账时,服务器逻辑要在继续处理之前先询问用户口令进行验证。即使服务器知道客户的身份,由于登录是自动的,付款操作也应该被保护。表明口令已经得到验证的标志当然也要保存在会话中,有30分钟的超时时间就完全足够了,用户请求登出则只需要将cookie 删除即可。整个架构如图3-3 所示。本例中提供了定制的登录管理。可以使用默认的Servlet 基于表单的登录,但是它只适用于单会话登录以限制对安全内容的访问,其设计并不适用于需要识别用户的多会话登录。Servlet 最佳实践63浏览器X 服务器数据库X: 购物车内容Y: 购物车内容X2持久用户cookie5874临时会话cookie9320 浏览器会话用户为 Y购物车内容缓存8849 会话用户为guest购物车内容5874 浏览器会话用户为 X购物车内容缓存验证为 true图3-3:购物车架构何时使用会话如购物车例子所示,会话很有用,但并非万能。会话在以下情况中最有意义:保存登录状态超时很有用处,另外改换浏览器或机器都自然地要求一个新的登录。保存取自数据库的用户数据本地缓存可以避免每次请求时都连接数据库。保存临时的用户数据临时数据包括查找结果、表单状态或无需长期保存的Guest 购物车内容。不要使用SingleThreadModel下面我们来看一个有害而无利的Servlet特性:SingleThreadModel。我的建议是千万不要使用此特性。设

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论