Servlet 3.0 之 Servlet Interface

Servlet Interface 是Java Servlet API的核心抽象。所有的servlets都直接或者通过继承一个类来实现这个接口。Java Servlet APIGeneticServletHttpServlet这两个类实现了这个Servlet。大多数场景下,开发者只需继承HttpServlet来实现它们的servlets。

1. Request Handling Methods

基本的Servlet接口定义了一个service方法来处理客户端请求。这个方法每个请求都会执行,并且servlet容器把每个请求路由到一个servlet实例。

Web应用中的并发请求的处理通常需要Web的开发者在并发执行service的时候考虑如何处理多线程问题。

通常Web容器通过在不同线程并发执行service方法来处理并发请求到同一个servlet。

1.1 HTTP Specific Request Handling Methods

HttpServlet抽象子类在基础的Servlet接口之外增加了额外的方法,它们会被service自动调用来帮助处理基于HTTP的请求。

  • doGet来处理HTTP GET请求。
  • doPost处理HTTP POST请求。
  • doPut处理HTTP PUT 请求。
  • doDelete处理HTTP DELETE请求。
  • doHead 处理HTTP HEAD请求。
  • doOptions 处理HTTP OPTIONS请求。
  • doTrace 处理HTTP TRACE请求。

1.2 额外的方法

doPut和doDelete方法允许Servlet开发者支持需要这些特性的HTTP/1.1客户端。HttpServlet中的doHead方法是doGet的一种特殊格式,它仅返回doGet返回数据的头部数据 。doOptions方法返回servlet支持的方法。doTrace方法在TRACE请求中生成包含所有headers实例的响应。

1.3 有条件的GET支持

HttpServlet接口定义了一个getLastModified方法来支持有条件的GET操作。一个有条件的GET操作仅请求自上次特定时间以来修改过的资源。实现这些方法有助于高效利用网络资源。

2. 实例数量

Servlet的声明会控制servlet容器提供怎样的servlet实例,这些声明包括annotation,部署描述符。
对于不是部署在分布式环境中的servlet,servlet容器对于每一个servlet声明对应一个实例。然而对于实现了SingleThreadModel接口的servlet,servlet容器可以初始化多个实例来处理大量的请求负载和把请求序列化为一个特殊的实例。

在通过部署描述符部署的分布式应用中,servlet部署为应用的一部分,一个JVM中,容器仅可以一个servlet声明对应一个实例。然而,如果servlet在分布式应用中实现了SingleThreadModel接口,容器在每个JVM中会实现多个实例。

2.1 单线程模型的注意点

SingleThreadModel接口的使用确保在一个给定servlet实例的service方法中,一个时刻仅有一个线程在执行。值得注意的是这种保证仅适用于每个servlet实例,因为容器可以选择缓存对象。这些能在同一时刻获取一个以上的servlet的对象,比如HttpSession实例,在任一时刻对多个servlet都是可用的,包括实现SingleThreadModel接口的servlet。
推荐开发者采用其它方式来解决问题,而不是实现这个接口,比如避免使用实例变量或者使用同步块来获取资源。这个SingleThreadModel接口在此版本中将不建议使用。

2.2 Servlet生命周期

Servlet通过定义良好的生命周期来管理。生命周期决定servlet何时加载、初始化、处理客户端请求、退出service。生命周期通过javax.servlet.Servlet接口的API (init, service, destroy) 方法来实现。所有的servlet必须直接或者间接地通过GenericServlet或者HttpServlet抽象类来实现这三个接口。

2.2.1 加载&实例化

Servlet容器负责加载和实例化servlets。加载和实例化动作可能在容器启动时发生,或者推迟到容器需要使用servlets的时候。
当Servlets引擎启动,必要的Servlets类必须被容器初始化。Servlets容器用普通Java类的加载方式来加载Servlets。它们可能从本地文件系统、远端文件系统或者其它网络服务加载。
在Servlets类加载后,容器把它们实例化好,以备后续使用。

2.2.2 初始化

在Servlets实例化后,容器必须在它处理客户端请求之前初始化Servlets。初始化让Servlets能读取持久化配置信息,初始化耗时资源 (如 JDBC 连接), 以及执行其他一次性动作。容器通过调用Servlet接口的init方法,并传递给init一个实现了ServletConfig接口的唯一对象来初始化servlet实例。这个配置对象允许servlet获取Web应用配置信息中的name-value初始化参数。这个配置对象也允许servlet获取描述servlet运行时环境的对象(实现了ServletsContext接口)

2.2.2.1 初始化中的错误条件

在初始化期间,servlet实例能抛出一个UnavailableException或者ServletException。这种情况下,servlet一定不能放进活跃服务中,且必须在容器中被释放掉。destroy方法不会被调用,因为它被认为是失败的初始化过程。

在初始化失败后,一个新的实例可以被容器实例化&初始化。这个规则有个例外,就是当UnavailableException异常明确指出一个最小不可用时间时,容器必须等待这段时间过去了,再初始化一个新的servlet实例。

2.2.2.2 工具考虑

当一个工具调用init方法来加载并运行一个web应用时,静态初始化方法的触发十分重要。直到servlet接口的init方法被调用,开发者才应该假设一个servlet在活跃的容器运行时之中。比如,一个servlet在仅仅调用静态方法时,不应该建立DB链接或者企业JavaBean容器。

2.2.3 请求处理

在一个servlet被初始化好之后,servlet容器就可以用它来处理客户端请求。请求通过ServletRequest类型的请求对象到达服务。servlet通过调用ServletResponse类型对象的方法来填充response对象。这些对象被作为参数传递给servlet接口的service方法。

在一个HTTP请求过程中,容器提供的对象是HttpServletRequest和HttpServletResponse类型。
需要注意的是,一个被容器放进service中的servlet实例在它的生命周期中可以不处理请求。

2.2.3.1 多线程问题

一个servlet容器可能通过servlet的service方法发送多个并发请求。处理请求过程中,servlet开发者必须在service方法中处理好多线程的并发执行过程。

尽管不推荐,但是一个替代方案是开发者实现SingleThreadModel接口,让容器来保证一个时刻,service方法中仅有一个线程。servlet容器通过在servlet上排序请求来满足这个需求,或者维护一个servlet实例池。如果servlet是分布式应用的一部分,容器可以在每一个应用部署所在的JVM中维护一个servlet实例池。
对那些没有实现SingleThreadModel接口的servlets,如果service方法 (或者方法,如doGet,doPost) 定义是带有synchronized关键字,容器不能使用实例池这种方式,但必须让请求有序。由于性能的原因,强烈建议开发者不要使用为service方法加上同步关键字。

2.2.3.2 处理请求过程中的异常

一个servlet在请求处理过程中可能抛出ServletException或者UnavailableException。一个ServletException表明在处理请求过程中出现了一些错误,并且容器需要采取措施来清理这个请求。

一个UnavailableException表明一个servlet暂时或者永远不能处理请求。
如果UnavailableException提示永久不可用,容器必须从服务中移除这个servlet,调用它的destroy方法,释放servlet实例。任何由于这个原因被拒绝的服务必须返回404.

如果UnavailableException提示暂时不可用,容器可以在这段不可用时段内选择不分发任何请求。任何在这段时间被拒绝的请求必须返回带有503的response,并且带着Retry-After头。

容器也可以不区分永久性和暂时性,把它们全部视为永久不可用,因此需要移除哪些抛UnavailableException的servlet。

2.2.2.3.3 异步处理

有时候,一个filter或者servlet在产生response之前没有等到一个资源或者事件,它不能完成请求的处理。比如,在继续生成response之前,一个servlet可能需要等待一个可用的连接、远程服务的结果、JMS消息或者一个应用事件。在一个servlet中等待是效率很低的事情,因为这种阻塞操作会消耗线程和其它有限的资源。一个被高频调用的资源,比如数据库连接可能让很多线程阻塞等待获取连接,这就会造成线程饥饿以及整个web容器的低性能。

servlet3.0引入了异步处理请求的能力,线程可以返回给容器接着执行其它任务。一个请求上,当异步处理开始,其它线程或者回调可以生成response并调用complete,或者把请求分发出去,让请求通过AsyncContext.dispatch方法,可以运行在容器的上下文中。一个典型的异步处理事件顺序如下:

  • 1.Request被接受,并且通过普通filters的鉴权等动作之后传递给servlet。
  • 2.servlet处理请求参数或者内容来决定Request的类型。
  • 3.servlet给请求分配资源、数据,比如发送一个远程web服务或者加入队列等待JDBC连接。
  • 4.servlet返回,并不生成response。
  • 5.一些时间之后,请求的资源就绪后,处理那个事件的线程继续在同一个线程执行或者在容器中使用AsyncContext分配一个资源。

Java EE的某些特征仅仅对于那些执行初始请求的线程可用,或者通过AsyncContext.dispatch方法把请求分发给容器。
Java EE特征对另外一些通过AsyncContext.start(Runnable)方法直接在response操作的线程可用。

@WebServlet和@WebFilter注解有一个属性--asyncSupported(拥有默认值为false的)。当asyncSupported被置为true,应用可以调用startAsync在单独的线程里面启动异步处理,传递一个执行Request和response对象的应用给它,然后从容器中最初的线程中退出。这也就是说response将穿过与此相反的filter链。response直到AsyncContext的complete方法被调用才提交。如果异步任务在调用startAsync,已初始化的分发已经返回给容器之前执行那么应用负责处理对Request和response的并发访问。

把一个asyncSupported置为true的servlet分发给asyncSupport置为false的servlet是可以的。这种场景下,当不支持async的servlet的service方法退出,response将退出。并且容器负责调用AsyncContext的complete以便那些感兴趣的AsyncListener实例被通知到。AsyncListener.onComplete通知也能被filters利用来清理拥有的资源,以让异步任务顺利完成。

从一个异步servlet分发到一个异步servlet是非法的。然而抛IllegalStateException的决定与应用调用startAsync的时间点不同。这允许一个servlet作为异步或者同步的servlet。

应用等待的异步任务可能在不同的线程,而不是最初的请求中写response。这个线程对filter一无所知。如果filter想操作新线程中的response,它必须在处理原始请求时包装response,并且把包装的response传递给filter链的下一个,最后传递给servlet。如果response被包装,应用处理请求并且直接写回response。这里其实真正写的是包装类,比如任何加到response的输出仍然要被response的包装类处理。当应用在单独线程中读request时,并把输出添加到response,其实它真正地是从request包装类中读,往response的包装类中写,因此任何包装类的输出和输出将继续执行。

应用可以使用AsyncContext来把请求从新线程中分发给容器中的一个资源,如果它选择这么做。这能让在容器范围内使用JSP这类内容生成技术可行。

除了注解属性,我们还有下面的方法或者类:

  • ServletRequest
  • public AsyncContext startAync(ServletRequest req, ServletResponse res). 这个方法把请求放进异步模式中,而且用request和response对象和getAsyncTimeout返回的超时时间来初始化AsynContext。ServletRequest和ServletResponse参数必须与调用servlet的service的参数、filter的doFilter方法或者ServletRequestWrapper和ServletResponseWrapper的包装类的子类一致。调用这个方法可以确保在应用退出service方法时,response不会提交。response将在调用AsyncContext的AsyncContext.complete或者AsyncContext超时并且没有处理超时的监听者时提交。异步时间的计时器直到请求和相关的response已经从容器返回时才开始计时。AsyncContext能用来在异步线程中写response。它也能用来通知--response没有关闭和提交。

    如果请求在不支持异步操作的servlet或者filter的scope中,或者response被提交或者关闭,又或者在相同的分发过程中再次被调用,这些情况下去调用startAsync是非法的。调用startAsync时返回的AsyncContext稍后能被进一步的异步处理使用。调用AsyncContext.hasOriginalRequestResponse() 将返回false,除非被传递的ServletRequest和ServletResponse参数是原始对象或者没有携带应用提供的包装类。在请求放进异步模式之后,任何在外部方向的被调用的filter能使用这个返回值来作为提示:一些在它们内部调用的时期添加的一部分Request和response包装类可能需要在异步处理期间保留,并且它们相关的资源可能不会被释放。仅当初始化AsyncContext并且调用AsyncContext.getRequest()返回的ServletRequest不包含所谓的ServletRequestWrapper,应用在一个filter的内部调用期间的ServletRequestWrapper可以通过外部调用释放。对于ServletResponseWrapper实例也是一样。

  • public AsyncContext startAsync() 方法被提供来作为使用原始Request和response对象来处理异步请求的方便方法。值得注意的是,这个方法的调用者在调用之前应该flush包装过的response,以确保写到包装response的数据不会丢失。

  • public AsyncContext getAsyncContext() - 返回调用startAsync创建或者重新初始化的AsyncContext对象。如果请求没有被放进异步模式中,调用getAsyncContext是非法行为。

  • public boolean isAsyncSupported() -如果请求支持异步处理返回true,否则返回false。当请求被传递给不支持异步(通过委托注解或者声明)的servlet或者filter,Async支持功能会被disable。

  • public boolean isAsyncStarted() - 如果异步处理开始了返回true,否则返回false。如果在它被放进异步模式或者AsyncContext.complete被调用的时候请求通过AsyncContext.dispatch方法分发,这个方法返回false。

  • public DispatcherType getDispatcherType() - 返回请求的分发器类型。容器根据分发器的类型来选择需要应用在Request上的filter。仅符合分发器类型和url模式的filter会被应用到。允许为多个分发器类型配置好的filter查询它请求的分发器类型能让filter根据分发器类型对请求做不同的处理。请求的初始化分发类型是DispatcherType.REQUEST
    通过RequestDispatcher.forward(ServletRequest, ServletResponse) 或者 RequestDispatcher.include(ServletRequest, ServletResponse) 分发的分发类型是DispatcherType.FORWARD或者DispatcherType.INCLUDE,然而通过AsyncContext.dispatch方法分发的分发器类型是DispatcherType.ASYNC。最后请求的分发器类型被容器的错误处理器分发到错误页面,它就是DispatcherType.ERROR。

  • AsyncContext - 这个类代表了在ServletRequest之上开始的异步操作的执行上下文。AsyncContext通过上文描述符的ServletRequest.startAsync调用来创建和初始化。下面是一些在AsyncContext中的方法:
  • public ServletRequest getRequest() - 返回用来初始化调用startAsync方法返回的AsyncContext对象的request对象。
  • public ServletResponse getResponse() - 返回用来初始化调用startAsync方法返回的AsyncContext对象的response对象。
  • public void setTimeout(long timeoutMilliseconds) - 给异步处理过程设置超时时间,单位是毫秒。调用这个方法会覆盖容器设置的超时时间。如果超时时间没有通过调用setTimeout设置,将会采用容器默认值。小于等于0的值表示异步操作从不超时。一旦已初始化的容器在任一被调用的ServletRequest.startAsync方法返回给容器期间分发出去,超时间就在AsyncContext上生效。如果这个方法在已初始化容器在已开始的异步周期之后返回给容器,那么设置超时间将导致IllegalStateException.
  • public long getTimeout() - 获取与AsyncContext相关的超时时间,单位为毫秒。这个方法返回容器默认的超时时间或者通过setTimeout方法设置的超时时间。
  • public void addListener(asyncListener, req, res) - 给通过调用ServletRequest.startAsync方法的异步周期中的超时,错误和complete的通知注册监听者。异步监听者会按照添加到请求对象中的顺序被通知。当AsyncListener被通知,被传递给这个方法的request和response对象与AsyncEvent.getSuppliedRequest()和AsyncEvent.getSuppliedResponse()返回的对象一致。这些对象不应该被读或者写,因为额外的包装在AsyncListener注册时可能出现,可能被用来按顺序释放资源。如果这个方法在已初始化容器在已开始的异步周期之后返回给容器,或者一个新的异步周期开始之前,那么此调用将导致IllegalStateException。
  • public <T extends AsyncListener> createListener(Class<T> claszz) - 实例化AsyncListener类。返回的AsyncListener实例在通过调用addListener返回的AsyncContext注册之前还可以进一步定制。AsyncListener类必须实现无参构造函数用来初始化实例。这个方法支持应用到AsyncListener上的任一注解。
  • public void addListener(asyncListener) - 给通过调用ServletRequest.startAsync方法的异步周期中的超时,错误和complete的通知注册监听者。如果请求中startAsync(req, res)或者 startAsync()被调用,且当AsyncListener被通知到,相同的request和response也能从AsyncEvent中获取。request和response可能被包装过,也可能没有被包装过。异步监听器将会按照他们添加进请求的顺序被通知。如果这个方法在已初始化容器在已开始的异步周期之后返回给容器,或者一个新的异步周期开始之前,那么此调用将导致IllegalStateException。
  • public void dispatch(path) - 把用来初始化AsyncContext的request和response对象分发给指定路径的资源。指定路径是相对于初始化AsyncContext的ServletContext。所有request查询方法相关的路径必须反映出分发目标,虽然原始的请求URI,上下文路径,路径信息和查询字符串可以从request属性中获取。即使是多次分发以后,这些属性必须要反应原始路径元素。
  • public void dispatch() - 提供一个方便的方法来分发用来初始化AsyncContext的request和response对象。
    如果AsyncContext通过startAsync(ServletRequest, ServletResponse)初始化并且传递的请求对象是HttpServletRequest实例,那么分发就是HttpServletRequest.getRequestURI()返回的URI。否则分发就是上次容器分发的请求对象的URI。下面的例子code example1-1, code example1-2, code example1-3说明了不同场景下的分发目标URI:
    CODE EXAMPLE1-1:
REQUEST to /url/A
AsyncContext ac = request.startAsync();
...
ac.dispatch(): dose a ASYNC dispatch to /url/A

CODE EXAMPLE1-2:

REQUEST to /url/A
REQUEST to /url/B
getRequestDispatcher("/url/B").forward(request, response);
AsyncContext ac = request.startAsync();
...
ac.dispatch(): dose a ASYNC dispatch to /url/A
CODE EXAMPLE1-3:
REQUESR to /url/A
REQUESR to /url/B
getRequestDispatcher("/url/B").forward(request, response);
AsyncContext ac = request.startAsync(request, response);
...
ac.dispatch(): dose a ASYNC dispatch to /url/B
  • public void dispatch(ServletContext context ,String path) - 把用来初始化AsyncContext的request和response对象分发给ServletContext中指定路径相关的资源上。

  • 对于上面定义的有不同变量的3个dispatch方法,调用这个方法,把request和response对象传递给容器管理的线程(分发动作会在此线程中执行)就立即返回。请求对象的分发器类型被置为ASYNC。
    不像RequestDispatcher.forward(ServletRequest, ServletResponse)分发,response对象的缓存和头信息不会被重置,即使response对象已经被提交,分发它也是合法的。在request和response对象之上的控制被委托给分发目标,当分发对象完成执行,response对象将会关闭,除非ServletRequest.startAsync()或者ServletRequest.startAsync(ServletRequest, ServletResponse)被调用。如果任何分发方法在已初始化的容器分发startAsync调用返回容器之前被调用,那么这个调用在已初始化分发返回容器之后才生效。调用AsyncListener.onComplete(AsyncEvent), AsyncListener.onTimeout(AsyncEvent)和AsyncListener.onError(AsyncEvent)将会推迟,直到已初始化容器分发已经返回给容器。在你每一个通过ServletRequest.startAsync方法调用开始的异步周期,最多有一个异步分发操作。在同一个异步周期内试图执行额外的异步分发操作是非法的,并且会导致IllegalStateException。如果startAsync在分发请求之后调用,任何一个dispatch方法调用都有上述同样的限制。

  • 任何在dispatch方法执行过程中会出现的错误或者异常必须被容器按下述方式捕获并处理。

    1. 为所有用ServletRequest注册的AsyncListener实例触发AsyncListener.onError(AsyncEvent)方法。
      AsyncContext被创建并且通过AsyncEvent.getThrowable()准备好Throwable异常。
    2. 如果没有监听者调用AsyncContext.complete或者任何一个AsyncContext.dispatch方法,那么用错误码 (HttpServletResponse.SC_INTERNAL_SERVER_ERROR) 来执行错误dispatch, 并且把与请求对象中相等的RequestDispatcher.ERROR_EXCEPTION属性值--Throwable准备好。
    3. 如果没有匹配的错误页面,或者错误页面并没有调用AsyncContext.complete(),和任一一个AsyncContext.dispatch 方法,那么容器必须调用AsyncContext.complete.
  • public boolean hasOriginalRequestAndResponse() - 这个方法检查调用ServletRequest.startAsync()返回的AsyncContext是否使用原始request和response对象初始化,或者它是否调用ServletRequest.startAsync(ServletRequest, ServletResponse)来初始化以及ServletRequest和ServletResponse参数不携带任何应用提供的包装层,这种情况将会返回true。如果AsyncContext使用方法ServletRequest.startAsync(ServletRequest, ServletResponse),并用request和response包装对象作为参数来初始化,它返回false。这些信息将在内部顺序中被filter使用来决定在内部调用(需要在异步操作期间保留或者释放)期间它们添加的是否是request和response包装对象。

  • public void start(Runnable r) - 这个方法让容器分发一个线程(很可能从线程池中获取)来运行指定的Runnable。容器可以传递合适的上下文信息给Runnable。

  • public void complete() - 如果Request.startAsync被调用,那么这个方法必须被调用来完成异步处理并提交、关闭response。如果request被分发给不支持异步过程的servlet,或者被AsyncContext.dispatch调用但并不继续调用startAsync的目标servlet,complete方法能被容器调用。这个case中一旦servlet的service退出,容器负责调用complete方法。如果startAsync没有被调用,IllegalStateException必须抛出。在调用ServletRequest.startAsync()或者ServletRequest.startAsync(ServletRequest, ServletResponse)之后,且调用一个dispatch方法之前,任何时间调用此方法都是合法的。如果这个方法在已初始化同期调用startAsync的分发返回给容器之前调用,那么直到已经初始化的分发返回给容器之后本次调用才生效。AsyncListener.onComplete(AsyncEvent)也将会延迟触发,直到已经初始化的分发返回给容器之后。

  • ServletRequestWrapper
  • public boolean isWrapperFor(ServletRequest req) - 递归检查包装器是否包装了给定ServletRequest,如果是返回true,否则返回false。
  • ServletResponseWrapper
  • public boolean isWrapperFor(ServletResponse req) - 递归检查包装器是否包装了给定ServletResponse,如果是返回true,否则返回false。
  • AsyncListener
  • public void onComplete(AsyncEvent event) - 用来通知在ServletRequest开始的异步操作完成的监听者。

  • public void onTimeout(AsyncEvent event) - 用来通知在ServletRequest开始的异步操作超时的监听者。

  • public void onError(AsyncEvent event) - 用来通知在ServletRequest开始的异步操作错误的监听者。

  • public void onStartAsync(AsyncEvent event) - 用来通知调用任意一个ServletRequest.startAsync方法调用后,一个新的异步周期初始化的监听者。对应正在重新初始化的异步操作AsyncContext可以通过在给定的event对象中调用AsyncEvent.getAsyncContext获得。

  • 在异步操作超时的事件中,容器必须执行以下步骤:

    1. 在ServletRequest上异步操作初始化期间注册的所有监听者实例上调用AsyncListener.onTimeout方法。
    2. 如果没有监听者调用AsyncContext.complete()方法或者任何一个AsyncContext.dispatch方法,那么用与HttpServletResponse.SC_INTERNAL_SERVER_ERROR等值的状态码去执行一次错误分发。
    3. 如果没有找到匹配的错误页面,或者错误页面没有调用AsyncContext.complete() 或者任何AsyncContext.dispatch方法,那么容器必须调用AsyncContext.complete()。
  • JSP中的异步处理默认不会被支持,因为它用于内容生成并且异步处理必须在内容生成之前完成。这取决于容器怎样处理这种情况。一旦所有的异步处理结束,使用AsyncContext.dispatch分发给JSP页面能被用来生成内容。

  • 下图1-4是各种不同异步操作的状态转换的图形说明。

图1-4 异步操作状态码转移图

2.3.3.4 线程安全

不像startAsync和complete函数,request和response对象的实现并不被保证线程安全。这就意味着他们应该只在请求处理的线程scope中或者应用能保证线程安全地获取request和response的情况下使用。

如果应用创建的线程使用容器管理的对象,比如request或者response对象,那些对象必须保证仅在他们的生命周期中被获取。需要注意的是startAsync和complete方法是线程安全的,但是request和response对象不是线程安全的。如果它们在多线程中被获取,获取过程需要被同步或者通过添加线程安全的包装类来实现。比如同步获取request属性获取的方法,或者在线程中为response对象使用本地输出流。

2.3.4 服务结束

容器并不要求在任意特殊时段保持一个servlet被加载。一个servlet实例在容器里保持活跃,可以是在毫秒时段中,可以是在servlet容器的生命周期中,也可以两者间的任意时间段。

当servlet决定一个servlet应该从服务中移除,它调用Servlets接口的destroy方法来让一个servlet释放它持有的任何资源和保存任何持久化状态。比如,当它想保存内存资源或者关闭的时候就可以调用destroy。

在servlet容器调用destroy方法之前,它必须让servlet的service方法里并发执行的任何线程执行完毕或者超出服务器设定的时间限制。

一旦servlet实例上的destroy方法被调用,容器不再把请求分发给servlet实例。如果容器需要再次使用servlet,它必须用此servlet类的新实例。

在destroy方法完成后,servlet容器必须释放servlet实例以便后面的GC。

翻译自 Java Servlet Specification
Version 3.0 Rev a
Author:Rajiv Mordani
Date: December 2010

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容