Java Servlet Specification Version 2.3
第一章 概要
1.1 什么是servlet
servlet是一种基于web组件的java技术,由容器管理,产生动态内容。象其他基于java的组件一样,servlet是不依赖平台的的java类,被编译为中间字节码,可被动态装载运行于支持java的web服务器上。这里说的容器,有时也称它为servlet引擎,提供Servlet功能的web服务器扩展,servelt通过一种由servlet容器实现的request/response范式(paradigm)与web客户机交互。
1.2什么是servlet容器
servlet容器是web服务器或应用服务器的一部分,它们提供处理处理request并发送response的网络服务,解析基于MIME的request,准备基于MIME的response。servlet容器包含并管理着servlet对象的一生。 servlet容器可以被嵌入web服务器主机,或者通过服务器本地扩展API安装为web服务器的一个组件。同时,它也可以被嵌入到支持web功能的应用服务器中。
所有的servlet容器必须支持以HTTP作为request和response的协议,但额外的协议如HTTPS可以支持。应支持的HTTP最低版本为HTTP/1.0,强烈议也支持HTTP1.1规范。
servlet容器可以在其执行环境中放置安全制约。Java2平台,标准1.2或者J2EE1.3环境,应当使用由java2平台定义的许可架构下的约束。例如,高端应用服务器可以限制线程对象的创建,确保容器内的其他组件免收影响。J2SE1.2是构建servlet容器的最低版本。
1.3 例子
以下是一系列典型的场景序列:
1.客户机(即,浏览器)访问web服务器,并发送HTTP请求。
2.web服务器接收到了这个请求,并向下传递给了servlet容器。servlet容器可以与web服务器运行在同一进程里,可以在也同一主机上的不同进程里,或者运行于不同主机上。
3.servlet容器使用servlet配置信息而决定调用哪一个servlet,同时向其传递request和response对象参数。
4.servlet利用request对象得到远端用户信息,HTTP POST参数是什么,以及其他相关数据。servlet执行其编码逻辑,并产生数据,通过response对象送回客户机。
5.一旦servlet完成处理request,容器应确保response对象被flush,并将控制权返回web服务器。
1.4 和其他技术的比较
功能上,servlet居于CGI和服务器扩展(如Netscape Server API或
Apache modules)之间。
但servlet还具有一些其他服务器扩展技术没有的先进性: .它们通常比CGI脚本运行快。
.它们使用很多web服务器都支持的标准API。
.它们具有所有java编程语言具有的优点,包括开发简单平台独立等。 .它们可以访问java平台大量的API。
1.5与J2EE的关系
Servlet API v2.3 是JavaTM 2 平台, 企业版 v1.31的必需的API 。servlet容器和发布servlet必须满足其他的需求在J2EE规范中都有描述。
第二章 Servlet接口
servlet接口是servlet主要抽象的API。所有servlet都需要直接实现这一接口或者继承实现了该接口的类。servlet API中有两个类实现了Servlet接口,GenericServlet和HttpServlet。大多数情况下,开发人员只需要在这两个类的基础上扩展来实现他们自己的Servlet。
2.1 request处理方法
servlet接口定义了service方法来处理客户机的请求。当容器将每个请求传递给servlet实例处理时都会调用该方法。为应付同时到达的请求,通常要求web开发人员编写的service方法可以多线程执行。
开发人员在不同线程内并发执行service方法来处理同时到达同一servlet的多个请求。
2.1.1 HTTP规范request处理方法
HttpServlet,实现了Servlet接口的抽象类,添加了一些附加的方法处理HTTP请求,由service方法自动调用。这些方法是: .doGet处理HTTP GET request .doPost处理HTTP POST request .doPut处理HTTP PUT request
.doDelete处理HTTP DELETE request .doHead处理HTTP HEAD request .doOption处理HTTP OPTION request .doTrace处理HTTP TRACE request
在开发HTTP Servlet的时候,开发人员一般关注doGet和doPost方法.其他方法在HTTP开发时很少用.
2.1.2 附加方法
doGet和doPost方法允许开发人员支持HTTP1.1特性.doHead()方法实际是一种特殊的doGet方法,它只返回由doGet产生的头信息.doOption
方法返回servlet支持的所有HTTP方法的信息.doTrace方法产生的响应包含TRACE请求发送的所有头实例.
对于仅支持HTTP/1.0的容器,只需支持doGet,doPost,doHead方法.因为HTTP/1.0没有定义PUT,DELETE,OPTIONS,TRACE方法.
2.1.3 条件Get支持
HttpServlet接口定义了getLastModified方法,目的是支持有条件的GET操作.一个有条件的GET操作所请求的资源只有在指定时间内被修改了的情况下才需要发送.在某些情形下,实现该方法可以更加有效地利用网络资源.
2.2 实例的数量
servlet声明包含在web应用配置描述内,控制着容器如何提供servlet实例。因为servlet不运行于分布式环境(缺省),容器为每个Servlet声明维护一个实例.然而,当servlet实现SingleThreadModel接口时,容器可以持有多个servlet实例来应付繁重的请求,但要保证一个实例每次只处理一个请求.
当servlet被发布到分布式应用时,容器可以在每个虚拟机中为每个servlet声明持有一个实例.如果实现SingleThreadModel,可以在每个虚拟机中为每个声明持有多个servlet实例.
2.2.1 注意单线程模式
SingleThreadModel只允许某一时刻只能有一个线程执行指定的servlet实例的service方法.应该注意到,此保证只针对servlet实例而言,因为容器可以选择池化这些对象.多个servlet实例可以同时访问的对象,例如HttpSession对象,在任意时候对多个servlet都是可用的,哪怕这些servlet实现了singleThreadModel.
2.3 servlet生命周期
servlet的生命周期定义了装载,实例化,初始化,处理客户机请求,卸载等等。用API表示生命周期就是init,service,和destroy方法.
2.3.1 装载和实例化
容器负责servlet的装载和实例化.它们可以发生在容器启动时,也可以延迟到容器认为需要该servlet处理request时.
当容器启动时,容器必须可以定位所需要的servlet类,并使用Java类装载机构装载servlet类.从本地文件系统中,或从远程文件系统中或者从其他网络服务中都可以装载servlet类. 装载完成后,容器实例化该类.
servlet对象被实例化后,容器必须在使用它处理客户机的请求之前初始化这个servlet实例.初始化的意义是srvlet对象可以读取一些持久性的配置数据,或者初始化某些重量级型资源(比如JDBC连接),或是执行某些一次性的行为.容器使用init方法并传递一个单例SrvletConf ig对象参数(每个servlet声明只有一个).该配置对象允许servlet访问应用配置信息中的初始化参数,同时提供了一个实现了ServletContext接口的对象来访问servlet运行环境的信息.
2.3.2.1 初始化的错误条件
在初始化期间,servlet实例可以抛出UnavailabelException或者ServletException异常.发生异常时不能将servlet放进可用服务中,容器必须释放它,因为未被成功初始化,所以无需调用destroy方法.
初始化失败后,应重新创建一个新的实例并初始化它.规则是,当UnavailabelException指示了最小不可用时间,容器必须等待这个最小时间才创建并初始化新的实例.
2.3.2.2 Tool Considerations
工具装载并内省web应用而触发类的静态初始化方法有别于调用servlet的init方法.在没有调用init方法前,开发人员不应假定sevlet是在一个活跃的servlet容器运行环境中。也就是说,servlet不应在静态初始化方法里试图建立数据库连接等.
2.3.3处理请求
当servlet被正确初始化后,容器就可以用它处理客户机的请求了.请求用ServletRequest类型的对象表示,servlet通过调用ServletResponse的对象上的方法填充该请求的响应。这些对象由service方法作为参数传递进来. 在HTTP请求中,容器提供的这些对象类型为HttpServletRequest和HttpServletResponse.
一个被容器放进服务序列的servlet实例也有可能在整个生命周期中都不处理请求.
2.3.3.1多线程
servlet容器可以向servlet的service方法发送并发请求.为了处理这些请求,开发人员必须在他们的service方法中遵守有关的多线程并发处理规则.
否则,一个可替代的办法是实现SingleThreadModel接口,它需要容器保证在service方法一次只有处理一个请求.容器通过序列化一个servlet上的请求来满足这个需求,还可以通过持有一个servlet实例池的方式.若servlet属于分布式应用的一部分,容器可以在应用分布的每个虚拟机上持有一个servlet实例池.
若servlet没有实现SingleThreadModel,而是使用synchronized关键字定义了service方法(或doGet,doPost),容器就不会使用实例池,但必须序列式地处理到达此servlet的请求,强烈建议开发人员不要同步service方法,否则会严重影响性能.
2.3.3.2 处理请求期间的异常
servlet在处理request期间可以抛出ServletException或UnavailableException异常. ServletException表明在处理request时发生了一些错误,此时容器应使用适当的方式清除此请求.
UnavailableException显示该servlet不能处理请求,有可能是暂时的,也有可能是永久性的.
如果是永久性的不可用,容器必须从服务中移除该servlet实例,并调用
它的destroy()方法,然后释放实例.
如果是暂时性的不可用,容器可以在不可用期间不发送任何请求到该servlet处理.
在此期间被容器拒绝的请求,必须以SERVICE_UNAVAILABLE (503)状态码返回,同时在HTTP响应头Retry-After中指明何时服务可用.
容器也可以选择忽略这两种可用性的区别,将所有UnavailableExceptions当作永久性的,而直接移除该servlet实例.
2.3.3.3 线程安全
request和response对象不是线程安全的,这意味着它们应该只能在处理该请求南叱棠诓渴褂?
在其他线程中使用被引用的request和response对象将引起不可预期的后果.
2.3.4 终止服务
容器并不需要永久持有servlet. servlet实例在容器中可能只存在数毫秒,也可能在整个容器的生命周期中都存在(可能是几天,个月,几年)
当容器决定应该从服务中移除servlet时,它调用servlet接口上的destroy()方法,这样,servlet可以释放它所使用的资源,或者存储任何持久性的状态.
在容器调用的destroy()方法之前.必须允许正在运行service方法的线程完成执行,或者延迟运行一段服务器定义的时限.
一旦servlet实例的destroy()方法开始被调用,容器不用再发送其他请求到该servlet实例处理.即使容器需要重新激活该servlet,它也必须重新创建一个实例。
当destroy()方法调用完成后,容器必须释放该实例,以便它可以被垃圾收集.
第三章 Servlet Context
3.1 介绍ServletContext接口
ServletContext接口定义了web应用的servlet视图.容器提供者有责任提供对ServletContext接口的实现.通过ServletContext对象,servlet可以记录日志,获得资源的URL引用,存储其他servlet也可以访问的属性.
ServletContext以Web服务器中一条已知的路径为根,例如,servlet context可以位于http://www.mycorp.com/catalog,所有以/catalog开始的请求被发送到与此ServletContext相关联的web应用.
3.2 ServletContext接口的范围
每个应用都会有一个ServletContext对象与之关联,当容器分布在在多个虚拟机上时,web应用在所分布的每个虚拟机上都拥有一个ServletContext实例.缺省情况下,ServletContext不是分布式的,并且只存在于一个虚拟机上。