Servlet运行原理以及生命周期

前言:
         

Servlet是一个java编写的程序,此程序是在服务器端运行的,

是按照Servlet规范编写的一个

java类。Servlet是处理客户端的请求,并将处理结果以响应的

方式返回给客户端。Servlet框架

是怎样的呢?它的生命周期又是什么情况呢?这是本文需要探

求的

🍊 Tomcat 执行 servlet 架构图
从上面可以得出结论: http://localhost:8080/ks/login ----
1.程序如果一旦打成 war 包,扔 tomcat 中,其实就已经和开
发环境没有任何联系和瓜葛
2.应用程序与应用程序直接,是通过上下文 Context 来隔离
3.其实我们在程序中编写的大量的 Servlet 其实都在给
Tomcat 编写子类。给 HttpServlet 编写子类
4. 如果 tomcat 一旦运行起来,那么就把应用程序所有对应
的子类 Servlet 全部加载内存中(没有实例化),然后用
上线文隔离,准备接受请求。
5. 如果用户一旦发起 http 请求,如果协议和端口都是一致
的,并且上下文 Context 匹配的。然后调用执行引擎
Engine 类,这个引擎类就把要执行的请求对象,响应对
象初始化准备好。(这个都有 HttpServletRequest,
HttpServletResponse
6. 然后通过你路由地址,根据你 path=/login----> 映射的
class---com.kuangstudy.LoginServlet --- 用反射实例化
LoginServlet 对象, 但是它只会实例化一次
<servlet>
2 <servlet-name>loginServlet</serlvet-
name>
3 <servlet-
class>com.kuangstudy.LoginServlet</servle
t-class>
4 </servlet>
5 <servlet-mapping>
6 <servlet-name>loginServlet</serlvet-
name>
7 <ul-pattern>/login</ul-pattern>
8 </servlet-mapping>
7.在实例化具体 servlet 对象的时候,先调用你的构造函
数, ---- 调用类中的 init 方法 --- 初始化结束
8.根据请求方法,统一执行 service 方法, ---- 在这个方法
中,会根据你的请求方式去执行具体的方法比如 get 请求
执行 doGet ,如果是 post 就执行 doPost
9.结束请求 ---destory() 没事具体实现。是空方法。
🍐 Servlet 框架
网上下载 Servlet 源码,解压之后发现其由两个包组成:
1 javax.servlet
2 javax.servlet.http
📦 javax.servlet
此包中定义了所有 Servlet 类都必须实现的接口或类。
接口定义:
1.ServletConfig 接口 --- 在初始化过程中由 Servlet 容器 (Tomcat
)
2.ServletContext 接口 --- 定义 Servlet 用于获取容器信息的方法
3.ServletRequest 接口 --- 向服务器请求信息
4.ServletResponse 接口 --- 响应客户端请求
Servlet 接口 --- 定义所有的 Servlet 必须实现的方法
类定义:
1.ServletInputStream --- 用于从客户端读取二进制数据 ---
求对象的输入流 ---- 遵循 http
2.ServletOutputStream --- 用于将二进制数据写入到客户端
3.GenricServlet--- 抽象类,定义一个通用的,独立于底层协议
servlet
📦 java.servlet.http
此包中定义了使用 HTTP 通信协议的所有 Servlet 类应该实现的
类、接口。
接口定义
HttpServletRequest 接口 --- 封装 http 请求
HttpServletResponse 接口 --- 封装 http 响应
HttpSession 接口 --- 用于表示客户端存储有关客户的信息
HttpSessionAttributeListener 接口 --- 实现这个监听接口,当
用户获取 Session 的属性列表发生 改变的时候得到通知。
类的定义
HttpServlet --- 扩展了 GenericServlet 的抽象类
Cookie --- 创建一个 Cookie Cookie 技术,用户存储服务器
发送给客户端的信息。
通过阅读 Servlet 框架源码,其主要的框架结构如下图:
🍊 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
附图:
Tomcat HttpServlet 是如何进行交互的呢?从源码中我们可
以得到
🍊 Servlet 生命周期
Servlet 框架中所有的 Servlet 类都必须实现 Servlet 这个接
口。其中定义了三个方法
1 init 方法:负责初始化 Servlet 对象。
2 service 方法:用于响应客户端的请求
3 destroy :销毁 Servlet 对象,释放占用的资源。
🍊 为什么说 Servlet 是请求的时候才初始化
呢?  --- 解决空间
如果说 Servlet 不是在请求的时候就初始化的?那应该在
启动的时候就执行你的构造函数(节约内存空间,需要
的时候在 new
比如想 springioc 容器就这样,它启动 springioc 就自
动去加载你的构造函数(用空间 ( 浪费一点 jvm 内存 )
换(初始化对象)时间)  --- springioc是用空间换时间
🍊 为什么 LoginServlet 每次请求它只会实例化
一次呢?
每个线程执行的 Servlet 创建只会实例化一次,只要有人
请求实例化了。就会对象就不会在创建了。
经典面试题: Servlet 是线程安全的吗? --- 不是

🍊Servlet生命周期四个阶段:

🍐 加载阶段
Tomcat 从文件系统,远程文件系统或其他网络服务中通过类
加载器来加载 Servlet ,并调用 Servlet 的默认构造方法 ( 不带参
构造器 )
🍐 初始化阶段 init() 方法
Servlet 容器启动时:读取 web.xml 配置文件中的信息,构造
指定的 Servlet 对象,根据配置
文件的信息创建 ServletConfig 对象,并将其作为参数传递给
init 方法进行调用。
Tomcat 启动后:用户首次想某个 Servlet 对象发送请求,
Tomcat 会判断内存中是否存在指定的
servlet 对象,如果没有则会去创建它,然后创建
HttpRequest HttpResponse 对象,调 service
方法处理用户的请求。从 Servlet 的构造开始我们没有显示的看
init() 方法的调用,那么 init 方法到底是何时进行调用的呢?
阅读源码可以知道: init 方法是在实例化 Servlet 之后调用的,
其参数 ServletConfig 是在 Servlet 初始化阶段 Tomcat 根据
web.xml 配置信息,和操作系统的相关环境生成并传递给 init
* Called by the servlet container to
indicate to a servlet that the
* servlet is being placed into service.
*
* <p>The servlet container calls the
<code>init</code>
* method exactly once after
instantiating the servlet.
* The <code>init</code> method must
complete successfully
* before the servlet can receive any
requests.
*
* <p>The servlet container cannot place
the servlet into service
* if the <code>init</code> method
* <ol>
* <li>Throws a
<code>ServletException</code>
* <li>Does not return within a time
period defined by the Web server

🍐 响应客户请求阶段 service 方法
service() 方法是在客户端第一次访问 servlet 时执行的,其实
init 方法同样也是在有客户端访问
servlet 的时候才被调用。不过需要特别注意的是讨论 init 方法
session 级别上时,当存在不同的
会话访问相同的 servlet 时, Tomcat 会开启一个线程处理这个
新的会话,但是此时 Tomcat 容器
不会实例化这个 servlet 对象,也就是有多个线程在共享这个
servlet 实例。换句话说 Servlet 对象在 servlet 容器中是以单例的
形式存在的!然而查看其源码可以发现, Servlet 在多线程下并
未使用同步机制,因此,在并发编程下 servlet 是线程不安全
的。对于 Servlet 的并发,线程安全的处理问题,笔者会找个时
间好好的整理下思路。对于不同的 session 访问相同的 serlvet
对象,只有一次 init 的过程,笔者会在接下来予以演示。阅读
HttpServlet 的源码可以知道,基于 Http 通信协议的
HttpServlet 在进行客户端响应处理的时候根据客户端请求,响
应的类别不同分别调用不同的方法,其中最常用的就是
doGet doPost 方法,这两个方法是我们在编写 Servlet 中的主
要的逻辑处理阶段
protected void service(HttpServletRequest
req, HttpServletResponse resp)
2 throws ServletException, IOException
3 {
4 String method = req.getMethod();
5
6 if (method.equals(METHOD_GET)) {
7 long lastModified =
getLastModified(req);
8 if (lastModified == -1) {
9 // servlet doesn't support if-
modified-since, no reason
10 // to go through further expensive
logic
11 doGet(req, resp);
12 } else {
13 long ifModifiedSince =
req.getDateHeader(HEADER_IFMODSINCE);
14 if (ifModifiedSince < (lastModified
/ 1000 * 1000)) {
15 // If the servlet mod time is
later, call doGet()
16 // Round down to the
nearest second for a proper compare
17 // A ifModifiedSince of
-1 will always be less
18 maybeSetLastModified(resp,
lastModified);
19 doGet(req, resp);
20 } else {21
resp.setStatus(HttpServletResponse.SC_NOT_MO
DIFIED);
22 }
23 }
24
25 } else if (method.equals(METHOD_HEAD)) {
26 long lastModified =
getLastModified(req);
27 maybeSetLastModified(resp,
lastModified);
28 doHead(req, resp);
29
30 } else if (method.equals(METHOD_POST)) {
31 doPost(req, resp);
32
33 } else if (method.equals(METHOD_PUT)) {
34 doPut(req, resp);
35
36 } else if (method.equals(METHOD_DELETE))
{
37 doDelete(req, resp);
38
39 } else if
(method.equals(METHOD_OPTIONS)) {
40 doOptions(req,resp);
41
42 } else if (method.equals(METHOD_TRACE))
{
43 doTrace(req,resp);
44
45 } else {
46 //
47 // Note that this means NO servlet
supports whatever
48 // method was requested, anywhere on
this server.
49 //50
51 String errMsg =
lStrings.getString("http.method_not_implemen
ted");
52 Object[] errArgs = new Object[1];
53 errArgs[0] = method;
54 errMsg =
MessageFormat.format(errMsg, errArgs);
55
56
resp.sendError(HttpServletResponse.SC_NOT_IM
PLEMENTED, errMsg);
57 }
58 }

🍐 终止阶段: destroy() 方法的调用(没有实现的方法)
上面的探讨中知道的了 Servlet 是如何加载、初始化、处理客户
端的请求响应的,那么 Servlet
在什么时候终止呢?其生命周期又是在什么时候结束的呢?
我们知道的是 Servlet 生命周期是有 Tomcat 容器来管理的,由
此在 Tomcat 关闭、或者 Restart
的时候, servlet 的生命周期必然结束, destroy 方法也必然被
调用过。在客户端与服务器的一次
Session 会话中, session 关闭之后 servlet 并未销毁。后续演
示。
总的来说 servlet 对象什么时候 destroy 的呢?
1 Tomcat 服务器 stop
2 web 项目 reload
3 Tomcat 容器所在的服务器 shutdown( 这不废话吗? )
更正之处:对于 destroy() 方法笔者略有疑惑, 它到底是如何
销毁 Servlet 的呢? ,基于这个问题
特意的去查看了源码,结果发现 destroy() 方法在 Servlet 框架中
并未具体去实现。它是由 Coder
自己去实现的。因此 “destroy() 方法用户销毁 Servlet” 这种说法
本身就是离谱的!
🍊 destory 方法
    此方法,并不是说当前 LoginServlet 执行请求结束之后执
行的方法。
           tomcat 容器如果内部执行 reload 或者关闭的服务器,会来
触发执行
    其实 tomcat 内部也并没有具体实现,其实这个方
法,没有什么意义
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值