郭老师总结servlet

来源:互联网 发布:alphago zero 算法 编辑:程序博客网 时间:2024/04/28 17:03

Java Servlet技术 

 

Web刚开始被用来传送服务时,服务提供者就已经意识到了动态内容的需要。Applet是为了实现这个目标的一种最早的尝试,它主要关注使用客户端平台来交付动态用户体验。与此同时,开发人员也在研究如何使用服务器平台实现这个目标。开始的时候,公共网关接口(Common Gateway Interface CGI)脚本是生成动态内容的主要技术。虽然使用得非常广泛,但CGI脚本技术有很多的缺陷,这包括平台相关性和缺乏可扩展性。为了避免这些局限性,Java Servlet技术因应而生,它能够以一种可移植的方法来提供动态的、面向用户的内容。 

 

什么是Servlet?

一个servlet就是Java编程语言中的一个类,它被用来扩展服务器的性能,服务器上驻留着可以通过“请求-响应”编程模型来访问的应用程序。虽然servlet可以对任何类型的请求产生响应,但通常只用来扩展Web服务器的应用程序。Java Servlet技术为这些应用程序定义了一个特定于HTTPservlet类。 

 

javax.servletjavax.servlet.http包为编写servlet提供了接口和类。所有的servlet都必须实现Servlet接口,该接口定义了生命周期方法。 

 

当实现一个通用的服务时,您可以使用或扩展由Java Servlet API提供的GenericServlet类。HttpServlet类提供了一些方法,诸如doGetdoPost,以用于处理特定于HTTP的服务。 

 

本章主要讲述如何编写对HTTP请求产生响应的servlet。这里假设您已经了解了一些HTTP协议的基础知识。如果对这些协议不熟悉的话,您可以从HTTP概述中对HTTP协议有一个初步的了解。 

 

Servlet示例 

本章使用Duke's Bookstore应用程序来说明与servlet编程相关的任务。表141列出了解决每个书店功能的servlet。每个编程任务用一个或多个servlet来说明。例如,BookDetailsServlet说明如何处理HTTP GET请求,BookDetailsServletCatalogServlet显示如何构建响应,而CatalogServlet 则说明如何跟踪会话信息。 

 

141 Duke's Bookstore Servlet例子  

 

功能 

Servlet

 

进入书店 

BookStoreServlet

 

创建书店标识 

BannerServlet

 

浏览书店的目录 

CatalogServlet

 

将书放入购物车 

CatalogServlet,

 

BookDetailsServlet

 

获取关于特定的某本书的一些详细信息 

BookDetailsServlet

 

显示购物车 

ShowCartServlet

 

从购物车中移除一本或多本书 

ShowCartServlet

 

购买购物车中的书 

CashierServlet

 

获得对购买的确认 

ReceiptServlet

 

 

这些书店应用程序的数据保存在数据库中,并通过帮助类database.BookDB进行存储。database包也包括BookDetails类,一个BookDetails类用来代表一种书。购物车和购物车项用cart.ShoppingCart cart.ShoppingCartItem来分别表示。 

 

书店应用程序的源代码放在<JWSDP_HOME>/docs/tutorial/examples/web/bookstore1目录中,这个目录是在对指南包进行解压缩时创建的。 

 

要构建、安装和运行这个实例,需完成以下步骤: 

 

1. 在终端窗口中,转到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore1 

 

2. 运行buildbuild目标将产生任何必要的编码并且将文件直接拷贝到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore1/build  

 

3. 确认Tomcat已经开始执行。 

 

4. 运行ant install install 目标通知Tomcat 已经有了新的上下文。 

 

5. 启动PointBase公司的数据库服务器,并且在没完全准备好的情况下仍然指向数据库(见从Web应用中访问数据库)。.

 

6. 打开书店的URL http://localhost:8080/bookstore1/enter以运行该应用程序。 

 

要部署该应用程序,要完成以下步骤: 

 

1. 运行ant package。这个包任务是创建一个WAR 文件,该文件包含WEB-INF/classes中的应用程序类和META-INF中的context.xml 文件。 

 

2. 确认Tomcat已经开始执行。 

 

3. 运行ant deployDeploy目标将WAR拷贝到Tomcat,并且通知已经有了新的上下文。 

 

故障排除  

一般的问题和其解决方案列举了Web客户端为什么会失败的一些原因。另外,Duke书店返回了以下异常: 

 

&<2539; BookNotFoundException――如果一本书不能在书店的数据库中找到,则返回该异常。如果用户没有运行ant create-book-db来加载书店数据库中的数据、或没有运行数据库服务器、或数据库已经崩溃,这些都将产生该异常。 

 

&<2539; BooksNotFoundException――如果书店的数据不能被获取,则返回该异常。如果用户没有运行ant create-book-db来加载书店数据库中的数据、或没有运行数据库服务器、或数据库已经崩溃,这些都将产生该异常。 

 

&<2539; UnavailableException――如果servlet不能获取到用来表示书店的Web上下文属性,则返回该异常。如果您没有拷贝指向(PointBase)的客户端库<PB_HOME>/lib/pbclient45.jar to <JWSDP_HOME>/common/lib、或者如果指向的(PiontBase)服务器没有运行、或用户没有定义Tomcat中用来引用指向数据库(PointBase)的数据源,这都将产生该异常。 

 

因为指定了一个错误页,用户将看到这样的一个消息The application is unavailable. Please try later. 如果指定了一个正确页,Web容器将产生一个包含A Servlet Exception Has Occurred消息的默认页和一个用来帮助诊断异常产生原因的栈。如果使用errorpage.html,用户可以了解Web容器决定异常产生原因的日志。Web日志位于<JWSDP_HOME>/logs目录中,由jwsdp_log.<date>.txt来命名。 Java Servlet技术 

 

Web刚开始被用来传送服务时,服务提供者就已经意识到了动态内容的需要。Applet是为了实现这个目标的一种最早的尝试,它主要关注使用客户端平台来交付动态用户体验。与此同时,开发人员也在研究如何使用服务器平台实现这个目标。开始的时候,公共网关接口(Common Gateway Interface CGI)脚本是生成动态内容的主要技术。虽然使用得非常广泛,但CGI脚本技术有很多的缺陷,这包括平台相关性和缺乏可扩展性。为了避免这些局限性,Java Servlet技术因应而生,它能够以一种可移植的方法来提供动态的、面向用户的内容。 

 

什么是Servlet?

一个servlet就是Java编程语言中的一个类,它被用来扩展服务器的性能,服务器上驻留着可以通过“请求-响应”编程模型来访问的应用程序。虽然servlet可以对任何类型的请求产生响应,但通常只用来扩展Web服务器的应用程序。Java Servlet技术为这些应用程序定义了一个特定于HTTPservlet类。 

 

javax.servletjavax.servlet.http包为编写servlet提供了接口和类。所有的servlet都必须实现Servlet接口,该接口定义了生命周期方法。 

 

当实现一个通用的服务时,您可以使用或扩展由Java Servlet API提供的GenericServlet类。HttpServlet类提供了一些方法,诸如doGetdoPost,以用于处理特定于HTTP的服务。 

 

本章主要讲述如何编写对HTTP请求产生响应的servlet。这里假设您已经了解了一些HTTP协议的基础知识。如果对这些协议不熟悉的话,您可以从HTTP概述中对HTTP协议有一个初步的了解。 

 

Servlet示例 

本章使用Duke's Bookstore应用程序来说明与servlet编程相关的任务。表141列出了解决每个书店功能的servlet。每个编程任务用一个或多个servlet来说明。例如,BookDetailsServlet说明如何处理HTTP GET请求,BookDetailsServletCatalogServlet显示如何构建响应,而CatalogServlet 则说明如何跟踪会话信息。 

 

141 Duke's Bookstore Servlet例子  

 

功能 

Servlet

 

进入书店 

BookStoreServlet

 

创建书店标识 

BannerServlet

 

浏览书店的目录 

CatalogServlet

 

将书放入购物车 

CatalogServlet,

 

BookDetailsServlet

 

获取关于特定的某本书的一些详细信息 

BookDetailsServlet

 

显示购物车 

ShowCartServlet

 

从购物车中移除一本或多本书 

ShowCartServlet

 

购买购物车中的书 

CashierServlet

 

获得对购买的确认 

ReceiptServlet

 

 

这些书店应用程序的数据保存在数据库中,并通过帮助类database.BookDB进行存储。database包也包括BookDetails类,一个BookDetails类用来代表一种书。购物车和购物车项用cart.ShoppingCart cart.ShoppingCartItem来分别表示。 

 

书店应用程序的源代码放在<JWSDP_HOME>/docs/tutorial/examples/web/bookstore1目录中,这个目录是在对指南包进行解压缩时创建的。 

 

要构建、安装和运行这个实例,需完成以下步骤: 

 

1. 在终端窗口中,转到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore1 

 

2. 运行buildbuild目标将产生任何必要的编码并且将文件直接拷贝到<JWSDP_HOME>/docs/tutorial/examples/web/bookstore1/build  

 

3. 确认Tomcat已经开始执行。 

 

4. 运行ant install install 目标通知Tomcat 已经有了新的上下文。 

 

5. 启动PointBase公司的数据库服务器,并且在没完全准备好的情况下仍然指向数据库(见从Web应用中访问数据库)。.

 

6. 打开书店的URL http://localhost:8080/bookstore1/enter以运行该应用程序。 

 

要部署该应用程序,要完成以下步骤: 

 

1. 运行ant package。这个包任务是创建一个WAR 文件,该文件包含WEB-INF/classes中的应用程序类和META-INF中的context.xml 文件。 

 

2. 确认Tomcat已经开始执行。 

 

3. 运行ant deployDeploy目标将WAR拷贝到Tomcat,并且通知已经有了新的上下文。 

 

故障排除  

一般的问题和其解决方案列举了Web客户端为什么会失败的一些原因。另外,Duke书店返回了以下异常: 

 

&<2539; BookNotFoundException――如果一本书不能在书店的数据库中找到,则返回该异常。如果用户没有运行ant create-book-db来加载书店数据库中的数据、或没有运行数据库服务器、或数据库已经崩溃,这些都将产生该异常。 

 

&<2539; BooksNotFoundException――如果书店的数据不能被获取,则返回该异常。如果用户没有运行ant create-book-db来加载书店数据库中的数据、或没有运行数据库服务器、或数据库已经崩溃,这些都将产生该异常。 

 

&<2539; UnavailableException――如果servlet不能获取到用来表示书店的Web上下文属性,则返回该异常。如果您没有拷贝指向(PointBase)的客户端库<PB_HOME>/lib/pbclient45.jar to <JWSDP_HOME>/common/lib、或者如果指向的(PiontBase)服务器没有运行、或用户没有定义Tomcat中用来引用指向数据库(PointBase)的数据源,这都将产生该异常。 

 

因为指定了一个错误页,用户将看到这样的一个消息The application is unavailable. Please try later. 如果指定了一个正确页,Web容器将产生一个包含A Servlet Exception Has Occurred消息的默认页和一个用来帮助诊断异常产生原因的栈。如果使用errorpage.html,用户可以了解Web容器决定异常产生原因的日志。Web日志位于<JWSDP_HOME>/logs目录中,由jwsdp_log.<date>.txt来命名。 

Servlet的生命周期 

一个servlet的生命周期由部署servlet的容器来控制。当一个请求映射到一个servlet时,该容器执行下列步骤。 

 

1. 如果一个servlet的实例并不存在,Web容器 

 

a. 加载servlet类。 

 

b. 创建一个servlet类的实例。 

 

c. 调用init初始化servlet实例。该初始化过程将在初始化servlet中讲述。 

 

2. 调用service方法,传递一个请求和响应对象。服务方法将在编写服务方法中讲述。 

 

如果该容器要移除这个servlet,可调用servletdestroy方法来结束该servlet。结束过程将在结束Serlvet中讨论。 

 

处理Servlet生命周期事件  

servlet的生命周期中,用户可以通过定义监听器对象对事件进行检测和产生反应。当生命周期事件发生时,调用该对象的方法。要使用这些监听器对象,用户必须定义监听器类,并且指定相应的监听器类。 

 

定义监听器类  

您可以将监听器类定义为一个listener接口的实现。Servlet生命周期事件列出了可以检测的事件和相应的必须实现的接口。当调用一个监听器方法时,需向该方法传递一个包含事件适当信息的事件。例如,向HttpSessionListener接口中的方法传递的是一个HttpSessionEvent事件,这个事件包含了一个HttpSession 

 

142Servle生命周期事件 

 

对象 

事件 

监听器接口和事件类 

 

Web上下文 

(见访问Web上下文)

初始化和销毁 

javax.servlet.

ServletContextListener  

 

ServletContextEvent

 

属性的添加、删除或替代 

javax.servlet.

ServletContextAttributeListener  

 

ServletContextAttributeEvent

 

会话 

(见维护客户给状态)

创建、失效和超时 

javax.servlet.http.

HttpSessionListener  

 

HttpSessionEvent

 

属性的添加、删除或替代 

javax.servlet.http.

HttpSessionAttributeListener  

 

HttpSessionBindingEvent

 

 

listeners.ContextListener类负责创建和移除在Duke书店应用程序中使用的数据库助手和计数器对象。方法从ServletContextEvent中获取Web上下文对象,进而存储(和移除)作为servlet上下文属性的对象。 

 

import database.BookDB;

import javax.servlet.*;

import util.Counter;

 

public final class ContextListener

implements ServletContextListener {

private ServletContext context = null;

public void contextInitialized(ServletContextEvent event) {

context = event.getServletContext();

try {

BookDB bookDB = new BookDB();

context.setAttribute("bookDB", bookDB);

} catch (Exception ex) {

System.out.println(

"Couldn't create database: "

+ ex.getMessage());

}

Counter counter = new Counter();

context.setAttribute("hitCounter", counter);

context.log("Created hitCounter"

+ counter.getCounter());

counter = new Counter();

context.setAttribute("orderCounter", counter);

context.log("Created orderCounter"

+ counter.getCounter());

}

 

public void contextDestroyed(ServletContextEvent event) {

context = event.getServletContext();

BookDB bookDB = context.getAttribute(

"bookDB");

bookDB.remove();

context.removeAttribute("bookDB");

context.removeAttribute("hitCounter");

context.removeAttribute("orderCounter");

}

}

 

指定事件监听器类  

为了指定一个事件监听器类,用户要为Web应用部署描述符添加一个listener元素。以下就是Duke书店应用程序的一个listener元素。 

 

<listener>

<listener-class>listeners.ContextListener</listener-class>

</listener>

 

处理错误  

servlet执行时,可能产生许多异常。而当异常产生时,Web容器将产生一个包含A Servlet Exception Has Occurred消息的缺省页。但是,用户也可返回一个容器,该容器应包含为给定异常指定的错误页。为了指定这样一个页,用户要为Web应用添加部署描述符添加一个error-page元素。这些元素将Duke书店应用程序返回的异常映射到errorpage.html 

 

<error-page>

<exception-type>

exception.BookNotFoundException

</exception-type>

<location>/errorpage.html</location>

</error-page>

<error-page>

<exception-type>

exception.BooksNotFoundException

</exception-type>

<location>/errorpage.html</location>

</error-page>

<error-page>

<exception-type>exception.OrderException</exception-type>

<location>/errorpage.html</location> </error-page>

共享信息  

像大多数对象一样,Web组件通常与其他一些对象协同工作,以完成任务。要做到这一点,可以有多种方法。Web组件可以使用私有的helper(助手)对象(例如,JavaBeans组件),也可以共享那些有公共作用域属性的对象,它们可以使用数据库,还可以调用其他的Web资源。Java Servlet技术机制允许一个Web组件调用其他的Web资源,这在调用其他Web资源中有描述。 

 

使用作用域对象 

几个协作的Web组件通过一些对象来共享信息,这些对象是作为四个作用域对象的属性来维护的。这些属性可以通过表示域的类的[get|set]Attribute方法访问。表143列出了这个作用域对象。 

 

14-3 作用域对象 

 

作用域对象 

 

哪些组件可以对其进行访问 

 

Web 上下文 

javax.servlet.

ServletContext

Web上下文中的Web组件。见访问Web上下文 

 

会话 

javax.servlet.

http.HttpSession

处理属于会话的请求的Web组件。见维护客户端状态。 

 

请求 

javax.servlet.

ServletRequest

 

的子类型 

处理请求的Web组件。 

 

 

javax.servlet.

jsp.PageContext

创建对象的JSP页。见隐式对象。 

 

 

141显示了Duke书店应用程序维护的作用域属性。 

 

 

 

14-1 Duke书店作用域属性 

 

控制对共享资源的并发访问 

在多线程的服务器中,可能出现对共享资源的并发访问。除了作用域对象属性外,共享资源还包括存储器中的数据(如实例和类变量)、外部对象(如文件)、数据库连接和网络连接。并发访问可出现在多个情况下。 

 

&<2539; 多个Web组件访问存储在Web上下文中的对象。t

 

&<2539; 多个Web组件访问存储在会话中的对象。 

 

&<2539; 一个Web组件中的多个线程访问实例变量。一个Web容器一般为每个请求创建一个线程来处理。如果用户确认一个servlet实例每次只处理一个请求,servlet就能实现SingleThreadModel 接口。如果servlet实现了这个接口,用户就能确保servlet的服务方法中不可能有两个线程并发执行。Web容器可通过同步访问一个servlet的单独实例、或者通过维护一个Web组件池为每个实例调用一个新的请求来实现。这个接口并不能防止Web组件访问共享资源(如静态类变量、外部对象)导致的同步问题 

 

当资源可以并发访问时,使用资源也就可以用不一致的方式。为了防止这样的情况发生,用户必须使用在Java指导中的线程单元中描述的同步机制来控制访问。 

 

在以前的部分中,我们说明了被多个servlet共享的5个作用域属性: bookDB, cart, currency, hitCounterorderCounterbookDB属性将在下一节中讨论。cart, currencycounter可以被多线程的servlet设置和读。使用同步方法来控制访问以防止这些对象的使用不一致。例如,下面是一个util.Counter类: 

 

public class Counter {

private int counter;

public Counter() {

counter = 0;

}

public synchronized int getCounter() {

return counter; }

public synchronized int setCounter(int c) {

counter = c; return counter;

}

public synchronized int incCounter() {

return(++counter);

}

}

 

访问数据库 

Web组件之间共享,并且在对一个Web应用被调用的间隙内维持的数据通常是由一个数据库来维护的。Web组件使用JDBC 2.0 API来访问关系数据库。书店应用程序的数据由数据库来维护,并通过助手类database.BookDB访问。例如,当用户购买书后,ReceiptServlet调用BookDB.buyBooks方法来更新书的清单。buyBooks方法为每本包含在购物车中的书调用buyBook。为了确保命令被完全执行,buyBook的调用程序将被包装在一个单独的JDBC事务处理中。通过[get|release]Connection方法可以使共享数据库连接同步使用。 

 

public void buyBooks(ShoppingCart cart) throws OrderException {

Collection items = cart.getItems();

Iterator i = items.iterator();

try {

getConnection();

con.setAutoCommit(false);

while (i.hasNext()) {

ShoppingCartItem sci = (ShoppingCartItem)i.next();

BookDetails bd = (BookDetails)sci.getItem();

String id = bd.getBookId();

int quantity = sci.getQuantity();

buyBook(id, quantity);

}

con.commit();

con.setAutoCommit(true);

releaseConnection();

} catch (Exception ex) {

try {

con.rollback();

releaseConnection();

throw new OrderException("Transaction failed: " +

ex.getMessage());

} catch (SQLException sqx) {

releaseConnection();

throw new OrderException("Rollback failed: " +

sqx.getMessage());

}

}

}

 

初始化Servlet

Web容器加载和实例化servlet类之后、servlet实例传递来自客户端的请求之前,Web容器对servlet进行初始化。用户可以自定义这个初始化过程,以允许servlet读持久的配置数据、初始化资源,并且忽略Servlet接口的init方法以执行任何其它的一次性的活动。servlet必须使用UnavailableException来完成初始化过程。 

 

所有的访问书店数据库的servletBookStoreServlet, CatalogServlet, BookDetailsServlet, ShowCartServlet)在它们的init方法中初始化一个变量,指向用Web上下文监听器创建的数据库助手对象。 

 

public class CatalogServlet extends HttpServlet {

private BookDB bookDB;

public void init() throws ServletException {

bookDB = (BookDB)getServletContext().

getAttribute("bookDB");

if (bookDB == null) throw new

UnavailableException("Couldn't get database.");

}

}

 

编写服务方法 

servlet提供的服务实现在GenericServletservice方法、HttpServletdoMethod方法(在该方法中,Method可以带GetDeleteOptionsPostPutTrace的值),或者是任何其他的由实现了Servlet接口的类定义的协议指定(protocol-specific)的方法中。在这一章剩下的部分中,服务方法这个术语将用于在一个向客户端提供服务的servlet类中定义的任何方法。 

 

服务方法的一般模式是从请求中提取信息、访问外部资源并且基于这些信息填充响应。 

 

对于HTTPservlet来说,填充响应的正确过程是:首先填充响应头,然后从响应中获取一个输出流,最后编写输出流的所有主体内容。响应头必须在PrintWriterServletOutputStream被获取到之前设置好,因为HTTP协议希望获得主体内容前的所有头的信息。下两节将描述如何从请求中获得信息和产生响应。 

 

从请求中获得信息 

一个请求包含客户端和servlet之间传递的数据。所有请求都实现了ServletRequest接口,该接口为访问一下的信息定义了方法: 

 

&<2539; 参数,通常用来在客户端和servlet之间传送信息 

 

&<2539; 对象属性(Object-valued attribute),通常用来在servlet容器与servlet之间或在协作的servlet之间传递信息 

 

&<2539; 有关协议的信息,用来在请求、客户端和涉及到该请求中的服务器之间的通信。 

 

&<2539; 有关地区化的信息。 

 

例如,在CatalogServlet中,顾客希望购买的书的标识符作为参数包含在请求中。下面的这段代码说明了如何使用getParameter方法提取标识符。 

 

String bookId = request.getParameter("Add");

if (bookId != null) {

BookDetails book = bookDB.getBookDetails(bookId);

 

用户也可以从请求中获取一个输入流,并对数据进行手工解析。要读字符数据,可以使用由请求的getReader方法返回的 BufferedReader对象来完成。而要读二进制数据,可以使用getInputStream 返回的ServletInputStream 

 

HTTP serlvet通过HTTP请求对象传递,HttpServletRequest包含了请URLHTTP头、查询字符串等等。 

 

一个HTTP请求URL包含以下几部分: 

 

http://[host]:[port][request path]?[query string]

 

请求路径由以下元素组成: 

 

&<2539; 上下文路径:向前的斜线/servletWeb应用的上下文根的拼接。 

 

&<2539; servlet路径:与激活该请求的组件别名相应的路径部分,由向前的斜线/开始。 

 

&<2539; 路径信息:请求路径的部分,不是上下文路径或者servlet路径的部分。 

 

如果上下文路径是/catalog和表14-4列举出的别名,表14-5给出了一些实例,说明如何分解URL 

 

14-4别名  

 

模式 

Servlet

 

/lawn/*

LawnServlet

 

/*.jsp

JSPServlet

 

 

14-5 请求路径元素 

 

请求路径 

Servlet 路径 

路径信息 

 

/catalog/lawn/index.html

/lawn

/index.html

 

/catalog/help/feedback.jsp

/help/feedback.jsp

null

 

 

查询字符串由参数和值的集合组成。每个参数都是从请求中用getParameter方法获取得到的。这里有两种方法产生查询字符串: 

 

&<2539; 一个查询字符串能在Web页中明确地显示出来。例如,一个HTML页由CatalogServlet产生,该HTML页包含了<a href="/bookstore1/catalog?Add=101">Add To Cart</a>CatalogServlet 将命名为Add的参数提出,如下: 

 

String bookId = request.getParameter("Add");

&<2539; 当一个表单与一个GET HTTP方法一起被提交时,在URL上附加一个查询字符串。在Duke书店应用程序中,首先CashierServlet产生了一个表单,然后在表单中输入一个用户名,该表单附加在映射到ReceiptServletURL上,最后ReceiptServlet使用getParameter方法提取用户名。

Servlet可以创建协议、平台无关的Web应用程序,Applet运行于浏览器的JRE中,而Servlet则运行于Web应用服务器的Servlet容器中,Servlet没有用户图形界面,ServletWeb应用服务器的Servlet容器交互以接收请求返回响应。请求最先由Web应用服务器的Servlet容器处理并传给ServletServlet通过Web应用服务器返回响应给客户端。客户端程序可以使用任何可向Web应用服务器发送请求的语言开发。 

 

  Servlet最大的优势在于它的高性能,Servlet采用了和CGI截然不同的运行方式,首先Servlet在第一次初始化时装载并驻留在内存中,以后直接从内存中运行;其次,在默认情况下Servlet以单实例多线程的方式工作,一个新请求到达后,Servlet实例开启一个新的线程服务这个请求。 

 

  Servlet 结构和线程安全 

 

  所有的Servlet都直接或间接地实现javax.servlet.Servlet接口,这些接口规定了Servlet如何与Servlet容器进行通讯的方法,此外还定义了Servlet的生命周期。GenericServlet是和协议无关的通用ServletHttpServlet是专门针对HTTP协议开发的ServletWeb应用程序的Servlet都直接继承HttpServlet。其类的继承体系如下图所示: 

 

 

1 Servlet的类继承体系  

 

  javax.servlet.Servlet接口包括了3个控制Servlet生命周期的方法,它们分别是: 

 

  &<2539;init(ServletConfig config)方法 

 

  当Servlet初始化时,init()方法被调用执行初始化Servlet的工作,init()方法只被调用一次。Servlet初始化后就进入就绪态,随时准备响应客户端的请求。 

 

  &<2539;service(ServletRequest req, ServletResponse resp)方法 

 

  Servlet容器调用service()方法处理请求并返回响应。ServletRequestServletResponse作为入参传给service()ServletRequest封装了请求的信息而ServletResponse封装了响应的信息。 

 

  &<2539;destroy()方法 

 

  Servlet容器可以在任何时候卸载Servlet,此时destory()被调用,你可以在这儿释放Servlet所占用的资源。 

 

  而javax.servlet.http包中的类用于支持HTTP协议,创建HTML网页。HTTP协议是基于请求/响应工作模式,这些HTTP的请求方式包括: 

 

  GET

 

  POST

 

  PUT

 

  DELETE

 

  HEAD

 

  TRACE

 

  CONNECT

 

  OPTIONS

 

  javax.servlet.http.HttpServlet定义了多个服务HTTP协议的方法,这些方法名为doXxx()的样式命名和HTTP请求方式名相呼应:如HTTP GET请求方式对应doGet(),而HTTP POST对应doPost()等。HttpServlet最初以service(HttpServletRequest req, HttpServletResponse resp)响应客户端请求,并依据HTTP的请求方式调用相应的doXxx()方法来处理。 

 

  一般的,你仅需要覆盖doGet()doPost()方法,如果希望得到更多的控制,你也可以覆盖doPut()doDelete()方法,其他的方法一般很少使用。如果你使用JBuilderServlet向导,你可以具体指定创建哪些方法。 

 

  特别需要指出的是Servlet是以多线程的方式工作的,Servlet可以同时处理多个请求。作为开发人员,需要注意Servlet成员变量的线程安全,在doGet()doPost()中的局部域变量是线程安全的,而Servlet的成员变量则有线程安全的隐患。所以除非你有意需要应用这种特性,在一般情况下,不宜将一些可改写的变量定义成Servlet的成员变量,否则一定要采取线程同步的措施确保线程安全。 

 

  Servlet的特性及适用场合 

 

  虽然Servlet也可以用于生成动态网页,但这个功能已经逐渐让位给新锐JSP了,不过Servlet并没有因为江山代有才人而成为Java历史博物馆的古董,它依然身怀绝技笑傲江湖: 

 

  &<2539;自动启动 

 

  一般情况下,JSP只有在客户端第一次调用后,方才进行编译并初始化,而Servlet则可以通过web.xml<load-on-startup>配置,使其在Web容器启动时自动初始化。可使用Servlet这个特性完成Web应用程序的初始化工作:如下载字典表、控制表、初始化配置信息等,启动某个后台进程等。 

 

  &<2539;路径匹配映射 

 

  Servlet可以通过web.xml <servlet-mapping>用通配符配置URI映射,对多个匹配的URI进行响应,而JSP只能通过一个具体的URI调用。这个特性可以使你在请求进入某个具体的页面前截获并处理它,许多Web应用框架,如StrutsSpring都利用了Servlet的这个特性,在此基础上创建构架。 

 

  &<2539;Servlet过滤器 

 

  Servlet过滤器继承于javax.servlet.Servlet并实现javax.servlet.Filter的类,在请求到达服务程序前和响应发往客户端前进行加工处理。如果有大量的页面都需要进行相同的处理,则可以使用一个Servlet过滤器对此一并处理。如你可以用Servlet过滤器进行编码转换、或者为每个页面添加一个统一的标题头等。 

 

  &<2539;Servlet监听器 

 

  J2EE定义了多个Web事件监听器接口,Servlet监听器是继承javax.servlet.Servlet并实现这些事件监听接口的类。如果Web应用服务器对某个Web容器的事件感兴趣,就可以构造一个实现该事件监听接口的Servlet,以便在该事件发生时做相应的处理。灵活使用Servlet监听器,可以使一些原来很难实现的操作变得易如反掌。 

 

  此外,如果一个动态网页的页面展现逻辑少,而业务处理逻辑多,如一个计算圆周率并返回结果页面的请求,这时使用ServletJSP更适合。所以需要创建一个动态网页,在到底使用Servlet还是JSP的问题上举棋不定时,请这记住下面这句经典的话:Servlet是包含HTML代码的Java程序,而JSP是包含Java代码的HTML网页。

. 从客户端传送到Web服务器的数据有两种:显式(HTTP表单的数据)和隐式(HTTP请求的报头).

. Web服务器到客户浏览器的数据也有两种:HTTP响应报头和文档.

. 服务器每次接收到对Servlet的请求,都会产生一个新的线程,调用service方法.service方法检查HTTP请求的类型(get,post,put,delete)并相应调用doGet,doPost,doPut,doDelete等方法.

. GET用来请求页面,包括:用户在地址栏中输入URL;点击Web页面内的链接;提交没有指定method或指定method=”get”的表单时,浏览器都会生成该请求,调用doGet方法.

POST:提交method=”post”表单时,会生成post请求,调用doPost方法.

. 如果您需要在servlet中等同的处理postget请求,只需让doGet调用doPost(或相反)即可.

. doGetdoPost方法有两个参数:HttpServletRequestHttpServletResponse

HttpServletRequest: 即与客户请求有关,通过此可以得到所有的输入数据.包括:表单(查询)数据,HTTP请求报头和客户的主机名等.

HttpServletResponse: 即与服务器的相应有关,通过此可以指定相应信息.包括:HTTP状态代码(200,404),响应报头,最重要等是通过getWriter方法获得PrintWriter,用它可以将文档内容发送给客户.

.init方法在servlet首次载入时调用,所以一些初始化任务可以放在该方法里,该方法在servlet里只调用一次.servlet一般在用户首次调用对应servletURL时创建,但也可以指定servlet在服务器启动时载入.

.web.xml<servlet>元素中有:

<init-param>

<param-name>config</param-name>

<param-value>/WEB-INF/struts-config.xml</param-value>

</init-param>

l 通过向web.xml<servlet>元素添加<init-param>子元素,可以指定初始化参数的名称和值.

l servletinit方法中,调用getServletConfig.得到ServletConfig对象.

l init参数的名称为参数,调用ServletConfiggetInitParameter方法,返回值就是init参数的值或null(没有该参数).

l HttpServlet extends GenericServlet

重写的init方法即重写GenericServlet中的init方法,在此接口中有两种形式:

public void init()throws ServletException{}

public void init(ServletConfig config)throws ServletException {

this.config = config;

this.init;

log(“init”);

}

.中文乱码解决:

doGetdoPost方法开头加入下两句:

request.setCharacterEncoding(“GBK”);

response.setContentType(“text/html; charset=GBK”);

:对用户提交的表单数据验证一般为:

String param = request.getParameter(“someName”);

If((param == null) || (param.trim.equals(“ “))) {

…… 

} else {

…… 

}

其中trim方法用来移除用户可能输入的任何空格,防止用户只输入空格.

十一:检查数组长度用length,检查String长度用length(),StringcharAt(int i)方法用来返回位于第i位置上的字符.

: String s = “Vincent”;

char c;

c = s.charAt(2);

显然字符c为’n’

十二.StringBufferString区别:

StringBuffer为可变长的字符串,append(String s)方法拼接字符串,String为不可变的,拼接字符串要用+来加上另一个String,这样就增加了额外的内存消耗.

十三.Enumeration e = request.getParameterNames();

getParameterNames方法用来得到表单中的所有参数名,并将他们全部放入Enumeration对象中(顺序不定),EnumerationhasMoreElementsnextElement(返回Object类型)方法.

: Enumeration paramNames = request.getParameterNames();

while(paramNames.hasMoreElements()) {

String name = (String)paramNames.nextElement();

String value = request.getParameter(name);

}

得到表单参数的另种形式为: Map map = request.getParameterMap();

其中,参数名(字符串)是表的键,参数值(字符串数组,getParameterNames返回值相同)是表的值.

十四.用户提交的表单值是如何封装到JavaBean?是通过:

BeanUtilspopulate(Object bean, Map properties)

bean即为JavaBean对象,properties即为request.getParameterMap()所得的Map对象,从上面知道表单值已经封装到Map对象中.

通过该方法即可把表单值封装到JavaBean.

: User user = new User();

Map map = request.getParameterMap();

BeanUtils.populate(user,map);

十五.报头Referer存放引用web页面的URL

:如果您在web页面A单击指向web页面B的链接,那么,在浏览器请求web页面B,就会将web页面AURL引入Referer报头.

得到报头信息用:request.getHeader(“报头名”);

:String referrer = request.getHeader(“referer”);

十六.得到所有报头: Enumeration e = request.getHeaderNames();

得到客户IP: request.getRemoteAddr();

得到客户端名: request.getRemoteHost();

得到URI的实际路径: request.getServletPath();即为URL在主机和端口后的那部分.

十七.response.sendRedirect(String url); 将用户重定向到另一个URL

十八.”状态代码”是用来标明失败或重定向的.

response.sendError(int code, String message);

用来返回状态代码与错误信息.

十九.response.setHeader(“refresh”, “5; URL=http://laz.blog.edu.cn”);

客户在原页面停留5秒后将自动跳转到http://laz.blog.edu.cn

JSP页面中等同的设置为:

<head>元素中设置 <meta http-equic = ”refresh”

Content = “5; URL=http://laz.blog.edu.cn” />

 

Refresh为报头.记住,服务器对客户浏览器的响应数据为两种:HTTP报头和文档.在发送报头前可对报头内容进行设置.客户浏览器收到设置后到报头,

response.setHeader(“refresh”, 5); 表每隔5秒刷新一次页面 

对某报头设值: response.setHeader(“报头名”, “值”);

二十.HTTP允许相同报头名多次出现 

setHeader, setIntHeader, setDateHeader 为替换同名已有报头 

addHeader, addIntHeader, addDateHeader 添加报头 

二十一.application即为ServletContext对象.

ServletContext application = getServletContext();

getServletContext方法为GenericServlet类方法,所以可直接用.

ServletContext类中有:setAttribute,getAttribute,removeAttribute方法.

二十二.Cookie

设置Cookie,并保存客户机内存:Cookie cookie = new Cookie(“名”, “值”);

response.addCookie(cookie);

此时客户机内存中将存有该cookie.若客户关闭浏览器,cookie将消失.

若要将cookie保存于客户硬盘,并设定保存时间,可以设置为:

Cookie cookie = new Cookie(“名”, “值”);

cookie.setMaxAge(60 * 60 * 24 * 365);

response.addCookie(cookie);

保存60 * 60 * 24 * 365,即为一年.

二十三.从客户机里得到所有Cookie,并找出Cookie名为userID的值id.

Cookie[] cookies = request.getCookies();

String userID = “userID”;

String id = null;

if(cookies != null) {

for(int i=0; i < cookies.length; i++) {

Cookie cookie = cookies[i];

if(cookie.getName().equals(userID)) {

id = cookie.getValue();

break;

} } }

二十四.cookie.getName(); 得到cookie.

cookie.getValue(); 得到cookie.

cookie.setValue(String s); cookie设置值.不能给cookie改名.

二十五.Cookie属性:

l setDomain(String)getDomain()

设置或读取Cookie适用的域.一般地,浏览器只将cookie返回给主机名与发送该cookie的主机名完全相同的主机.

Cookie.setDomain(“.vacations.com”); 具体设置规则看书171

l setMaxAge(int)getMaxAge()

前面已说过,这里补充下,默认情况下int值为-1,即保存于内存中.

若要从客户机中删除某个cookie,只需将该cookie保存时间设置为0.

l setPath(String)getPath()

设置或取得cookie所适用的路径.如果没有指定一个路径.浏览器只将该cookie返回给发送cookie的页面所在目录中或之下的URL.详看书172.

二十六.关于Stringint之间的转型 

前提:String一定要为数字,:String s = “123”;

Stringint: int i = Integer.parseInt(String s);

intString: String s = String.valueOf(int i);

二十七.<input type=”hidden” name=”propertyName” value=”propertyValue”>

意思为:在提交表单时,要将指定的名称和值自动包括在getpost数据中.

二十八.HttpSession session = request.getSession();

有以下方法:setAttrivute, getAttribute, removeAttribute.

invalidate废气整个废话.

logout使客户退出web服务器.

Enumeration e = session.getAttributeNames();

HttpSession对象存在于服务器端,他们不在网络上来回传送,他们只是通过某种后台运作机制,比如cookieURL重写,自动与客户关联在一起.

二十九.HttpServletResponseencodeURL方法,这个方法确定当前是否在使用URL重写.

: String encodedURL = response.encodeURL(“http://laz.blog.edu.cn”);

总结: HttpServlet的功能  

HttpServlet首先必须读取Http请求的内容。Servlet容器负责创建HttpServlet对象,并把Http请求直接封装到HttpServlet对象中,大大简化了HttpServlet解析请求数据的工作量。HttpServlet容器响应Web客户请求流程如下: 

1Web客户向Servlet容器发出Http请求; 

2Servlet容器解析Web客户的Http请求; 

3Servlet容器创建一个HttpRequest对象,在这个对象中封装Http请求信息; 

4Servlet容器创建一个HttpResponse对象; 

5Servlet容器调用HttpServletservice方法,把HttpRequestHttpResponse对象作为service方法的参数传给HttpServlet对象; 

6HttpServlet调用HttpRequest的有关方法,获取HTTP请求信息; 

7HttpServlet调用HttpResponse的有关方法,生成响应数据; 

8Servlet容器把HttpServlet的响应结果传给Web客户。