Tomcat【调用Servlet与执行逻辑】

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: Tomcat【调用Servlet与执行逻辑】

🍒一.Tomcat调用Servlet


Servlet是属于上层建筑,它处在应用层,它的下层有传输层,网络层,数据链路层,硬件,属于“经济基础”,毕竟下层经济基础决定上层建筑。前面说过,Servlet是一组操作HTTP的API,Tomcat可作为HTTP服务器来处理请求,这个处理请求的关键就是调用Servlet来操作HTTP给客户端做出响应


🍎1.1Tmocat定位


3f0ac094fa4e4c38b79fbbad31ca78f7.png在 Servlet 的代码中我们并没有写 main 方法, 那么对应的 doGet 代码是如何被调用的呢? 响应又是如何返回给浏览器的?其实是Tomcat在调用Servlet,Tomcat其实就是一个应用程序,是运行在用户态上的一个普通的Java进程


当浏览器给服务器发送请求的时候, Tomcat 作为 HTTP 服务器, 就可以接收到这个请求.HTTP 协议作为一个应用层协议, 需要底层协议栈来支持工作. 如下图所示



66d9abfa91754158a5961a4f8c39be7d.png

更详细的交互过程可以参考下图:


0c791a0cd1cc4e598e86a315b35e17b9.png

接收请求:

●用户在浏览器输入一个 URL, 此时浏览器就会构造一个 HTTP 请求.


●这个 HTTP 请求会经过网络协议栈逐层进行 封装 成二进制的 bit 流, 最终通过物理层的硬件设备转换成光信号/电信号传输出去.


●这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达目标主机(这个过程也需要网络层和数据链路层参与).

]

●服务器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成HTTP 请求. 并交给 Tomcat 进程进行处理(根据端口号确定进程)


●Tomcat 通过 Socket 读取到这个请求(一个字符串), 并按照 HTTP 请求的格式来解析这个请求, 根据请求中的 Context Path 确定一个webapp, 再通过 Servlet Path 确定一个具体的 类. 再根据当前请求的方法 (GET/POST/…), 决定调用这个类的 doGet 或者 doPost 等方法. 此时我们的代码中的doGet / doPost 方法的第一个参数 HttpServletRequest 就包含了这个 HTTP 请求的详细信息.


根据请求计算响应:

在我们的 doGet / doPost 方法中, 就执行到了我们自己的代码. 我们自己的代码会根据请求中的一些信息, 来给 HttpServletResponse 对象设置一些属性. 例如状态码, header, body 等.


返回响应:

●我们的 doGet / doPost 执行完毕后, Tomcat 就会自动把 HttpServletResponse 这个我们刚设置好的对象转换成一个符合 HTTP 协议的字符串, 通过 Socket 把这个响应发送出去.


●此时响应数据在服务器的主机上通过网络协议栈层层 封装, 最终又得到一个二进制的 bit 流, 通过物理层硬件设备转换成光信号/电信号传输出去.


●这些承载信息的光信号/电信号通过互联网上的一系列网络设备, 最终到达浏览器所在的主机(这个过程也需要网络层和数据链路层参与).


●浏览器主机收到这些光信号/电信号, 又会通过网络协议栈逐层进行 分用, 层层解析, 最终还原成HTTP 响应, 并交给浏览器处理.


●浏览器也通过 Socket 读到这个响应(一个字符串), 按照 HTTP 响应的格式来解析这个响应. 并且把body 中的数据按照一定的格式显示在浏览器的界面上.


🍒二.Tomcat的执行逻辑


下面的代码通过 “伪代码” 的形式描述了 Tomcat 初始化/处理请求 两部分核心逻辑


初始化与收尾工作,又细分为以下部分:


(1)从指定的目录中找到Servlet类,并加载。

(2)根据加载的结果,给这些类创建实例

(3)创建好实例后,调用Servlet对象中的init方法

(4)创建TCP socket对象,监听8080端口,等待客户端来连接

(5)如果请求处理完毕,也就是处理请求的循环退出了,那Tomcat也结束了,调用destroy方法结束进程,但是这个环节不一定可靠,正常退出的情况下,需要在管理端口(8005)去调用destroy,将Tomcat关闭,但很多时候都是直接杀死进程来达到关闭的目的,此时根本来不及调用dsetroy方法


🍎2.1Tomcat 初始化流程

class Tomcat {
    // 用来存储所有的 Servlet 对象
    private List<Servlet> instanceList = new ArrayList<>();
    public void start() {
        // 根据约定,读取 WEB-INF/web.xml 配置文件;
        // 并解析被 @WebServlet 注解修饰的类
        // 假定这个数组里就包含了我们解析到的所有被 @WebServlet 注解修饰的类. 
        Class<Servlet>[] allServletClasses = ...;
        // 这里要做的的是实例化出所有的 Servlet 对象出来;
        for (Class<Servlet> cls : allServletClasses) {
            // 这里是利用 java 中的反射特性做的
            // 实际上还得涉及一个类的加载问题,因为我们的类字节码文件,是按照约定的
            // 方式(全部在 WEB-INF/classes 文件夹下)存放的,所以 tomcat 内部是
            // 实现了一个自定义的类加载器(ClassLoader)用来负责这部分工作。
            Servlet ins = cls.newInstance();
            instanceList.add(ins);
        }
        // 调用每个 Servlet 对象的 init() 方法,这个方法在对象的生命中只会被调用这一次;
        for (Servlet ins : instanceList) {
            ins.init();
        }
        // 利用我们之前学过的知识,启动一个 HTTP 服务器
        // 并用线程池的方式分别处理每一个 Request
        ServerSocket serverSocket = new ServerSocket(8080);
        // 实际上 tomcat 不是用的固定线程池,这里只是为了说明情况
        ExecuteService pool = Executors.newFixedThreadPool(100);
        while (true) {
            Socket socket = ServerSocket.accept();
            // 每个请求都是用一个线程独立支持,这里体现了我们 Servlet 是运行在多线程环境下的
            pool.execute(new Runnable() {
                doHttpRequest(socket);//处理请求
            });
        }
        // 调用每个 Servlet 对象的 destroy() 方法,这个方法在对象的生命中只会被调用这一次;
        for (Servlet ins : instanceList) {
            ins.destroy();
        }
    }
    public static void main(String[] args) {
        new Tomcat().start();
    }
}

小结:

●Tomcat 的代码中内置了 main 方法. 当我们启动 Tomcat 的时候, 就是从 Tomcat 的 main 方法开始执行的.

●被 @WebServlet 注解修饰的类会在 Tomcat 启动的时候就被获取到, 并集中管理.

●Tomcat 通过 反射 这样的语法机制来创建被 @WebServlet 注解修饰的类的实例.

●这些实例被创建完了之后, 会点调用其中的 init 方法进行初始化.这个方法在对象的生命中只会被调用这一次 (这个方法是 HttpServlet 自带的,我们自己写的类可以重写 init)

●这些实例被销毁之前, 会调用其中的 destory 方法进行收尾工作.这个方法在对象的生命中只会被调用这一次 (这个方法是 HttpServlet 自带的,我们自己写的类可以重写 destory)

●Tomcat 内部也是通过 Socket API 进行网络通信.

●Tomcat 为了能同时相应多个 HTTP 请求, 采取了多线程的方式实现. 因此 Servlet 是运行在 多线程环境 下的.


🍎2.2Tomcat 处理请求流程


(1)读取socket中的数据,并按照HTTP协议的格式来进行解析,获取请求

(2)判断请求是需要静态内容还是动态内容,如果是静态内容,可以在根路径上找到目的文件,返回请求

(3)如果是动态文件,则需要通过URL上的一级路径与二级路径来确定通过哪一个Servlet类来进行处理,没有的话就会返回404

(4)找到对应Servlet对象,调用对象里面的service方法,根据请求的方法来调用对应的do…方法

class Tomcat {
    void doHttpRequest(Socket socket) {
        // 参照我们之前学习的 HTTP 服务器类似的原理,进行 HTTP 协议的请求解析,和响应构建
        HttpServletRequest req = HttpServletRequest.parse(socket);
        HttpServletRequest resp = HttpServletRequest.build(socket);
        // 判断 URL 对应的文件是否可以直接在我们的根路径上找到对应的文件,如果找到,就是静态
        // 直接使用我们学习过的 IO 进行内容输出
        if (file.exists()) {
            // 返回静态内容
            return;
       }
        // 走到这里的逻辑都是动态内容了
        // 根据我们在配置中说的,按照 URL -> servlet-name -> Servlet 对象的链条
        // 最终找到要处理本次请求的 Servlet 对象
        Servlet ins = findInstance(req.getURL());
        // 调用 Servlet 对象的 service 方法
        // 这里就会最终调用到我们自己写的 HttpServlet 的子类里的方法了
        try {
          ins.service(req, resp); 
       } catch (Exception e) {
            // 返回 500 页面,表示服务器内部错误
       }
   }
}


🍎2.3service方法执行逻辑

class Servlet {
    public void service(HttpServletRequest req, HttpServletResponse resp) {
        String method = req.getMethod();
        if (method.equals("GET")) {
            doGet(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);
       } 
       ......
   }
}


在整个流程中,有三个关键的方法:


●init方法,在初始化阶段执行,用来初始化每一个Servlet对象,对象创建好之后,就会执行,用户可重写该方法,来执行一些初始化程序的逻辑,没有重写,init方法一般是空的,也就是什么也不执行。

●destroy方法,退出请求处理的主循环之后,tomcat结束之前就会执行到该方法,用来释放资源。

●service方法,在处理请求的阶段调用,针对每个动态资源的请求都需要调用

相关文章
|
2月前
|
Java 应用服务中间件 Maven
JavaWeb基础5——HTTP,Tomcat&Servlet
JavaWeb技术栈、HTTP、get和post区别、响应状态码、请求响应格数据式、IDEA使用Tomcat、报错解决、Servlet的体系结构、IDEA使用模板创建Servlet
JavaWeb基础5——HTTP,Tomcat&Servlet
|
5月前
|
Java 应用服务中间件 Maven
|
5月前
|
前端开发 应用服务中间件
|
6月前
|
JSON Java 应用服务中间件
|
5月前
|
XML 数据格式
|
5月前
|
XML Java 应用服务中间件
|
5月前
|
前端开发 Java 应用服务中间件
|
6月前
|
XML 前端开发 Java
Tomcat和Servlet
Tomcat和Servlet
42 0
|
6月前
|
前端开发 Java 应用服务中间件
HTTP&Tomcat&Servlet
HTTP&Tomcat&Servlet
81 0
|
Java 应用服务中间件 Linux
HTTPS && Tomcat && Servlet && 博客系统 && 软件测试的概念 && Linux
HTTPS && Tomcat && Servlet && 博客系统 && 软件测试的概念 && Linux
57 0