A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 小江哥 黑马粉丝团   /  2018-10-18 15:00  /  465 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 小江哥 于 2018-10-18 15:03 编辑

什么是容器:

       Servlet没有main()  方法,它受控于另一个java应用,这个java应用就是容器。Tomcat就是这样一个容器。如果Web服务器应用(Apache)得到一个指向某servlet的请求,此时服务器不是把这个请求交给servlet本身,而是交给部署该servlet的容器(Tomcat)。由容器向servlet提供HTTP请求和响应,而且要由容器调用servlet的方法,如doPost()doGET().

容器能够提供什么:

通信支持、生命周期管理、多线程支持、声明方式实现安全、JSP支持。由容器来管理和运行Servlet,容器负责与Web服务器通信,容器就像是一个“中间人”。

容器如何处理请求:

       Web服务器(Apache  )收到指向Servlet的请求后并没有将请求交给Servlet本身,而是交给管理Servlet的容器(Tomcat)

1.容器收到请求后创建HttpServletRequestHttpServletResponse两个对象。

2.为该请求创建(或分配)一个新的线程。

3.容器根据URL加载正确的Servlet类。注意:任何servlet类都不会有多个实例,容器运行多个线程来处理对一个servlet的多个请求。

4.运行构造函数实例化Servlet,此时只是一个普通的java对象。

5.容器再调用实例的init(ServletConfig  config)方法,初始化该Servlet中的ServletConfig对象,此时才算是一个真正的Servlet(容器会事先为该Servlet创建一个唯一ServletConfig对象)。

6.接着容器调用service()方法,并用HttpServletRequestHttpServletResponse作为参数。

7.最后调用destroy()方法,在servlet对象被销毁之前清理所有的资源。

ServletConfig

       容器建立一个servlet时,它会读DD(部署描述文件),并为ServletConfig创建名值对。此后容器不会再读初始化参数,除非你重新部署Servlet。前面说过,任何servlet类都不会有多个实例,容器运行多个线程来处理对同一个servlet的多个请求。这就意味着Servlet实例是共享的,其中的ServletConfig实例变量也是共享的,那么Servlet就不是线程安全的吗?答案是否定的。因为虽然ServletConfig是共享的,但是它是只读的,即没有一个方法可以对他的数据进行更改。在ServletConfig中配置的参数,只能用于该Servlet。如果在其他ServletJSP中使用可以将参数取出来然后添加到Request对象中,通过请求分派传送过去。但只有请求分派的JSPServlet可以使用,如果其他组件也需要使用这些参数该怎么办呢?

ServletContext

       ServletContext叫做上下文初始化参数,对整个WEB应用都可用。而不只是针对一个Servlet。所以应用中的所有ServletJSP都能够访问它。ServletContext不是线程安全的,具体而言,是ServletContext的属性不是线程安全的。不能使用同步服务方法(如doGet)来达到线程安全。因为,同步一个ServletdoGet()方法意味着Servlet中一次只能运行一个线程,但是并不能阻止其他ServletJSP访问这个属性。



因为每一个Servlet都可以对其进行修改。此外,初始化参数只能是String,如果我想用一个对象来初始化整个应用呢?请看ServletContextListener

ServletContextListener

     可以创建一个单独的类,该类实现了ServletContextListener,它能监听ServletContext一生中的两个关键事件:初始化和撤销。当容器为WEB应用创建一个ServletContext的时候,于是ServletContextListener类便监听到了该初始化事件,执行该类中的初始化方法。从事件对象可以获取ServletContext引用,从ServletContext中获取初始化参数,用初始化参数创建一个对象,将该对象作为ServletContext的一个属性,此后,Web应用的任何一个组件都可以访问这个对象了。注意,需要在部署描述文件中部署一个Listener

HttpSession

       HttpSession session=Request.getSession()返回一个和当前请求相关联的一个会话。容器对每一个请求创建一个线程。如何来标识一个请求?答案就是一个浏览器(对于chrome浏览器,是一个窗口)标识一个请求。测试结果显示,在同一台电脑上用不同的浏览器(或者同一个浏览器不同的窗口)访问同一个Servlet(这就相当于来自三个不同客户的请求),容器会为每一个请求创建不同的请求对象Request,因此会创建不同的Session,它们的Session ID 都不相同。但是对于某个特定浏览器窗口(即特定的客户)的前后多次请求,都是使用同一个HttpSessionSession ID相同)。这是如何实现的呢?当某个客户第一次请求时,容器生成HttpSession时会为其产生一个唯一的SessionID,这个SessionID会随着响应一起发送到该客户,当这个客户下一次访问时,就会发送这个SessionID。那么客户和容器具体是如何交换SessionID信息的呢?请看Cookie!!!所以,在当前版本中一个会话session不会被多个线程同时使用。因此Head First Servlet一书中199页的描述已经过时了。

Cookie

       客户和容器使用Cookie来交换SessionID信息。Cookie是这样运行的:服务器把Cookie(响应体中是Set-Cookie首部)发送给客户,客户做出下一个请求时(请求体中是Cookie首部)再把Cookie返回给服务器。对一个特定的客户的第一次请求时,request.getSession()会创建一个新的HttpSession对象,并且会导致响应发送一个Cookie(在响应体中有一个Set-Cookie首部),在该Cookie中包含了SessionIDrequest.getSession()所做的工作是,检查请求中是否有包含SessionIDCookie,如果有,就找到与该SessionID匹配的会话,如果没有包含SessionIDCookie或者这个SessionID没有匹配的Session(过期了),就会为这个请求创建一个新的HttpSession

SessionCookie的区别

       最大的区别在于Session保存在服务器端内存,而Cookie保存在浏览器或客户端文件。以上所述都是为了实现Session,服务器自动产生Cookie用于与客户交换SessionID信息,当浏览器关闭时,Cookie也就删除了。而平常常说的Cookie是指持久Cookie。持久Cookie是指存放于客户端硬盘中的Cookie信息(设置了一定的有效期)。当用户第一次访问某个网站时,服务器生产包含域名及相关信息的Cookie发送并保存到客户端硬盘中。当浏览器下一次访问该网站时,就会搜索硬盘中的相关Cookie,并发送到服务器,服务器通过Cookie判断是新客户还是老客户。



线程安全与不是线程安全的变量

1.ServletConfig,每一个servlet类都从GenericServlet继承了一个ServletConfig对象,尽管是私有的,但是有一个公有getServletConfig()方法。因此这个对象有多个线程共享,但是它是只读的。

2.ServletContext,属于整个WEB应用,每一个ServletJSP都能够访问。因此它的属性不是线程安全的。为了让ServletContext属性线程安全,可以用ServletContext对象去同步。

3.HttpSession,来自同一台电脑的不同浏览器(甚至同一个浏览器的不同窗口)的请求,容器会为其创建各自的请求对象,HttpSession来自于请求对象,因此,HttpSession也会不相同。只有同一个窗口前后多次请求会对应相同的HttpSession。因此,HttpSession是线程安全的。

4.HttpServletRequest由第3点可知,请求对象是线程安全的。

5.Servlet中的实例变量,显然不是线程安全的。

6.服务方法中的局部变量,显然是线程安全的。
7.servlet中的静态变量,显然不是线程安全的。



0 个回复

您需要登录后才可以回帖 登录 | 加入黑马