Servlet的生命周期

Servlet运行在Servlet容器中,其生命周期由容器来管理。Servlet 生命周期定义了一个Servlet如何被加载、初始化,以及它怎样接收请求、响应请求,提供服务。Servlet的生命周期通过javax.servlet.Servlet接口中的 init()、service()和destroy()方法 来表示。

Servlet 工作过程

Servlet运行原理
Web服务器在与客户端交互时Servlet的工作过程是:

  • 在客户端对web服务器发出请求。
  • web服务器接收到请求后将其发送给Servlet。
  • Servlet容器为此产生一个实例对象并调用ServletAPI中相应的方法来对客户端HTTP请求进行处理,然后将处理的响应结果返回给WEB服务器。
  • web服务器将从Servlet实例对象中收到的响应结构发送回客户端。

init() 方法

在Servlet的生命周期中,仅执行一次init()方法,它是在服务器装入Servlet时执行的,可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init()。

I.如何配置Servlet的初始化参数?
在web.xml中该Servlet的定义标记中,比如:

    <servlet>
         <servlet-name>TimeServlet</servlet-name>
         <servlet-class>com.allanlxf.servlet.basic.TimeServlet</servlet-class>
        <init-param>
            <param-name>user</param-name>
            <param-value>username</param-value>
       </init-param>
       <init-param>
           <param-name>blog</param-name>
           <param-value>http://。。。</param-value>
       </init-param>
    </servlet>

配置了两个初始化参数user和blog它们的值分别为username和http://。。。, 这样以后要修改用户名和博客的地址不需要修改Servlet代码,只需修改配置文件即可。

II.如何读取Servlet的初始化参数?
ServletConfig中定义了如下的方法用来读取初始化参数的信息:

       public String getInitParameter(String name)

参数:初始化参数的名称。
返回:初始化参数的值,如果没有配置,返回null。

III.init(ServletConfig)方法执行次数
在Servlet的生命周期中,该方法执行一次。

IV.init(ServletConfig)方法与线程
该方法执行在 单线程 的环境下,因此开发者 不用考虑线程安全 的问题。

V.init(ServletConfig)方法与异常
该方法在执行过程中可以抛出ServletException来通知Web服务器Servlet实例初始化失败。一旦ServletException抛出,Web服务器不会将客户端请求交给该Servlet实例来处理,而是报告初始化失败异常信息给客户端,该Servlet实例将被从内存中销毁。如果在来新的请求,Web服务器会创建新的Servlet实例,并执行新实例的初始化操作

service() 方法

I. service()方法的职责
service()方法为Servlet的核心方法,每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。

默认的服务功能是调用与HTTP请求的方法相应的do功能

客户端的业务逻辑应该在该方法内执行,典型的服务方法的开发流程为:
解析客户端请求-〉执行业务逻辑-〉输出响应页面到客户端

II.service()方法与线程
为了提高效率,Servlet规范要求一个Servlet实例必须能够 同时服务于多个客户端请求,即service()方法运行在 多线程 的环境下,Servlet开发者必须保证该方法的 线程安全性

III.service()方法与异常
service()方法在执行的过程中可以抛出ServletException和IOException。其中ServletException可以在处理客户端请求的过程中抛出,比如请求的资源不可用、数据库不可用等。一旦该异常抛出,容器必须回收请求对象,并报告客户端该异常信息。IOException表示输入输出的错误,编程者不必关心该异常,直接由容器报告给客户端即可。

编程注意事项说明:

  1. 当Server Thread线程执行Servlet实例的init()方法时,所有的Client Service Thread线程都不能执行该实例的service()方法,更没有线程能够执行该实例的destroy()方法,因此Servlet的init()方法是工作在单线程的环境下,开发者不必考虑任何线程安全的问题。

  2. 当服务器接收到来自客户端的多个请求时,服务器会在单独的Client Service Thread线程中执行Servlet实例的service()方法服务于每个客户端。此时会有多个线程同时执行同一个Servlet实例的service()方法,因此必须考虑线程安全的问题。

  3. 请大家注意,虽然service()方法运行在多线程的环境下,并不一定要同步该方法。而是要看这个方法在执行过程中访问的资源类型及对资源的访问方式。分析如下:

    i. 如果service()方法没有访问Servlet的成员变量也没有访问全局的资源比如静态变量、文件、数据库连接等,而是只使用了当前线程自己的资源,比如非指向全局资源的临时变量、request和response对象等。该方法本身就是线程安全的,不必进行任何的同步控制。

    ii. 如果service()方法访问了Servlet的成员变量,但是对该变量的操作是只读操作,该方法本身就是线程安全的,不必进行任何的同步控制。

    iii. 如果service()方法访问了Servlet的成员变量,并且对该变量的操作既有读又有写,通常需要加上同步控制语句。

    iv. 如果service()方法访问了全局的静态变量,如果同一时刻系统中也可能有其它线程访问该静态变量,如果既有读也有写的操作,通常需要加上同步控制语句。

    v. 如果service()方法访问了全局的资源,比如文件、数据库连接等,通常需要加上同步控制语句。

destroy() 方法

仅执行一次,在服务器端停止且卸载Servlet时执行该方法,有点类似于C++的delete方法。一个Servlet在运行service()方法时可能会产生其他的线程,因此 需要确认在调用destroy()方法时,这些线程已经终止或完成

Servlet 生命周期

Servlet的生命周期是由Servlet容器来控制的,它始于 装入Web服务器的内存时,并 在终止或重新装入Servlet时结束。这项操作一般是动态执行的。然而,Server通常会提供一个管理的选项,用于在Server启动时强制装载和初始化特定的Servlet。

在代码中,Servlet生命周期由 接口javax.servlet.Servlet定义。所有的Java Servlet 必须直接或间接地实现javax.servlet.Servlet接口,这样才能在Servlet Engine上运行。javax.servlet.Servlet接口定义了一些方法,在Servlet 的生命周期中,这些方法会在特定时间按照一定的顺序被调用。

Servlet的生命周期包含了下面4个阶段:

  1. 加载和实例化
  2. 初始化
  3. 请求处理
  4. 服务终止

Servlet的生命周期如下图所示:
Servlet的生命周期

加载和实例化Servlet

Servlet容器 负责加载和实例化Servlet。当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Servlet实例。

当Servlet容器启动后,它必须要知道所需的Servlet类在什么位置,Servlet容器可以从本地文件系统、远程文件系统或者其他的网络服务中通过类加载器加载Servlet类,成功加载后,容器创建Servlet的实例。

当启动Servlet容器时,容器首先查找一个配置文件 web.xml,这个文件中记录了可以提供服务的Servlet。每个Servlet被指定一个Servlet名,也就是这个Servlet实际对应的Java的完整class文件名。

因为容器是通过 Java的反射API 来创建Servlet实例,调用的是Servlet的默认构造方法(即不带参数的构造方法),所以,每个Servlet类必须有一个公共的无参数的构造器

初始化

当Servlet被实例化后,Servlet容器将调用每个Servlet的 init() 方法来实例化每个实例,执行完init方法之后,Servlet处于“已初始化”状态。

Servlet在启动后不立即初始化,而是收到请求后进行。对于每一个Servlet实例,init()方法只被调用一次。

初始化的目的是为了让Servlet对象在处理客户端请求前完成 一些初始化的工作,如 建立数据库的连接,获取配置信息等

在初始化期间,Servlet实例可以使用容器为它准备的 ServletConfig 对象从Web应用程序的配置信息(在 web.xml 中用<load-on-statup> ...... </load-on-statup>对Servlet进行预先初始化。)中获取 初始化的参数信息

在初始化期间,如果发生错误,Servlet实例可以抛出 ServletException 异常或者 UnavailableException 异常来通知容器。ServletException异常用于指明一般的 初始化失败,例如没有找到初始化参数;而UnavailableException异常用于通知容器 该Servlet实例不可用。例如,数据库服务器没有启动,数据库连接无法建立,Servlet就可以抛出UnavailableException异常向容器指出它暂时或永久不可用。

请求处理

Servlet 被初始化以后,就处于能响应请求的就绪状态。Servlet容器调用Servlet的 service()方法 对请求进行处理,这个方法可以调用其他方法来处理请求。要注意的是,在service()方法调用之前,init()方法必须成功执行。

在service()方法中,Servlet实例通过ServletRequest对象得到客户端的相关信息和请求信息,在对请求进行处理后,调用ServletResponse对象的方法设置响应信息。

Service方法会在服务器被访问时调用,Servlet对象的生命周期中 service() 方法可能被多次调用,由于web-server启动后,服务器中公开的部分资源将处于网络中,当网络中的不同主机(客户端)并发访问服务器中的同一资源,服务器将开设 多个线程 处理不同的请求,多线程同时处理同一对象时,有可能出现 数据并发访问 的错误。
另外注意,多线程难免同时处理同一变量时(如:对同一文件进行写操作),且有 读写操作 时,必须考虑是否加上 同步,同步添加时,不要添加范围过大,有可能使程序变为纯粹的单线程,大大削弱了系统性能;只需要做到多个线程安全的访问相同的对象就可以了。

在service()方法执行期间,如果发生错误,Servlet实例可以抛 出 ServletException 异常或者 UnavailableException 异常。如果UnavailableException异常指示了该实例 永久不可用,Servlet容器将调用实例的destroy()方法,释放该实例。此后对该实例的任何请求,都将收到容器发送的 HTTP 404(请求的资源不可用)响应。如果UnavailableException异常指示了该实例 暂时不可用,那么在暂时不可用的时间段内,对该实例的任何请求,都将收到容器发送的 HTTP 503(服务器暂时忙,不能处理请求)响应。

服务终止

当服务器不再需要Servlet实例或重新装入时,会调用 destroy() 方法,使用这个方法,Servlet可以释放掉所有在init方法申请的资源。一个Servlet实例一旦终止,就不允许再次被调用,只能等待被卸载。
Servlet一旦终止,Servlet实例即可被 垃圾回收,处于“卸载”状态。如果再次需要这个Servlet处理请求,Servlet容器会创建一个新的Servlet实例。如果Servlet容器被关闭,Servlet也会被卸载。

一个Servlet实例只能初始化一次,但可以创建多个相同的Servlet实例。如相同的Servlet可以在根据不同的配置参数连接不同的数据库时创建多个实例。

在整个Servlet的生命周期过程中,创建Servlet实例调用实例的init()destroy()方法只进行一次,当初始化完成后,Servlet容器会将该实例保存在内存中,通过调用它的service()方法,为接收到的请求服务。

参考文章:
https://www.cnblogs.com/fifiyong/p/6390805.html
https://www.cnblogs.com/lgk8023/p/6427977.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值