servlet的生命周期

来源:互联网 发布:win7网络图标无法打开 编辑:程序博客网 时间:2024/05/20 05:10

引用1            以下是jt   198952 在csdn上的原帖

 http://blog.csdn.net/jt198952/article/details/5656130

大多数程序员都知道Servlet的生命周期,简单的概括这就分为四步:servlet类加载--->实例化--->服务--->销毁。对这个过程只是肤浅了解下,对于servlet何时被销毁,还是不太情楚。下面我们描述一下Tomcat与Servlet是如何工作的,首先看下面的时序图.

 

1、Web Client 向Servlet容器(Tomcat)发出Http请求

2、Servlet容器接收Web Client的请求

3、Servlet容器创建一个HttpRequest对象,将Web Client请求的信息封装到这个对象中

4、Servlet容器创建一个HttpResponse对象

5、Servlet容器调用HttpServlet对象的service方法,把HttpRequest对象与HttpResponse对象作为参数传给 HttpServlet对象

6、HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息

7、HttpServlet调用HttpResponse对象的有关方法,生成响应数据

8、Servlet容器把HttpServlet的响应结果传给Web Client

 

对于Servlet容器(Tomcat)与HttpServlet是怎样进行交互的呢,看下类图

Servlet的框架是由两个Java包组成的:javax.servlet与javax.servlet.http。在javax.servlet包中定义了所有的Servlet类都必须实现或者扩展的通用接口和类。在javax.servlet.http包中定义了采用Http协议通信的HttpServlet类。Servlet的框架的核心是javax.servlet.Servlet接口,所有的Servlet都必须实现这个接口。

 

在Servlet接口中定义了5个方法,

其中3个方法代表了Servlet的生命周期:

1、init方法:负责初始化Servlet对象。

2、service方法:负责响应客户的请求。

3、destroy方法:当Servlet对象退出生命周期时,负责释放占用的资源。

 

一、创建Servlet对象的时机

1、Servlet容器启动时:读取web.xml配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,同时将ServletConfig对象作为参数来调用Servlet对象的init方法。

2、在Servlet容器启动后:客户首次向Servlet发出请求,Servlet容器会判断内存中是否存在指定的Servlet对象,如果没有则创建它,然后根据客户的请求创建HttpRequest、 HttpResponse对象,从而调用Servlet对象的service方法。

3、Servlet的类文件被更新后,重新创建ServletServlet容器在启动时自动创建Servlet,这是由在web.xml文件中为Servlet设置的<load-on-startup>属性决定

的。从中我们也能看到同一个类型的Servlet对象在Servlet容器中以单例的形式存在。

 

二、销毁Servlet对象的时机

1、Servlet容器停止或者重新启动:Servlet容器调用Servlet对象的destroy方法来释放资源。以上所讲的就是Servlet对象的生命周期。那么Servlet容器如何知道创建哪一个Servlet对象?

Servlet对象如何配置?实际上这些信息是通过读取web.xml配置文件来实现的。

我们来看一下web.xml文件中的Servlet对象的配置节信息

 

-------------------------------------------

<servlet>

<servlet-name>action<servlet-name>

<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>

<init-param>

<param-name>config</param-name>

<param-value>/WEB-INF/struts-config.xml</param-value>

</init-param>

<init-param>

<param-name>detail</param-name>

<param-value>2</param-value>

</init-param>

<init-param>

<param-name>debug</param-name>

<param-value>2</param-value>

</init-param>

<load-on-startup>2</load-on-startup>

</servlet>

 

<servlet-mapping>

<servlet-name>action</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

 

--------------------------------------------

 

下面对上面的配置节信息进行解析

servlet-name:Servlet对象的名称

servlet-class:创建Servlet对象所要调用的类

param-name:参数名称

param-value:参数值

load-on-startup:Servlet容器启动时加载Servlet对象的顺序

servlet-mapping/servlet-name:要与servlet中的servlet-name配置节内容对应

url-pattern:客户访问的Servlet的相对URL路径

 

当Servlet容器启动的时候读取<servlet>配置节信息,根据<servlet-class>配置节信息创建Servlet对象,同时根据<init-param>配置节信息创建HttpServletConfig对象,然后执行Servlet对象的init方法,并且根据<load-on-startup>配置节信息来决定创建Servlet对象的顺序,如果此配置节信息为负数或者没有配置,那么在Servlet容器启动时,将不加载此Servlet对象。当客户访问Servlet容器时,Servlet容器根据客户访问的URL地址,通过<servlet-mapping>配置节中的<url-pattern>配置节信息找到指定的Servlet对象,并调用此Servlet对象的service方法。


http://blog.sina.com.cn/s/blog_5198c7370100cwrz.html

Servlet的生命周期定义了一个Servlet如何被加载、初始化,以及它怎样接收请求、响应请求、提供服务。 
  在代码中,Servlet生命周期由接口javax.servlet.Servlet定义。所有的Java Servlet 必须直接或间接地实现javax.servlet.Servlet接口,这样才能在Servlet Engine上运行。Servlet Engine提供network Service,响应MIME request,运行Servlet Container。 javax.servlet.Servlet接口定义了一些方法,在Servlet 的生命周期中,这些方法会在特定时间按照一定的顺序被调用。如下图所示。
 
  servlet的定义及其生命周期
 
   Servlet的生命周期
  Servlet 如何被加载(Load)、被实例化(Instantiated)
 
  Servlet Engine 负责实例化和加载Servlet,这个过程可以在Servlet Engine 加载时执行,可以在Servlet 响应请求时执行,也可以在两者之间的任何时候执行(配置为load on start up的 servlet是在容器启动时被加载的)。
 
  Servlet如何被初始化(Initialized)
 
  Servlet Engine 加载好Servlet 后,必须要初始化它。初始化时Servlet 可以从数据库里读取初始数据,建立JDBC Connection,或者建立对其他有价值的资源的引用。
 
  在初始化阶段,Init( )方法被调用。这个方法在javax.servlet.Serlet接口中定义。Init( )方法以一个Servlet 配置文件(ServletConfig 型)为参数。Servlet configuration 对象由Servlet Engine 实现,可以让Servlet 从中读取一些name-value对的参数值。ServletConfig对象还可以让Servlet接受一个Servlet Context对象。
 
  Servlet 如何处理请求
 
  Servlet 被初始化以后,就处于能响应请求的就绪状态。每个对Servlet 的请求由一个Servlet Request 对象代表。Servlet 给客户端的响应由一个Servlet Response对象代表。当客户端有一个请求时,Servlet Engine 将ServletRequest 和ServletResponse对象都转发给Servlet,这两个对象以参数的形式传给Service方法。这个方法由javax.servlet.Servlet定义并由具体的Servlet 实现。
 
  Servlet还可以实现 ServletRequest 和ServletResponse接口。ServletRequest接口可以让Servlet 获取客户端请求中的参数,如form data、request信息、协议类型,等等。Servlet 可以从ServletInputStream流中读取request 数据。ServletResponse接口允许Servlet设置response headers和status codes。实现这个接口可以使Servlet能访问ServletOutputStream流用来向客户端返回数据。
 
  Servlet如何被释放
 
  Servlet Engine 没有必要在Servlet 生命周期的每一段时间内都保持Servlet的状态。Servlet Engine可以随时随意使用或释放Servlet。因此,你不能依赖Servlet class或其成员存储信息。当Servlet Engine判断一个Servlet应当被释放时(比如说Engine准备Shut down 或需要回收资源),Engine必须让Servlet 能释放其正在使用的任何资源,并保存持续性的状态信息。这些可以通过调用Servlet的destroy方法实现。 在Servlet Engine 释放一个Servlet 以前,必须让其完成当前实例的service方法或是等到timeout(如果Engine定义了timeout)。当Engine释放一个Servlet以后,Engine将不能再将请求转发给它,Engine必须彻底释放该Servlet并将其标明为可回收的(给garbage collection)。
 
  在Servlet API中最重要的是Servlet interface。所有的Servlets执行这个interface的方式有很多种:或者是直接的,或者通过extending这个class执行它,如 HttpServlet。这个Servlet interface 提供并安排Servlet与客户端联系的方法。Servlet 编写者可以在他们开发Servlet程序时提供更多一些或所有这样的方法。
 
  当一个Servlet接收来自客户端的调用请求时,它接收两个对象:一个是ServletRequest,另外一个是ServletResponse。这个ServletRequest class 概括从客户端到服务器之间的联系,而 ServletResponse class 概括从Servlet返回客户端的联系。
 
  ServletRequest interface 可以获取到这样一些信息,如由客户端传送的参数名称,客户端正在使用的协议,产生请求并且接收请求的服务器远端主机名。它也提供获取数据流的Servlet、ServletInputStream,这些数据是客户端引用中使用HTTP POST和PUT方法递交的。一个ServletRequest的子类可以让Servlet获取更多的协议特性数据。
 
  例如,HttpServletRequest 包含获取HTTP-specific头部信息的方法。ServletResponse interface 给出相应客户端的Servlet方法。它允许Servlet设置内容长度和回应的MIME类型,并且提供输出流ServletOutputStream,通过编写者可以发回相应数据。ServletResponse子类可以给出更多protocol-specific容量的信息。例如,HttpServletResponse 包含允许Servlet操作HTTP-specific头部信息的方法。

总结与运用:

在实践中可以配置一个load on start up 的Servlet,它随服务器启动而初始化,在其init方法中启用一些参数设置,例如把某些变量存入ServletContext,这样以后在其他Servlet中就可以获得该参数了;而在其destroy中写入一些资源回收的代码,当服务器关闭时就可以自动关闭相遇的资源了。

http://he-wen.iteye.com/blog/800100

这里是servlet的类图之间的设计关系,里面接口、抽象类我只写上关键的方法让大家参考:



 

下面根据类图关系和servlet的生命周期讲解:

一、servlet容器(如tomcat)加载servlet类,读入其.class类文件到内存
二、servlet容器开始针对这个servlet,创建ServletConfig对象(他的主要任务就是读取配置文件的相关信息,想我们写一个Servlet时,就要配置.XML文件,指定自己的Servlet在哪个地方,还有是否经过过滤器等等)
三、 servlet容器创建servlet对象4. servlet容器调用servlet对象的init(ServletConfig config)方法,在这个init方法中,建立了sevlet对象和servletConfig对象的关联,执行了如下的代码:
 
 

public void init(ServletConfig config) throws ServletException {
        this.config = config;
        this.init(); //调用了无参的 init()方法  
    }
    /***
     *     无参的init()方法
     * @throws ServletException
     */
    public void init() throws ServletException {

    }

    public void init(ServletConfig config) throws ServletException 
    {   
      this.config = config;  //将容器创建的servletConfig 对象传入,并使用私有成员变量引用该servletConfig对象   
      this.init();    
    }
 //获取初始化参数  
    public String getInitParameter(String name) {
        return getServletConfig().getInitParameter(name);
    }

    //实现了接口<ServletConfig>中的方法,用于返回在web.xml文件中为servlet所配置的全部的初始化参数的值  
    public Enumeration getInitParameterNames() {
        return getServletConfig().getInitParameterNames();
    }

    //获取在web.xml文件中注册的当前的这个servlet名称。没有在web.xml 中注册的servlet,该方法直接放回该servlet的类名。  
    //法实现了接口<ServleConfig>中的getServletName方法    
    public String getServletName() {
        return config.getServletName();
    }

public ServletContext getServletContext() {
        return getServletConfig().getServletContext();
    }
 有的人会问设计者为什么要写两个init()方法呢?
这是因为这里的config对象的引用就来自Init(ServletConfig config)执行的结果.
所以如果子类需要覆盖了父类的init(ServletConfig config)方法,则首先要调用父类的init(ServletConfig config)方法,也就是先加入 super.init(config) 语句来先执行父类的方法.否则,会产生空指针异常 java.lang.NullPointerException ,因为config对象的引用为空。
 
为了避免这样的情况的产生,GenericServlet的设计人员 在GenericServlet中又定义了一个无参数的init()空方法. 且在init(servletConfig config)方法最后也调用了这个无参的空方法
Java代码 
public void init(ServletConfig config) throws ServletException  
{  
    this.config=config;  
    init();       
}  
public void init(ServletConfig config) throws ServletException
{
    this.config=config;
    init();     
}
所以,我们只需覆盖这个无参的init方法加入自己的初始代码即可,而无需覆盖带参数的父类的init方法.如果有多个重载的init方法,对以servlet而言,servlet容器始终就之调用servlet接口中的那个方法:init(ServletConfig config) (当然也会执行已经覆盖的无参的init()方法),其他的覆盖的init方法不会执行。
通过以上的初始化步骤建立了servlet对象和sevletConfig对象的关联,而servletConfig对象又和当前容器创建的ServleContext对象获得关联.
 
五、运行时阶段当容器接受到访问特定的servlet请求时,针对这个请求,创建对应的ServletRequest对象和 ServletResponse对象,并调用servlet的service()方法,service()根据从ServletRequest对象中获得 客户的请求信息并将调用相应的doxxx方法等进行响应,再通过ServletResponse对象生成响应结果,然后发送给客户端,最后销毁创建的ServletRequest 和ServletResponse
在抽象类中GenericServlet中service()是一个抽象方法,但在HttpServlet中对这个方法进行了实现。servlet 接口中定义的service()方法中的两个参数分别是servletRequest 和 ServletResponse 这两个类型。当前的http请求,如果需要在这个service()方法内部使用http消息特有的功能,也就是要调用 HttpServletRequest 和HttpServletResponse来中定义的方法时,需要将请求和响应对象进行一个类型的转换,所以,在GenericServlet中,使用了 两个方法来共同完成这个工作。
实现父类GenericServlet中的service(ServltRequest req,ServeltResponse res)抽象方法
为什么在Servlet中的service方法?
service(ServletRequest servletrequest, ServletResponse servletresponse)
而不是用HttpServletRequest和HttpServletResponse呢?这是因为这样定义就与应用层的任何协议没有任何关系.在HttpServlet重载service()这样就可以向下转型。查看源代码
相应的Java代码:
/**
   * 通过参数的向下转型,然后调用重载的
     */
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {
        HttpServletRequest request;
        HttpServletResponse response;
        try
        {
            request = (HttpServletRequest)req;
            response = (HttpServletResponse)res;
        }
        catch(ClassCastException e)
        {
            throw new ServletException("non-HTTP request or response");
        }
        service(request, response);
    }
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();
        if(method.equals("GET"))
        {
            long lastModified = getLastModified(req);
            if(lastModified == -1L)
            {
                doGet(req, resp);
            } else
            {
                long ifModifiedSince = req.getDateHeader("If-Modified-Since");
                if(ifModifiedSince < (lastModified / 1000L) * 1000L)
                {
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else
                {
                    resp.setStatus(304);
                }
            }
        } else
        if(method.equals("HEAD"))
        {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);
        } else
        if(method.equals("POST"))
            doPost(req, resp);
        else
        if(method.equals("PUT"))
            doPut(req, resp);
        else
        if(method.equals("DELETE"))
            doDelete(req, resp);
        else
        if(method.equals("OPTIONS"))
            doOptions(req, resp);
        else
        if(method.equals("TRACE"))
        {
            doTrace(req, resp);
        } else{
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object errArgs[] = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }
        }
六、、销毁阶段:只有当web应用被终止时,servlet才会被销毁。servlet容器现调用web应用中所有的Servlet对象的 destroy()方法,然后再销毁这些servlet对象,此外,容器还销毁了针对各个servlet所创建的相关联的serveltConfig对象
注意在Servlet生命周期中init()和destory()方法只调用一次,是单列模式

http://blog.sina.com.cn/s/blog_4d91c1660100iz56.html

在tomcat5中,为了保证get方式提交的数据采用UTF-8编码,在server.xml中进行了如下设置:

<Connector port="8080" maxThreads="150" minSpareThreads="25"
maxSpareThreads="75" enableLookups="false" redirectPort="8443"
acceptCount="100" debug="99" connectionTimeout="20000"
disableUploadTimeout="true" URIEncoding="UTF-8"/>

这里指定了get时候的数据编码。这里要注意的一点是大小写,我以前就犯过这样的错误,把URIEncoding写成了uriEncoding,郁闷了好长时间都不知道错误在哪里,最后去Tomcat官方网站上偶然发现大小写不一致,最后改过来问题解决了。另外一点,当使用IIS作为webserver转发servlet/jsp请求给Tomcat时候,这个设置却失效了。其实 原因很简单:IIS是通过AJP协议,把请求转发到Tomcat监听的8009端口上的,所以这里针对8080的设置自然就无效了。正确的方法是进行下面 的设置:

<Connector port="8009" enableLookups="false" redirectPort="8443"
debug="0" protocol="AJP/1.3" URIEncoding="UTF-8"/>


servlet中service()方法存在时,doGet()方法里就不起作用

其实Servlet中,service方法是一直存在的,因为最高层的接口Servlet(想HttpServlet等具体的servlet都是直接或者间接实现了这个接口)里面就有这个方法,所以不管是怎样的servlet类,都有service方法,没有service就不能称为一个servlet了。我想你说的意思是你写了一个Servlet(应该是继承HttpServlet吧),重写了service方法,一般来说这个方法是不需要重写的,因为在HttpServlet中已经有了很好的实现,它会根据请求的方式,调用doGet或者doPost方法,所以我们么一般写一个servlet,只需要重写doGet或者doPost就可以了,如果你重写了service方法,那么servlet容器就会把请求交给这个方法来处理,而你又没有在service方法中调用doGet或者doPost,那么当然doGet方法就会不起作用了,我的建议是如果你由于某种需要,需要重写service方法,在末尾最好加上一句super.service(),这样就可以解决问题了。


0 0
原创粉丝点击