servlet教程.docx
《servlet教程.docx》由会员分享,可在线阅读,更多相关《servlet教程.docx(19页珍藏版)》请在冰豆网上搜索。
![servlet教程.docx](https://file1.bdocx.com/fileroot1/2022-12/7/21a6152b-9264-4619-bdc3-d5d1b33ec624/21a6152b-9264-4619-bdc3-d5d1b33ec6241.gif)
servlet教程
Servlet教程
一、Servlet简介
Servlet是对支持Java的服务器的一般扩充。
它最常见的用途是扩展Web服务器,提供非常安全的、可移植的、易于使用的CGI替代品。
它是一种动态加载的模块,为来自Web服务器的请求提供服务。
它完全运行在Java虚拟机上。
由于它在服务器端运行,因此它不依赖于浏览器的兼容性。
servlet容器:
负责处理客户请求、把请求传送给servlet并把结果返回给客户。
不同程序的容器实际实现可能有所变化,但容器与servlet之间的接口是由servlet API定义好的,这个接口定义了servlet容器在servlet上要调用的方法及传递给servlet的对象类。
servlet的生命周期:
1、servlet容器创建servlet的一个实例
2、容器调用该实例的init()方法
3、如果容器对该servlet有请求,则调用此实例的service()方法
4、容器在销毁本实例前调用它的destroy()方法
5、销毁并标记该实例以供作为垃圾收集
一旦请求了一个servlet,就没有办法阻止容器执行一个完整的生命周期。
容器在servlet首次被调用时创建它的一个实例,并保持该实例在内存中,让它对所有的请求进行处理。
容器可以决定在任何时候把这个实例从内存中移走。
在典型的模型中,容器为每个servlet创建一个单独的实例,容器并不会每接到一个请求就创建一个新线程,而是使用一个线程池来动态的将线程分配给到来的请求,但是这从servlet的观点来看,效果和为每个请求创建一个新线程的效果相同。
servlet API
servlet接口:
public interface Servlet
它的生命周期由javax.servlet.servlet接口定义。
当你在写servlet的时候必须直接或间接的实现这个接口。
一般趋向于间接实现:
通过从javax.servlet.GenericServlet或javax.servlet.http.HttpServlet派生。
在实现servlet接口时必须实现它的五个方法:
init():
public void init(ServletConfig config) throws ServletException
一旦对servlet实例化后,容器就调用此方法。
容器把一个ServletConfig对象传统给此方法,这样servlet的实例就可以把与容器相关的配置数据保存起来供以后使用。
如果此方法没有正常结束就会抛出一个ServletException。
一旦抛出该异常,servlet就不再执行,而随后对它的调用会导致容器对它重新载入并再次运行此方法。
接口规定对任何servlet实例,此方法只能被调用一次,在任何请求传递给servlet之前,此方法可以在不抛出异常的情况下运行完毕。
service():
public void service(ServletRequest req,ServletResponse res) throws ServletException,IOException
只有成功初始化后此方法才能被调用处理用户请求。
前一个参数提供访问初始请求数据的方法和字段,后一个提供servlet构造响应的方法。
destroy():
public void destroy()
容器可以在任何时候终止servlet服务。
容器调用此方法前必须给service()线程足够时间来结束执行,因此接口规定当service()正在执行时destroy()不被执行。
getServletConfig():
public ServletConfig getServletConfig()
在servlet初始化时,容器传递进来一个ServletConfig对象并保存在servlet实例中,该对象允许访问两项内容:
初始化参数和ServletContext对象,前者通常由容器在文件中指定,允许在运行时向sevrlet传递有关调度信息,后者为servlet提供有关容器的信息。
此方法可以让servlet在任何时候获得该对象及配置信息。
getServletInfo():
public String getServletInfo()
此方法返回一个String对象,该对象包含servlet的信息,例如开发者、创建日期、描述信息等。
该方法也可用于容器。
GenericServlet类
Public abstract class GenericServlet implants Servlet,ServletConfig,Serializable
此类提供了servlet接口的基本实现部分,其service()方法被申明为abstract,因此需要被派生。
init(ServletConfig conf)方法把servletConfig对象存储在一个private transient(私有临时)实例变量里,getServletConfig()方法返回指向本对象的指针,如果你重载此方法,将不能使用getServletConfig来获得ServletConfig对象,如果确实想重载,记住要包含对super.config的调用。
2.1版的API提供一个重载的没有参数的init()方法。
现在在init(ServletConfig)方法结束时有一个对init()的调用,尽管目前它是空的。
2.1版API里面,此类实现了ServletConfig接口,这使得开发者不用获得ServletConfig对象情况下直接调用ServletConfig的方法,这些方法是:
getInitParameter(),getInitParameterNames(),getServletContext。
此类还包含两个写日志的方法,它们实际上调用的是ServletContext上的对应方法。
log(String msg)方法将servlet的名称和msg参数写到容器的日志中,log(String msg,Throwable cause)除了包含servlet外还包含一个异常。
HttpServlet类
该类扩展了GenericServlet类并对servlet接口提供了与HTTP更相关的实现。
service():
protected void service(HttpServletRequest req,HttpServletResponse res) throws ServletException,IOExceptionpublic void service(HttpServletRequest req,HttpServletResponse res)throws ServletException,IOException
该方法作为HTTP请求的分发器,这个方法在任何时候都不能被重载。
当请求到来时,service()方法决定请求的类型(GET,POST,HEAD,OPTIONS,DELETE,PUT,TRACE),并把请求分发给相应的处理方法(doGet(),doPost(),doHead(),doOptions(),doDelete(),doPut(),doTrace())每个do方法具有和第一个service()相同的形式。
为了响应特定类型的HTTP请求,我们必须重载相应的do方法。
如果servlet收到一个HTTP请求而你没有重载相应的do方法,它就返回一个说明此方法对本资源不可用的标准HTTP错误。
getLatModified():
protected long getLastModified(HttpServletRequest req)
该方法返回以毫秒为单位的的自GMT时间1970年1月1日0时0分0秒依赖的最近一次修改servlet的时间,缺省是返回一个负数表示时间未知。
当处理GET请求时,调用此方法可以知道servlet的最近修改时间,服务器就可决定是否把结果从缓存中去掉。
HttpServletRequest接口
public interface HttpServletRequest extends ServletRequest
所有实现此接口的对象(例如从servlet容器传递的HTTP请求对象)都能让servlet通过自己的方法访问所有请求的数据。
下面是一些用来获取表单数据的基本方法。
getParameter()
public String getParameter(String key)
此方法试图将根据查询串中的关键字定位对应的参数并返回其值。
如果有多个值则返回列表中的第一个值。
如果请求信息中没有指定参数,则返回null。
getParameterValues():
public String[] getParameterValues(String key)
如果一个参数可以返回多个值,比如复选框集合,则可以用此方法获得对应参数的所有值。
如果请求信息中没有指定参数,则返回null。
GetParameterNames():
Public Enumeration getParameterNames()
此方法返回一个Enumeration对象,包含对应请求的所有参数名字列表。
HttpServletResponse接口
public interface HttpServletResponse extends servletResponse
servlet容器提供一个实现该接口的对象并通过service()方法将它传递给servlet。
通过此对象及其方法,servlet可以修改响应头并返回结果。
setContentType():
public void setContentType(String type)
在给调用者发回响应前,必须用此方法来设置HTTP响应的MIME类型。
可以是任何有效的MIME类型,当给浏览器返回HTML是就是”text/html”类型。
getWriter():
public PrintWriter getWriter()throws IOException
此方法将返回PrintWriter对象,把servlet的结果作为文本返回给调用者。
PrintWriter对象自动把Java内部的UniCode编码字符转换成正确的编码以使客户端能够阅读。
getOutputStream():
public ServletOutputStream getOutputStream() throws IOException
此方法返回ServletOutputStream对象,它是java.io.OutputStream的一个子类。
此对象向客户发送二进制数据。
setHeader():
public void setHeader(String name,String value)
此方法用来设置送回给客户的HTTP响应头。
有一些快捷的方法用来改变某些常用的响应头,但有时也需要直接调用此方法。
编译条件
需要从 获得一份JSDK的拷贝,并把servlet.jar移动到JDK安装目录下的/jre/lib/ext目录下。
如果是JDK1.1,则移动到/lib下,并在CLASSPATH中加入servlet.jar的绝对路径。
运行条件
需要Apache Jserv,Jrun Servlet Exec,Java Web Server,Weblogic,WebSphere,Tomcat,Resin等servlet服务器端程序。
简单范例
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorld extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("");
out.println("
");
out.println("
");
out.println("
Hello World!");
out.println("");
out.println("
");
out.println("
Hello World!
");
out.println("");
out.println("");
}
}
servlet的性能和效率
一个servlet仅被初始化一次而执行多次,因此极小的低效性也会随着时间的增加而产生很很大的影响。
在代码中需要考虑String对象的使用,如果产生HTML响应需要用到很多字符串时,不应该为每一个字符串生成一个String对象,因为这会产生大量的String和StringBuffer对象,造成大量的对象构造消耗和垃圾收集负担,解决的办法是一行一行的把所有需要写入的直接写入PrintWriter中,或者创建一个StringBuffer对象,并使用append()方法将文本加入。
及时回送
有时,程序需要花费很长时间执行,在这种情况下应该回送给客户端一些信息,而不是长时间显示白屏,这可以在执行到一定程度就回送一些东西,可以使用PrintWriter的flush()方法强制将现有的内容回送给浏览器。
Servlet会话
由于Web服务器使用的协议HTTP是一种无状态的协议,因此要维护单个客户机一系列请求的当前状态就需要使用其它的附加手段,在以前,一般的方法是使用:
l.隐藏的表格字段:
在浏览器中,这种类型的字段是不可见的,然而,它在请求中被传送,服务器端程序可以读取它的值。
它的优点是实现容易,且大多浏览器都支持;缺点是字段必须按照特定的顺序建立,客户端可以通过查看源代码得到其值,在浏览器中单击“后退”按钮会丢失加到当前页中的附加字段,同时也限制了用户动态的生成文档。
l.Cookie:
是一些关键的数据片断,由服务器建立并由客户机的浏览器存放。
浏览器维护一个它自己的Cookie表,这使得它可以作为一种会话跟踪的解决方案。
使用Cookie的好处是它比在URL或表单中储存数据更直观。
它的缺点是它可以用于在比一次短会话更长时间内跟踪用户,甚至可以用来跟踪某个用户向站点发送的每一个请求,因此有人担心自己的隐私问题而关闭了Cookie,一些老的浏览器也不支持cookie。
Servlet API提供一个Cookie类支持cookie,使用HttpServletResponse.addCookie()和HttpServletResponse.getCookies()方法添加和读取cookie。
l.URL重写:
修改请求的url,使之包含会话ID。
这种方法的缺点是:
对于大量的数据,URL会变得很长而失去控制;在某些环境下,URL的字符串长度有一定的限制;数据保密问题,你可能不想让旁边的人或者可以使用同一个计算机的看到你的会话数据。
Servlet提供HttpServletRequest类可以获得参数。
Servlet API有自己内置的会话跟踪支持,使用HttpSession对象既可。
它的setAttribute()方法绑定一对名字/值数据,把它存到当前会话中,如果会话中已经存在该名字责替换它,语法为:
public void setAttribute(String name,Object value)。
getAttribute()方法读取存储在会话中的对象,语法为:
public Object getAttribute(String name)。
getAttributeNames()方法返回存储在会话中的所有名字,语法为:
public String[] getAttributeNames()。
最后一个方法是removeAttribute()方法,它从会话中删除指定的信息,语法为:
public void removeAttribute(String name)。
HttpSession对象可以使用HttpServletRequest对象request的getSession(true)方法获得。
参数为true意味着如果不存在该对象则创建一个。
二、servlet规范定义的Servlet 生命周期
servlet有良好的生存期的定义,包括如何加载、实例化、初始化、处理客户端请求以及如何被移除。
这个生存期由javax.servlet.Servlet接口的init,service和destroy方法表达。
1、加载和实例化
容器负责加载和实例化一个servlet。
实例化和加载可以发生在引擎启动的时候,也可以推迟到容器需要该servlet为客户请求服务的时候。
首先容器必须先定位servlet类,在必要的情况下,容器使用通常的Java类加载工具加载该servlet,可能是从本机文件系统,也可以是从远程文件系统甚至其它的网络服务。
容器加载servlet类以后,它会实例化该类的一个实例。
需要注意的是可能会实例化多个实例,例如一个servlet类因为有不同的初始参数而有多个定义,或者servlet实现SingleThreadModel而导致容器为之生成一个实例池。
2、初始化
servlet加载并实例化后,容器必须在它能够处理客户端请求前初始化它。
初始化的过程主要是读取永久的配置信息,昂贵资源(例如JDBC连接)以及其它仅仅需要执行一次的任务。
通过调用它的init方法并给它传递唯一的一个(每个servlet定义一个)ServletConfig对象完成这个过程。
给它传递的这个配置对象允许servlet访问容器的配置信息中的名称-值对(name-value)初始化参数。
这个配置对象同时给servlet提供了访问实现了ServletContext接口的具体对象的方法,该对象描述了servlet的运行环境。
2.1初始化的错误处理
在初始化期间,servlet实例可能通过抛出UnavailableException 或者 ServletException异常表明它不能进行有效服务。
如果一个servlet抛出一个这样的异常,它将不会被置入有效服务并且应该被容器立即释放。
在此情况下destroy方法不会被调用因为初始化没有成功完成。
在失败的实例被释放后,容器可能在任何时候实例化一个新的实例,对这个规则的唯一例外是如果失败的servlet抛出的异常是UnavailableException并且该异常指出了最小的无效时间,那么容器就会至少等待该时间指明的时限才会重新试图创建一个新的实例。
2.2、工具因素
当工具(注:
根据笔者的理解,这个工具可能是应用服务器的某些检查工具,通常是验证应用的合法性和完整性)加载和内省(introspect)一个web应用时,它可能加载和内省该应用中的类,这个行为将触发那些类的静态初始方法被执行,因此,开发者不能假定只要当servlet的init方法被调用后它才处于活动容器运行状态(active container runtime)。
作为一个例子,这意味着servlet不能在它的静态(类)初始化方法被调用时试图建立数据库连接或者连接EJB容器。
3、处理请求
在servlet被适当地初始化后,容器就可以使用它去处理请求了。
每一个请求由ServletRequest类型的对象代表,而servlet使用ServletResponse回应该请求。
这些对象被作为service方法的参数传递给servlet。
在HTTP请求的情况下,容器必须提供代表请求和回应的HttpServletRequest和HttpServletResponse的具体实现。
需要注意的是容器可能会创建一个servlet实例并将之放入等待服务的状态,但是这个实例在它的生存期中可能根本没有处理过任何请求。
3.1、多线程问题
容器可能同时将多个客户端的请求发送给一个实例的service方法,这也就意味着开发者必须确保编写的servlet可以处理并发问题。
如果开发者想防止这种缺省的行为,那么他可以让他编写的servlet实现SingleThreadModel。
实现这个类可以保证一次只会有一个线程在执行service方法并且一次性执行完。
容器可以通过将请求排队或者维护一个servlet实例池满足这一点。
如果servlet是分布式应用的一部分,那么,那么容器可能在该应用分布的每个JVM中都维护一个实例池。
如果开发者使用synchronized关键字定义service方法(或者是doGet和doPost),容器将排队处理请求,这是由底层的java运行时系统要求的。
我们强烈推荐开发者不要同步service方法或者HTTPServlet的诸如doGet和doPost这样的服务方法。
3.2、处理请求中的异常
servlet在对请求进行服务的时候有可能抛出ServletException或者UnavailableException异常。
ServletException表明在处理请求的过程中发生了错误容器应该使用合适的方法清除该请求。
UnavailableException表明servlet不能对请求进行处理,可能是暂时的,也可能是永久的。
如果UnavailableException指明是永久性的,那么容器必须将servlet从服务中移除,调用它的destroy方法并释放它的实例。
如果指明是暂时的,那么容器可以选择在异常信息里面指明的这个暂时无法服务的时间段里面不向它发送任何请求。
在这个时间段里面被被拒绝的请求必须使用SERVICE_UNAVAILABLE (503)返回状态进行响应并且应该携带稍后重试(Retry-After)的响应头表明不能服务只是暂时的。
容器也可以选择不对暂时性和永久性的不可用进行区分而全部当作永久性的并移除抛出异常的servlet。
3.3线程安全
开发者应该注意容器实现的请求和响应对象(注:
即容器实现的HttpServletRequest和HttpServletResponese)没有被保证是线程安全的,这就意味着他们只能在请求处理线程的范围内被使用,这些对象不能被其它执行线程所引用,因为引用的行为是不确定的。
4、服务结束
容器没有被要求将一个加载的servlet保存多长时间,因此一个servlet实例可能只在容器中存活了几毫秒,当然也可能是其它更长的任意时间(但是肯定会短于容器的生存期)
当容器决定将之移除时(原因可能是保存内存资源或者自己被关闭),那么它必须允许servlet释放它正在使用的任何资源并保存任何永久状态(这个过程通过调用destroy方法达到)。
容器在能够调用destroy方法前,它必须允许那些正在service方法中执行的线程执行完或者在服务器定义的一段时间内执行(这个时间段在容器调用destroy之前)。
一旦destroy方法被调用,容器就不会再向该实例发送任何请求。
如果容器需要再使用该servlet,它必须创建新的实例。
destroy方法完成后,容器必须释放servlet实例以便它能够被垃圾回收。
三、serlvet为什么只需要实现doGet和doPost
Serlvet接口只定义了一个服务方法就是service,而HttpServlet类实现了该方法并且要求调用下列的方法之一:
doGet:
处理GET请求
doPost:
处理P