[转]通俗易懂Tomcat中Servlet的生命周期,讲的非常详细

晨曦之光 发布于 2012/04/11 18:10
阅读 986
收藏 6

我在上一篇文章里详细的介绍了 HTTP协议工作的流程,其中最重要的就是如何理解HTTP请求头和HTTP响应头,现在在这里再来详细的说明Tomcat 容器(即Servlet 容器)到底是如何 管理Servlet的,Servlet 的生命周期到底是如何进行的,其中与 Tomcat 容器的交互过程,相信大家只要看懂下面的分析,一定会真正理解Servlet 生命周期的。

其中所以引用的实例说明均来自本人自己的配置,以下所有例子均只实现了 doGet( ) 方法。

一. Tomcat 是如何 加载 Servlet 的,期间到底发生了什么。(Servlet 的初始化)

首先,我们 在 C:\apache-tomcat-5.5.20\webapps\ROOT\WEB-INF 目录下找到 web.xml 配置文件,这个 配置文件实际上起到的最直接的作用就是 管理 Servlet 。里面我先写两个 <servlet> 和<serlvet-mapping>

<servlet>
 <servlet-class>RequRepon</servlet-class>
 <servlet-name>reqrep</servlet-name>
</servlet>

<servlet-mapping>
 <servlet-name>reqrep</servlet-name>
 <url-pattern>/accp/reqrep</url-pattern>
</servlet-mapping>

<servlet>
 <load-on-startup>0</load-on-startup>                                                  -------------tomcat 服务一启动就会加载
   <init-param>
  <param-name>num</param-name>
  <param-value>10000</param-value>
   </init-param>
       <servlet-name>www</servlet-name>
    <servlet-class>MyServlet</servlet-class>
   </servlet>

   <servlet-mapping>
       <servlet-name>www</servlet-name>
    <url-pattern>/accp/myservlet</url-pattern>
   </servlet-mapping>

在 C:\apache-tomcat-5.5.20\webapps\ROOT\WEB-INF\classes  路径下放好我已经经过编译后的两个 Servlet  .class 文件 ,一个是 MyServlet,一个是 RequRepon

启动 C:\apache-tomcat-5.5.20\bin 路径下 startup.bat ,tomcat 开始启动。

这个时候我们会发现在 Tomcat 命令控制台上出现了 一句话 ,

MyServlet 中的 init () 方法被调用了一次!

其实我在 MyServlet ,和 RequRepon 都写了 ---####的 init () 方法被调用了一次!但是RequRepon 中的init()并没有被调用,原因就在于 web.xml 中

<servlet>
 <load-on-startup>0</load-on-startup>                                                  -------------tomcat 服务一启动就会加载
   <init-param>
  <param-name>num</param-name>
  <param-value>10000</param-value>
   </init-param>
       <servlet-name>www</servlet-name>
    <servlet-class>MyServlet</servlet-class>
   </servlet>

就通知了 Tomcat 在它的服务一启动的时候 ,就要去加载 所对应的 Servlet-class   MyServlet 这个类,0代表优先级别,随后是 1. 2. 3. .4.... 0的优先级别最高。Tomcat 首先把这些类加载并实例化,保存在自己的Servlet 容器池中,以后如果有请求,直接从此容器池中取出来,处理相应的请求。

RequRepon  没有被加载没有关系,我们在地址栏输入 http://localhost:8080/accp/reqrep

这个时候出现:注意最下面的两行

RequRepon 中的 init( ) 方法不仅被调用了,并且其 doGet( ) 方法也被调用了!

那么这是为什么呢?原因就是虽然 RequRepon  首先被加载到 Tomcat 容器中,但是一旦有来自客户端的请求,Tomcat 会解析这个请求 URL 找到指定的 Servlet 类,再加载同时处理请求,所以就会调用它的 doGet( )方法

<servlet>
 <servlet-class>RequRepon</servlet-class>
 <servlet-name>reqrep</servlet-name>
</servlet>

<servlet-mapping>
 <servlet-name>reqrep</servlet-name>
 <url-pattern>/accp/reqrep</url-pattern>                            

</servlet-mapping>

根据请求 http://localhost:8080/accp/reqrep     

找到 <servlet-name> reqrep 

根据 reqrep   找到对应的 <servlet-class> RequRepon  加载并初始化

这个时候我们再做这样的一个动作,把 C:\apache-tomcat-5.5.20\webapps\ROOT\WEB-INF\classes 中的两个类删除掉,在 http://localhost:8080/accp/reqrep          或者  http://localhost:8080//accp/myservlet

会发现 Tomcat 中 这两个类的 doGet( ) 方法仍然被实现了,也就说来自于客户端的请求他们已经收到了,并调用了doGet( ) 方法进行了处理。

再来总结以上的内容,就是Servlet 如何被加载的。

1. Tomcat 加载 Servlet 并且实例化,有两种方法:

一种是根据 web.xml中  <load-on-startup>0</load-on-startup>  的配置,在Tomcat 一启动的时候就加载,并同时实例化,并马上调用 其 init( ) 方法,完成初始化过程。

第二种就是动态的加载,根据客户端的请求 URL 如: http://localhost:8080/accp/reqrep     中的accp/reqrep     

去在 web.xml 中寻找指定的 Servlet 类,加载它并实例化,马上调用其 init( ) 方法完成初始化,由于是来自客户端的一个请求,那么自然要 调用 doGet( ) 方法来处理请求。

2. Servlet 的初始化 ,即init( ) 方法是优于其它所有方法之前的,在处理任何响应之前就会调用的,并且在一个生命周期中有且只有一次!

3. Tomcat 一旦加载 Servlet ,实例化,就将其保存在内存中,以后不管多少次来自于客户端的请求都是由保存在Tomcat 容器池(即内存)里面的 Servlet实例来处理,它不是开启了一个进程,而是实现了多线程操作,这也就是为什么要引用 Servlet 而不用 CGI 的优点。这个证据就是 我已经在C:\apache-tomcat-5.5.20\webapps\ROOT\WEB-INF\classes  路径下删除了 这两个Servlet  .class 文件,但是请求一样处理,这就能证明,Tomcat 完成类加载是将他们保存在内存中,以便以后使用,效率很高。

二.doGet( ) 和 doPost( ) 到底是怎么样来的,为什么没有 Service( ) 方法,里面的流程是什么?

上面只说明了 init( ) 有关的东西,现在来说 doGet( ) 和 doPost( ) ,那么这个要和我的另一篇文章 关于 HTTP 请求头和HTTP 响应头结合起来看,会有很好的效果的。

http://blog.csdn.net/lvpin/archive/2007/06/09/1645770.aspx

 通俗易懂客户端与服务器端交互原理(HTTP数据请求与HTTP响应,包括Servlet部分)

当Tomcat 容器收到一个消息,用户请求一个Servlet ,那么就生成了一个响应头,其实就是 HttpServletRequest 对象,根据 url 指定的 Servlet ,Tomcat 就让此 Servlet 来处理这个请求。

那么我们在 Java程序中其实实现的就是 HttpServelt ,重写了 doGet( )或者doPost( ) 方法,就可以处理这个请求了。但是 doGet( ) 或者 doPost( ) 方法是由 Service( )来调用的,我们怎么不重写Service( ) 方法呢?那是因为HttpServlet 继承了GenericServlet,而GenericServlet 实现了Servlet 接口。在其中的一个类中,它的 Service( ) 其实就已经解析了 来自于 Http请求头的内容,根据请求行的 method  来决定调用 doGet( ) 或者 doPost( ) 方法,同时把HttpServletRequest 和 HttpServletResponse 以参数的形式传递给 doGet( ) 或者doPost(),来进行我们的业务处理。

如果重写 Service( )方法,那么里面的很多工作可能就需要我们完成,其实这并不需要,我们只需要重写 doGet( ) 或者 doPost(  )方法就可以了。这个重写的动作就是Servlet 生命周期中的 Service( ) 方法,里面调用了 doGet( ) 或者doPost( )方法。

三.处理请求结束,生成响应回发,销毁实例

处理请求结束,生成响应回发,这个在我上面所提到的文章里已经讲的很清楚了。

至于销毁实例 ,完成 destroy( ) 动作,一是只要把 tomcat服务关闭就可以了。另外还有一种能实现 destroy( ) 的方法,可能是 Tomcat 里面的一些小BUG,我正在找证据,已经发现一些端倪,今天有事暂时不写。重点就是前面的内容了。



原文链接:http://blog.csdn.net/wangxiaojing123/article/details/7055760
加载中
返回顶部
顶部