Java Servlet 工作原理
2015-12-09 23:09
447 查看
问题:Servlet是如何工作的?Servlet 如何实例化、共享变量、并进行多线程处理?
假设我有一个运行了大量 Servlet的 web 服务器。通过
Servlet之间传输信息得到
Servlet上下文,并设置
session 变量。
现在,如果有两名或更多使用者向这个服务发送请求,接下来 session 变量会发生什么变化?究竟是所有用户都是用共同的变量?还是不同的用户使用的变量都不一样?如果是后者,服务器如何区分不同用户?
另一个相似的问题,如果有
*n*名用户访问一个特定的
Servlet,那么该
Servlet是仅在第一个用户首次访问的时候实例化,还是分别为每个用户实例化?
回答(BalusC):
ServletContext
当 Servlet 容器(比如 Apache Tomcat)启动后,会部署和加载所有 web 应用。当web 应用被加载,Servlet 容器会创建一次 ServletContext,然后将其保存在服务器的内存中。web
应用的
web.xml被解析,找到其中所有
servlet、
filter和
Listener或
@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-pattern的
Filter和
Servlet实例中的方法,所有工作都在同一个线程中处理。
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并设置一个拥有新
sessionID
的 cookie。
概述
ServletContext与 web 应用存活时间一样长。它被所有 session
中的所有请求共享。
只要客户端一直与相同浏览器实例的web应用交互并且没有超时,HttpSession就会存在。
HttpServletRequest和
HttpServletResponse的存活时间为客户端发送完成到完整的响应(web
页面)到达的这段时间。不会被其他地方共享。
所有 Servlet、
Filter和
Listener对象在
web 应用运行时都是活跃的。它们被所有 session 中的请求共享。
你设置在
HttpServletRequest、
HttpServletResponse和
HttpSession中的所有属性在问题中的对象存活时都会一直保持存活。
线程安全
即便如此,你最关心的可能是线程安全。你现在应该学习到 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. } }
相关文章推荐
- 第三个spring冲刺第3天
- cannot be cast to javax.servlet.Servlet
- 开发维护大型 Java 项目的建议
- Java:单例模式的几种写法
- leetcode 003 Longest Substring Without Repeating Characters(java)
- java 静态方法调用非静态内部类报错
- kettle 如何使用java代码
- JAVA-抽象工厂模式
- SpringMVC中Controller和RestController
- JDK环境变量配置
- 课时1第一章:Java概述
- spring mvc 在同一个controller 中同时返回多种格式的数据 (xml json atom)
- struts2类型转换
- java Class.forname与SelectMethod=Cursor
- Java多线程、线程的生命周期和状态控制
- Spring mvc Controller层与前端JSON数据交互
- java---46
- 【Java自学】 梅森素数
- java之trycatchfinally代码块与return,throw的执行顺序的探索
- Java代码整理