一、servlet的接口
Servlet接口是Java Servlet API的重要抽象。所有的servlet直接实现这个接口,或者更常见的是扩展实现这个接口的一个类。在Java Servlet API中有两个类实现了Servlet接口,它们是GenericServlet和HttpServlet。在大多数情况下,开发者将扩展HttpServlet来实现它们的servlet。
基本的Servlet接口定义一了个处理客户端请求的service方法。当servlet容器将第个请求路由到servlet实例时会调用这个方法。处理web应用程序的并发请求,通常需要web开发者设计的servlet,能够在特定的时间,在service方法内处理多线程的执行,一般来说web容器处理到同一个servlet的并发请求是通过在不同线程中并发执行service方法来实现的。
二、HTTP特定的请求处理方法
HttpServlet抽象子类在基本的Servlet接口中增加了额外的方法,这些方法可以被service方法自动调用帮助处理基于HTTP的请求。这些方法是:
通常在开发基于HTTP的servlet时,Servlet开发者只需要关心自己的doGet和doPost方法。其他方法被认为是非常熟悉HTTP编程的程序员使用的方法。
doPut和doDelete方法允许Servlet开发者支持使用HTTP/1.1特性的客户端。HttpServlet中的doHead方法是一种特殊形式的doGet方法,只返回由doGet方法产生的头信息。doOptions方法响应由servlet支持的HTTP方法。doTrace方法生成的响应,包含TRACE请求中发送的所有头信息的实例HttpServlet接口中定义了getLastModified方法,支持有条件的GET操作。有条件的GET操作请求的资源只有在指定时间内被修改了才需要发送。在适当的情形,实现该方法可以帮助有效地利用网络资源。
servlet实例的数量:
因为servlet不运行于分布式环境(默认),servlet容器必须为每个servlet声明使用一个实例。然而,对于实现了SingleThreadModel接口的servlet,servlet容器可能实例化多个实例来处理沉重的的请求负载和序列化请求到特定的实例。
在作为应用程序一部分部署的servlet在部署描述符中标记为distributable的情况下,容器可能在每个Java虚拟机(JVM)的每个servlet声明中只有一个实例。但是,如果在分布式应用中的servlet实现了SingleThreadModel接口,容器可能在容器的每个JVM中实例化多个servlet实例。
三、Servlet生命周期Servlet通过明确的生命周期定义来管理,这个生命周期定义了servlet如何加载并实例化、初始化、处理客户端请求,以及从服务中除去。
生命周期体现为API中javax.servlet.Servlet接口的init、service,destroy方法。所有的servlet必须直接或间接的通过GenericServlet或HttpServlet抽象类来实现。
1、加载和实例化
Servlet容器负责加载和实例化Servlet。加载和初始化可以发生在容器启动时,或者延迟直到容器决定servlet需要服务请求的时候。
当servlet引擎启动时,servlet容器必须加载需要的servlet类。Servlet容器使用普通的java类加载机制加载servlet类。加载可以从本地文件系统,远程文件系统,或者网络服务。
在加载servlet类之后,容器会实例化它来使用。
2、初始化
在servlet对象被实例化之后,容器必须在它处理客户端请求之前初始化它。初始化提供以便servlet可以读取持久化的配置数据,初始化昂贵的资源(例如JDBC的连接),和执行其他一次性的活动。容器通过调用唯一的实现了ServletConfig接口的对象为参数的Servlet接口的init方法初始化servlet实例。配置对象允许servlet访问Web应用程序配置信息中的name-value初始化参数。配置对象也提供给servlet可以访问的描述servlet运行时环境信息对对象(实现了ServletContext接口)。
初始化过程中,servlet实例可以抛出UnavailableException或ServletException。在这种情况下,servlet一定不会被置为有效的服务,并且一定会被容器释放掉。destroy方法没有被调用,因为它被认为是不成功初始化。
在初始化失败后,容器可以实例化和初始化一个新的实例。这种规则的例外是,当用UnavailableException表示最小不可用时间,容器必须在创建和初始化新的servlet实例之前等待这段时间。
在servlet被正确地初始化之后,servlet容器可以用它来处理客户端请求。请求由ServletRequest类型的request对象表示。Servlet通过调用ServletResponse对象提供的方法填充该请求的响应。这些对象作为参数传递给Servlet接口的service方法。
在HTTP请求的情况下,容器提供HttpServletRequest和HttpServletResponse类型的对象。
注意,servlet容器放置到服务中的servlet实例,可以在生命周期中不处理任何请求。
Servlet容器可以通过servlet的service方法发送并发请求。为了处理这些请求,servlet开发者必须为service方法中的多线程并发处理做好足够的准备。
虽然不建议,开发者的一种选择是实现SingleThreadModel接口,这样容器保证在service方法中在同一时间只有一个请求线程。Servlet容器可以通过序列化servlet上的请求来满足这个需求,或者维护一个servlet实例池。如果servlet是Web应用程序的一部分,并且已经被标记为分布式,那么容器可以在每个应用程序分布式访问的JVM中维护servlet实例池。
对于没有实现SingleThreadModel接口的servlet,如果service方法(或者由HttpServlet抽象类的service方法转发的doGet或doPost这类方法)已经使用synchronized关键字进行定义,servlet容器不能使用实例池方法,但是必须序列化传入的请求。在这种情况下,强烈建议开发者不要同步service方法(或者这个方法转发的方法),因为对性能产生不利的影响。
Servlet在服务请求期间可能抛出ServletException或者UnavailableException。ServletException表示在处理请求期间发生了一些错误,容器应该采取适当的措施来清理请求。
UnavailableException表示servlet不能处理请求,暂时性的或者永久性的。
如果UnavailableException表示的是永久性的不可用,那么servlet容器必须从服务中移除servlet,调用destroy方法,并释放servlet实例。由于这个原因容器拒绝任何请求,必须返回SC_NOT_FOUND (404)响应。
如果UnavailableException表示的是暂时性的不可用,容器可以选择在这个暂时不可用期间内不向它路由任何请求。在此期间任何被容器拒绝的请求,必须返回SC_SERVICE_UNAVAILABLE (503)响应以及Retry-After头信息,指明不可用将何时终止。
容器可以选择忽略永久性和暂时性不可用的区别,把所有的UnavailableException看作永久性的,因此从服务中移除抛出UnavailableException的servlet。
请求和响应对象的实现不保证线程安全。这意味着它们只应该在请求处理线程的作用域内被使用。
请求和响应对象的引用不应该交给在其他线程里运行的对象,这样会导致行为的不确定性。如果应用程序创建的线程使用了容器管理的对象,例如请求或者响应对象,这些对象必须只在servlet的service生命周期中访问,并且这些线程的生命周期必须处于servlet的service方法的生命周期之内,因为在service方法结束后,再访问这些对象,可能导致不确定的问题。注意请求和响应对象不是线程安全的。如果在多线程中访问这些对象,访问必须同步,或者通过包装来添加线程安全,例如,同步调用访问请求属性的方法,或者在线程内给响应对象使用一个本地的输出流。
Servlet容器不需要在任何特定的时间内保持servlet被装载。Servlet实例可以在servlet容器中的一段毫秒内保持活动,或者和servlet容器的寿命一样(可以是几天,几个月或几年),或者任何之间的时间。
当servlet容器确定要将servlet从服务中移除时,会调用Servlet接口的destroy方法,使servlet释放任何它使用的资源,以及保存任何持久化的状态。例如,容器可以在需要节约内存资源时这样做,或者当它被关闭时。
在servlet容器调用destroy方法之前,它必须允许任何当前正在运行servlet的service方法的线程完成执行,或者超过服务器定义的时间限制。
一旦servlet实例的destroy方法被调用,容器就不会把其它请求路由给那个servlet实例。如果容器需要再次启用servlet,它必须重新new一个此servlet类的实例。
在destroy方法执行完毕后,container必须释放这个servlet实例,以便它可以被垃圾回收。
更多文章见:http://www.16boke.com