jsr 之 servlet 2.5 第二章

java specification requirement  之 servlet 2.5 第二章
无责任翻译by潘星臣 (casazaft)
The Servlet Interface

    Servlet 接口是Java Servlet API的核心抽象。所有的servlet都必须实现(implement)Servlet接口,或者是拓展(extending)一个已经实现该接口的类。在Java Servlet API中,已经提供了两个实现servlet接口的类,它们是GenericServlet和HttpServlet。在大多数情况下,开发者是通过拓展HttpServlet类来实现他们的servlets。

    2.1 Request Handling Methods(处理请求的方法)

    基本的Servlet 接口定义了一个叫做service的函数用来处理客户端请求。当servlet容器将请求转给指定的servlet实例时,实例中的service函数就被调用。

    对于web应用程序来说,要处理并发请求,通常要求开发者在设计servlet的时候使service函数能够运行在多线程的情况下。

    通常来说,servlet容器处理针对同一个servlet实例的并发请求,是通过在不同线程中执行这个实例的service函数来实现的。

    2.1.1 Http Specific Request Handling Methods(处理http请求的方法)

    HttpServlet 抽象子类在Servlet interface的基础上增加了一些额外的方法。当HttpServlet类处理基于http的请求时,这些方法能够自动地被service()方法调用。这些方法包括:

    doGet    用于处理HTTP GET 请求
    doPost    用于处理HTTP POST 请求
    doPut    用于处理 HTTP PUT 请求
    doDelete 用于处理 HTTP DELETE 请求
    doHead   用于处理 HTTP HEAD 请求
    doOptions 用于处理HTTP OPTIONS 请求
    doTrace 用于处理 HTTP TRACE 请求

    大部分情况下,开发基于http的servlet,开发者只需要考虑doGet和doPost 方法 。其他的方法要求程序员非常熟悉http编程才行。

    2.1.2    附加的函数
    doPut和doDelete 方法容允许servlet开发者支持HTTP/1.1客户端的相应特性。

    在HttpServlet中,doHead方法是doGet的一个特例,它只返回doGet方法产生的http header。

    doOptions方法告诉客户端,本servlet支持哪些HTTP方法。

    doTrace方法产生一个应答,该应答包含所有通过TRACE发送的请求的header的实例。

    2.1.3    对条件化GET的支持

    HttpServlet接口定义了getLastModified方法,用于支持条件化的GET操作。一个条件化GET操作是:当且仅当一个资源在特定的时间被修改时才被发送。在适当的情况下,实现这个方法能够助益于网络资源(如带宽)的高效利用。

    2.2 实例的数量

    servlet 声明(declaration)是web应用程序部署描述符(deployment descriptor,在第十三章可见)的一部分,控制着servlet容器如何提供servlet实例。

    如果一个servlet部署在非分布式环境里(默认情况),那么servlet容器对每一个servlet声明仅使用一个servlet实例。但是,如果一个servlet实现了SingleThreadModel接口,那么servlet容器将实例化它的多个实例,以应对繁重的请求负载,并串行地将请求分配给每个实例去处理。

    当一个servlet在部署描述符里被标记为可“分布式”时,容器对每个JVM都使用一个servlet实例。但是,当一个“可分步式”的servlet实现了SingleThreadModel接口,容器在每个JVM中都会实例化它的多个实例。

    2.2.1 详解Single Thread Model
    使用SingleThreadModel保证了对于给定的servlet实例的 service 函数,在同一时间内只被一个线程执行。值得注意的是,这种保证只适用于每个servlet实例,因为容器可能会池化一些对象。这些对象在同一时间内能被多个servlet实例访问,例如 HttpSession 对象,在每一时刻都可能被多个servlet对象使用到,包括那些实现了SingleThreadModel接口的 servlet。因此建议开发者使用别的方式来解决线程安全问题,例如避免使用全局变量,或者是使用代码同步块(synchronize)来访问需要线程安全的资源。SingleThreadModel接口在这个版本的规格说明中已经被标记为过时的。

    2.3 Servlet 生命周期
    一个 servlet 是通过一个良好定义的生命周期来被管理的,包括 载入、实例化、初始化、处理客户端请求以及停止服务。这个生命周期表达在API里就是javax.servlet.Servlet接口里的: init(),service(),destory() 方法。所有的 servlet 都必须直接实现,或者是间接地通过GenericServlet、HttpServlet抽象类实现这个接口。

    2.3.1    载入和实例化
    servlet容器负责载入并实例化servlet。可以在容器启动的时候就载入并实例化servlet,也可以延迟直到容器决定要用这个 servlet 来服务一个请求。
    当 servlet 引擎启动,所需的servlet必须被容器找到。servlet容器载入servlet类是通过一个普通的java class 载入装置。servlet 的来源可以是本地文件系统,远程文件系统,或者是其他网络服务。
    载入了 Servlet 类以后,容器实例化它以备可用。

    2.3.2    初始化
    servlet 对象被实例化后,容器首先对其进行初始化。初始化使得 servlet 能够读取一个持久性配置文件,初始化费时的资源(例如基于JDBC的数据库连接),并执行一些一次性的活动。为了初始化 servlet 实例,容器需要调用 Servlet 接口的 init()函数,以及一个唯一的,实现了ServletConfig接口的对象(每个servlet声明有一个)。这个称之为configuration的对象允许 servlet 访问Web应用程序配置信息里的“名字-值”参数对。    configuration对象也使得 servlet 能够访问到一个对象,这个对象实现了 ServletContext 接口,能够提供 servlet 所处的运行环境的信息。参看第四章里"Servlet Context"以获得关于 ServletContext 接口的更多信息。

    2.3.2.1    初始化时的错误处理
    在整个初始化过程中,servlet 实例可以抛出一个 Unavailable 异常或者是一个 Servlet 异常。在这种情况下,servlet 不能被投入使用,并且必须被容器释放。destory 函数在这种不成功的初始化中不会被调用。
    在一次失败的初始化后,容器可能会实例化和初始化一个新的实例。关于这条规则的例外是:当一个 Unavailable 异常指示了一个“不可用”的最小时间期限后,容器必须等待这个时间,然后才能创建和初始化一个新的 servlet 实例。

    2.3.22    工具问题
    当一个工具被装载并内省web应用程序时,所触发的静态初始化函数必须要与 init() 函数调用区别开来。在 Servlet 接口的 init() 函数被调用以前,开发者不能认为一个 servlet 是一个活动的容器进行时。例如,一个 servlet 不会试图建立一个数据库连接或是连接Enterprise JavaBeans,直到静态的初始化函数被调用。

    2.3.3    处理请求
    servlet 被初始化后,将会被容器拿来处理客户端请求。请求,这一概念,是通过 ServletRequest 这类对象来表达的。servlet 通过调用 ServletResponse 类型的对象的方法来产生“应答”。这些对象都是由容器提供的,作为参数传递给 Servlet 接口的 service() 函数。

    对于 HTTP 请求,容器提供的对象是 HttpServletRequest 和 HttpServletResponse 。
    
    注意,放置在 servlet 容器里的一个 servlet 实例可以在其生命周期内都不被用来处理请求。

    2.3.3.1    多线程问题
    容器可能会发送并发的请求给 servlet 的  servlce() 函数。为处理这种请求,Servlet 开发者必须为 service() 函数的多线程并发处理做好充分的准备。
    尽管有一种方法是不被推荐的,那就是开发者可以通过实现 SingleThreadModel 接口,强制要求容器保证:每一时间,service() 函数只有一个请求的线程在工作。容器为了实现这一要求,可能会通过“序列化”的方式将请求排队发给 servlet,或者是使用一个 servlet 实例池。如果 servlet 是一个分布式web应用程序的一部分,那么容器会在每个JVM里都会使用一个 servlet 实例池。
    如果 servlet 不实现 SingleThreadModel 接口,而 service() 函数(或者是 HttpServlet 抽象类里的 doGet()、doPost() 函数,它们都会分派给 service())使用了 synchronized 关键字,那么容器就不能使用 servlet 实例池这个途径,而必须采用序列化请求。在此强烈建议开发者不要 synchronized 这个 service()函数,或者是分派给service()的函数,因为这样会对性能造成损耗。
    
    2.3.3.2 在处理请求时的异常
    在处理客户端请求时,servlet 可能会抛出 ServletException 或者 UnavailableException 异常。一个 ServletException 信号表示:在处理的过程中发生了一些错误。容器会选择妥当的方法来清理请求。
    一个 UnavailableException 信号表示:servlet 暂时地或永久地不能处理请求。
    如果 UnavailableException 表示的是永久的不可用,那么容器就调用 servlet 的 destory() 函数,将servlet移出服务,并释放实例的内存。任何请求容器都会返回一个 SC_NO_FOUND(404) 应答。
    如果 UnavailableException 表示的是暂时的不可用,那么容器在这个不可用的期间不会传送任何请求给 servlet 。在此期间,对于任何请求容器会返回一个 SC_SERVICE_UNAVAILABLE(503) 应答状态,以及一个"稍后请重试"的报头。
    容器可以选择忽略永久性和暂时性不可用的区别,将所有的 UnavailableException 视为永久不可用。籍此,在 service() 中抛出的任何 UnavailableException 都会导致 servlet 被移出容器。
    
    2.3.3.3    线程安全性
    request和response 的实现对象并没有保证线程安全。这意味着这些对象只能在处理请求的线程的生命周期内使用。
    在其他线程内引用 request 和 response 对象会导致不预期的结果。如果应用程序产生的线程使用了这些容器管理的对象,如request和response,那么这些对象只能在 servlet 的 service()函数的作用期内被访问,并且这些线程本身必须在 servlet 的 service()函数里也有一个生命周期,在 service()函数结束后再访问这些对象会导致不预期的后果。注意,request 和 response 对象并没有线程安全。如果这些对象被多线程访问,那么访问必须要进行同步化(synchronized)或者通过包装器(wrapper)来加入线程安全性。例如,同步化函数调用来访问request对象,或者在线程里为response对象使用一个本地输出流。

    2.3.4 停止服务
    servlet 容器并不要求一直保持一个 servlet 被加载。一个 servlet 实例可以在容器里保持激活数毫秒到数年的时间。
    当容器决定 servlet 需要被移除,它就调用 Servlet 接口的 destroy() 函数来允许 servlet 释放资源以及保存状态。例如,容器会在在需要保留内存资源或者是关闭的时候,做这些工作。
    在 servlet 容器调用 destroy() 函数之前,它允许任何并发运行在 servlet 的 service()函数里的线程完成执行,或者是超出服务器定义的等待时间。
    一旦 destroy() 函数被调用,容器就不再传递任何请求到这个 servlet 实例。如果容器需要再次使用这个 servlet ,它必须实例化这个 servlet 类的一个新实例。
    在 destroy() 函数执行完毕,容器将释放这个 servlet 实例,这个实例就可以被垃圾处理机制选中处理。
    
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值