Java Servlet工作原理问答

标签: 基础技术 Servlet 问答 | 发表时间:2015-11-03 04:08 | 作者:honoka
出处:http://www.importnew.com

导读

本文来自stackoverflow的问答,讨论了Java Servlet的工作机制,如何进行实例化、共享变量和多线程处理。

问题:Servlet是如何工作的?Servlet 如何实例化、共享变量、并进行多线程处理?

假设我有一个运行了大量 Servlet 的 web 服务器。通过 Servlet 之间传输信息得到 Servlet 上下文,并设置 session 变量。

现在,如果有两名或更多使用者向这个服务发送请求,接下来 session 变量会发生什么变化?究竟是所有用户都是用共同的变量?还是不同的用户使用的变量都不一样?如果是后者,服务器如何区分不同用户?

另一个相似的问题,如果有 *n* 名用户访问一个特定的 Servlet,那么该 Servlet 是仅在第一个用户首次访问的时候实例化,还是分别为每个用户实例化?

回答(BalusC):

ServletContext

当 Servlet 容器(比如 Apache Tomcat)启动后,会部署和加载所有 web 应用。当web 应用被加载,Servlet 容器会创建一次 ServletContext,然后将其保存在服务器的内存中。web 应用的 web.xml 被解析,找到其中所有 servletfilterListener@WebServlet@WebFilter@WebListener 注解的内容,创建一次并保存到服务器的内存中。对于所有过滤器会立即调用 init()。当 Servlet 容器停止,将卸载所有 web 应用,调用所有初始化的 Servlet 和过滤器的 destroy() 方法,最后回收 ServletContext 和所有 Servlet、Filter 与 Listener 实例。

当问题中的 Servlet 配置的 load-on-startup 或者 @WebServlet(loadOnStartup) 设置了一个大于 0 的值,则同样会在启动的时候立即调用 init() 方法。“load-on-startup”中的值表示那些 Servlet 会以相同顺序初始化。如果配置的值相同,会遵循 web.xml 中指定的顺序或 @WebServlet 类加载的顺序。另外,如果不设置 “load-on-startup” 值, init() 方法只在第一次 HTTP 请求命中问题中的 Servlet 时才被调用。

HttpServletRequest 与 HttpServletResponse

Servlet 容器附加在一个 web 服务上,这个 web 服务会在某个端口号上监听 HTTP 请求,在开发环境中这个端口通常为 8080,生产环境中通常为 80。当客户端(web 浏览器)发送了一个 HTTP 请求,Servlet 容器会创建新的 HttpServletRequest HttpServletResponse 对象,传递给已创建好并且请求的 URL 匹配 url-patternFilterServlet 实例中的方法,所有工作都在同一个线程中处理。

request 对象可以访问所有该 HTTP 请求中的信息,例如 request header 和 request body。response 对象为你提供需要的控制和发送 HTTP 响应方法,例如设置 header 和 body(通常会带有 JSP 文件中的 HTML 内容)。提交并完成HTTP 响应后,将回收 request 和 response 对象。

HttpSession

当用户第一次访问该 web 应用时,会通过 request.getSession() 第一次获得 HttpSession。之后 Servlet 容器将会创建 HttpSession,生成一个唯一的 ID(可以通过 session.getId() 获取)并储存在服务器内存中。然后 Servlet 容器在该次 HTTP 响应的 Set-Cookie 头部设置一个 Cookie,以 JSESSIONID 作为 Cookie 名字,那个唯一的 session ID 作为 Cookie 的值。

按照 HTTP cookie 规则(正常 web 浏览器和 web 服务端必须遵循的标准),当 cookie 有效时,要求客户端(浏览器)在后续请求的 Cookie 头中返回这个 cookie。使用浏览器内置的 HTTP 流量监控器,你可以查看它们(在 Chrome、Firefox23+、IE9+ 中按 F12,然后查看 Net/Network 标签)。Servlet 容器将会确定每个进入的 HTTP 请求的 Cookie 头中是否存在名为 JSESSIONID 的 cookie,然后用它的值(session ID)从服务端内存中找到关联的 HttpSession

你可以在 web.xml 中设置 session-timeout ,默认值为 30 分钟。超时到达之前 HttpSession 会一直存活。所以当客户端不再访问该 web 应用超过 30 分钟后,Servlet 容器就会回收这个 session。后续每个请求,即使指定 cookie 名称也不能再访问到相同的 session。Servlet 容器会创建一个新的 Cookie

另一方面,客户端上的 session cookie 有一个默认存活时间,该事件和该浏览器实例运行时间一样长。所以,当客户端关闭该浏览器实例(所有标签和窗口)后,这个 session 就会被客户端回收。新浏览器实例不再发送与该 session 关联的 cookie。一个新的 request.getSession() 将会返回新的 HttpSession 并设置一个拥有新 session ID 的 cookie。

概述

  • ServletContext 与 web 应用存活时间一样长。它被所有 session 中的所有请求共享。
  • 只要客户端一直与相同浏览器实例的web应用交互并且没有超时,HttpSession就会存在。
  • HttpServletRequestHttpServletResponse 的存活时间为客户端发送完成到完整的响应(web 页面)到达的这段时间。不会被其他地方共享。
  • 所有 ServletFilterListener 对象在 web 应用运行时都是活跃的。它们被所有 session 中的请求共享。
  • 你设置在 HttpServletRequestHttpServletResponseHttpSession 中的所有属性在问题中的对象存活时都会一直保持存活。

线程安全

即便如此,你最关心的可能是线程安全。你现在应该学习到 Servlet 和 filter 被所有请求共享。那是 Java 的一个优点,使得多个不同线程(读取 HTTP 请求)可以使用同一个实例。否则为每个请求重新创建线程的开销实在过于昂贵。

但你应该也意识到永远不要将任何 request 或 session 域中的数据赋值给 servlet 或 filter 的实例变量。它将会被所有其他 session 中的所有请求共享。那是非线程安全的!下面的示例对这种情况进行了展示:

public class ExampleServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}

请参考:

相关文章

相关 [java servlet 工作] 推荐:

Java Servlet工作原理问答

- - ImportNew
本文来自stackoverflow的问答,讨论了Java Servlet的工作机制,如何进行实例化、共享变量和多线程处理. 问题:Servlet是如何工作的. Servlet 如何实例化、共享变量、并进行多线程处理. 假设我有一个运行了大量 Servlet 的 web 服务器. 通过 Servlet 之间传输信息得到 Servlet 上下文,并设置 session 变量.

Java中用Servlet Listener实现定时监听 (Timer、TimerTask)

- - C++博客-牵着老婆满街逛
    (1)实现 javax.servlet.ServletContextListener 接口的两个方法:contextInitialized()和contextDestroyed().     contextInitialized():当Servlet容器启动时会执行contextDestroyed():当Servlet容器停止时会执行.

Servlet Filter 学习

- - CSDN博客架构设计推荐文章
最近在研究CAS , CAS 中的Servlet Filter 不太熟悉, 所以花了点时间学下了下这部分的知识, 分成以下几部分 学习. Servlet Filter  的功能和用法. Servlet Filter 顺序的注意事项. A filter is an object that performs filtering tasks on either the request to a resource (a servlet or static content), or on the response from a resource, or both.

Servlet、Filter和Listener

- - Web前端 - ITeye博客
Java Servlet是与平台无关的服务器端组件,运行于Servlet容器中(如Tomcat),Servlet容器负责Servlet和客户端的通信以及调用Servlet的方法,Servlet和客户端的通信采用“请求/响应”的模式. Servlet可完成以下功能:. 1、创建并返回基于客户请求的动态HTML页面.

Servlet – 会话跟踪

- - ImportNew
HTTP本身是 “无状态”协议,它不保存连接交互信息,一次响应完成之后即连接断开,下一次请求需要重新建立连接,服务器不记录上次连接的内容.因此如果判断两次连接是否是同一用户, 就需要使用 会话跟踪技术来解决.常见的会话跟踪技术有如下几种:. URL重写: 在URL结尾附加. 会话ID标识,服务器通过会话ID识别不同用户..

servlet的四种响应

- - CSDN博客推荐文章
        在一个servlet的请求中,响应的方式的通常有四式,response.getWriter(),response.getOutputStream(),. request.getRequestDispatcher("ajax.jsp").forward(request, response) 和 response.sendRedirect("ajax.jsp").

Servlet是否线程安全

- - 研发管理 - ITeye博客
Servlet是线程安全吗. 要解决这个问题,首先要知道什么是线程安全:.   如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码. 如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的. 或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题.

Java GC的工作原理详解

- - Java - 编程语言 - ITeye博客
JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆、栈、本地方法栈、方法区等部分组成,另外JVM分别对新生代和旧生代采用不同的垃圾回收机制. 首先来看一下JVM内存结构,它是由堆、栈、本地方法栈、方法区等部分组成,结构图如下所示. JVM学习笔记 JVM内存管理和JVM垃圾回收.

Servlet 3.0的檔案上傳寫法

- - 簡睿隨筆
Servlet 3.0已經大幅簡化網頁檔案上傳的程式寫法,以下是撰寫的幾個重點.
的enctype要是"multipart/form-data". 是主要使用的檔案瀏覽元素. 後端接收的Servlet寫在action屬性裡. 以@MultipartConfig(location = “c:/www/xxx/data/")指定寫檔路徑.

jsp+servlet实现验证码功能

- - CSDN博客推荐文章
验证码的功能大多数人可能不都理解,但几乎每个安全网站都会有. 验证码是用来防止非人为因素操作的行为,例如一个黑客要黑一个网站,怎么弄呢. 最简单的思路当然是造成其网路拥堵直至系统瘫痪掉. 如果没有验证码,那么我就可以在注册页面,写一个程序,只有注册表单,不断更换主键或不可重复的内容,不停的提交. 那这样每秒可以注册几万次都有可能,这样服务器就大量负载,很容易就瘫痪并死掉.