Servlet生命周期
文章的内容比较基础,俺会继续补充深入的,冲冲冲...
# Servlet生命周期
# 1.Servlet中的生命周期方法
Servlet 加载——>实例化——>服务——>销毁
【图是借来的,原文链接 Servlet的生命周期简述 (opens new window)】
init( )
在Servlet的生命周期中,仅执行一次
init()
方法。它是在服务器装入Servlet时执行的,负责初始化Servlet对象。可以在启动服务器或客户机首次访问时装入Servlet。无论有多少客户机访问Servlet,init()
只执行一次。service( ):
执行多次。它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象时,该对象的
service()
方法就要调用,而且传递给这个方法一个 “请求”(ServletRequest)对象和一个 “响应”(ServletResponse)对象作为参数。在HttpServlet中已存在service()
方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。destroy( ):
仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个Servlet在运行
service()
方法时可能会产生其他的线程,因此需要确认在调用destroy()
方法时,这些线程已经终止或完成。
# 1.1 创建
创建执行init()
方法,只执行一次,加载资源
<!--配置ServletMethods-->
<servlet>
<servlet-name>ServletMethods</servlet-name>
<servlet-class>org.iqqcode.web.servlet.ServletMethods</servlet-class>
<!--指定Servle的创建时机(在服务器启动时被创建)
1.第一次被访问时创建
* <load-on-startup>的默认值为-1
2.在服务器启动时创建
* <load-on-startup>的值为0或正整数
-->
<load-on-startup>-5</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ServletMethods</servlet-name>
<url-pattern>/ServletMethods</url-pattern>
</servlet-mapping>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<load-on-startup> 1</load-on-startup>
启动时没有创建servlet,在启动后调用init()
<load-on-startup>-5</load-on-startup>
启动时创建servlet,在启动前调用init()
指定Servle的创建时机(在服务器启动时被创建)
第一次被访问时创建
<load-on-startup>
的默认值为-1在服务器启动时创建
<load-on-startup>
的值为0或正整数
Servlet的init()
方法,只执行一次,说明一个Servlet在内存中只存在一个对象,Servlet是单例的(单例:一个类只能产生一个对象)
那么,为什么Servlet时单例的呢?因为多个用户同时访问时,可能存在线程安全问题。如果同时读,那么数据不会被修改,就没有问题。但是如果A在修改,B在读取,可能就会造成数据不一致的问题。
如何解决呢?
可能大家会想到加锁,但是没法通过同步方法来上锁,因为Servlet是定义了一个标准,我们是来实现的,不能再外部修改,在service()
方法中无法实现。同步代码块上锁之后,一次只允许一个对象来访问资源,对于服务器来说性能低下。
解决:
尽量不要在Servlet中定义成员变量,即使定义了成员变量,也不要修改值。
如果将变量定义在方法中,为局部变量,此时对象在栈中,不共享只在此方法中有效。
//提供服务
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws {
int num = 0;
System.out.println("Hello Servlet!");
}
2
3
4
5
6
# 1.2 提供服务
提供服务时执行service()
方法,执行多次。每次访问Servlet时,service()
方法都会被调用一次
参数 service(ServletRequest servletRequest, ServletResponse servletResponse)
ServletRequest & ServletResponse
对于每一个HTTP请求,servlet容器会创建一个封装了HTTP请求的ServletRequest实例传递给servlet的service方法,ServletResponse则表示一个Servlet响应,其隐藏了将响应发给浏览器的复杂性。通过ServletRequest的方法你可以获取一些请求相关的参数,而ServletResponse则可以将设置一些返回参数信息,并且设置返回内容。
# 1.3 被销毁
Servlet被销毁(服务器正常关闭)前执行destroy
方法,只执行一次 ,释放资源
# 2.Servlet工作原理
当Web服务器接收到一个HTTP请求时,它会先判断请求内容——
如果是静态网页数据,Web服务器将会自行处理,然后产生响应信息;
如果牵涉到动态数据,Web服务器会将请求转交给Servlet容器。此时Servlet容器会找到对应的处理该请求的Servlet实例来处理,结果会送回Web服务器,再由Web服务器传回用户端。
针对同一个Servlet,Servlet容器会在第一次收到Http请求时建立一个Servlet实例,然后启动一个线程。第二次收到Http请求时,Servlet容器无须建立相同的Servlet实例,而是启动第二个线程来服务客户端请求。所以多线程方式不但可以提高Web应用程序的执行效率,也可以降低Web服务器的系统负担。
# 3.容器与Web应用上下文的关系
Tomcat就是容器,其它替代者有Jetty、JBoss等等。
JavaWeb应用就是包含了一组Servlet、JSP、HTML等各种文件的一个集合。就是一定要放在在WEB-INF 目录下(包含web.xml文件)。
下图的Tomcat的组成结构刚好可以解释Java web里面最常见的一些概念:
顶层的Server元素代表Tomcat容器
一个Server可以包含多个Service组件
一个Service里面可以有多个负责接受与响应客户请求的Connector组件
一个Service只能包含一个Engine组件
一个Engine组件可以包含多个虚拟主机(Host)组件(localhost是默认创建的主机,你还可以创建更多的虚拟主机,并且只要把DNS指向这台机器,就可以访问这个虚拟主机的URL)
一个Host组件可以包含多个Context。一个Context就代表一个Web应用。
容器在启动一个Web应用时就创建一个ServletContext对象和这个应用对应起来,并且这个对象和Web应用的生命周期一致。因此,可以利用这个对象来存储全局数据。
# 4.Web服务器工作流程
Servlet工作原理时序图
- Web Client 向Servlet容器(Tomcat)发出Http请求;
- Servlet容器接收Web Client的请求;
- Servlet容器创建一个HttpRequest对象,将Web Client请求的信息封装到这个对象中;
- Servlet容器创建一个HttpResponse对象;
- Servlet容器调用HttpServlet对象的service方法,把HttpRequest对象与HttpResponse对象作为参数传给 HttpServlet对象;
- HttpServlet调用HttpRequest对象的有关方法,获取Http请求信息;
- HttpServlet调用HttpResponse对象的有关方法,生成响应数据;
- Servlet容器把HttpServlet的响应结果传给Web Client;
Web应用的生命周期
启动阶段:加载Web应用的数据,创建ServletContext对象,对Filter和一些Servlet进行初始化。
运行阶段:所有的Servlet都处于待命状态,随时可以响应客户端的请求。
终止阶段:销毁各种对象和资源。
【参考文章】