https://blog.csdn.net/weixin_48321993/article/details/125422511
https://blog.csdn.net/weixin_48321993/article/details/126027770

Web容器基础认知

如何实现服务器和浏览器的交互

客户端和服务器端之间的交互式通过Socket来实现的,它属于应用层的协议。

  • HTTP协议
  • Socket
    Socket是网络连接的一个端点。套接字使得一个应用可以从网络中读取和写入数据。
    放在两个不同计算机上的两个应用可以通过连接发送和接受字节流。为了从你的应用发送一条信息到另一个应用,你需要知道另一个应用的 IP 地址和套接字端口。
    在 Java 里边,套接字指的是java.net.Socket类。
  • ServerSocket
    Socket 类代表一个客户端套接字,即任何时候你想连接到一个远程服务器应用的时候你构造的套接字。
    现在,假如你想实施一个服务器应用,例如一个 HTTP 服务器或者 FTP 服务器,你需要一种不同的做法。这是因为你的服务器必须随时待命,因为它不知道一个客户端应用什么时候会尝试去连接它。
    为了让你的应用能随时待命,你需要使用java.net.ServerSocket类。这是服务器套接字的实现。

ServerSocket 和 Socket 不同,服务器套接字的角色是等待来自客户端的连接请求。一旦服务器套接字获得一个连接请求,它创建一个 Socket 实例来与客户端进行通信。

什么是Servlet

所谓Servlet,其实就是Sun为了让Java能实现动态可交互的网页,从而进入Web编程领域而制定的一套标准!

一个Servlet主要做下面三件事情:

  1. 创建并填充Request对象,包括:URI、参数、method、请求头信息、请求体信息等
  2. 创建Response对象
  3. 执行业务逻辑,将结果通过Response的输出流输出到客户端

Servlet没有main方法,所以,如果要执行,则需要在一个容器里面才能执行,这个容器就是为了支持Servlet的功能而存在。
Tomcat其实就是一个Servlet容器的实现。

Tomcat和Spring、Spring MVC的关系

Spring MVC

DispatcherServlet

在整个 Spring MVC 框架中,DispatcherServlet 处于核心位置,它负责协调和组织不同组件完成请求处理并返回响应工作。
DispatcherServlet 是 SpringMVC统一的入口,所有的请求都通过它。

总结

  1. Tomcat是HTTP服务器和Servlet容器,负责给类似Spring这种servlet提供一个运行的环境。
    • Http服务器,可以把HTTP服务器想象成前台的接待,负责网络通信和解析请求;
    • Servlet容器是业务部门,负责处理业务请求。
  2. Tomcat作为Web服务器和Servlet容器的结合,可以接受网络http请求解析为Servlet规范的请求对象和响应对象。
    • Servlet是规范。
    • HttpServletRequest对象是Tomcat提供的,Tomcat是实现规范的Servlet容器。
    • SpringMVC是处理Servlet请求的应用,其中DispatcherServlet实现了Servlet接口,Tomcat负责加载和调用DispatcherServlet。
    • DispatcherServlet有自己的容器-Spring MVC容器(子容器),这个容器负责管理Spring MVC相关的bean,比如Controler和ViewResolver等。同时,Spring中还有其他的Bean,比如Service和DAO等,这些由全局的Spring IOC容器(父容器)管理。
  3. 如果只是使用spring(不包含springmvc),那么是tomcat容器解析xml文件,通过反射实例化对应的类,根据这些servlet规范实现类,触发对应的代码处理逻辑,这个时候tomcat负责http报文的解析和servlet调度的工作。
  4. 如果使用spring mvc,那么tomcat只是解析http报文,然后将其转发给dispatchsetvlet,然后由springmvc根据其配置,实例对应的类,执行对应的逻辑,然后返回结果给dispatchservlet,最后由它转发给tomcat,由tomcat负责构建http报文数据。

Tomcat源码结构


Tomcat 源码位于 java 文件夹下面。java目录下,分为了两个结构,一个是javax,一个是org.apache

javax

javax中保存的是新的JavaEE规范。可以具体来看看每个目录的作用。

模块 作用说明
annotation annotation 这个模块的作用是定义了一些公用的注解,避免在不同的规范中定义相同的注解。
ejb ejb是个古老的传说,我们不管
el 在jsp中可以使用EL表达式,这么模块解析EL表达式的
mail 和邮件相关的规范
persistence 持久化相关的
security 和安全相关的内容
servlet 这个指定的是Servlet的开发规范,Tomcat本质上就是一个实现了Servlet规范的一个容器,Servlet定义了服务端处理Http请求和响应的方式(规范)
websocket 定义了使用 websocket 协议的服务端和客户端 API
xml.ws 定义了基于 SOAP 协议的 xml 方式的 web 服务

org.apache

org.apache这个包是Tomcat的源码包,也是针对上面的JavaEE规范的部分实现,Tomcat的本质就是对JavaEE的某些规范的实现合集,首先肯定实现了Servlet规范。

模块 作用
catalina catalina是Tomcat的核心模块,里面完整的实现了Servlet规范,Tomcat启动的主方法也在里面,后面我们分析的重点。
coyote tomcat 的核心代码,负责将网络请求转化后和 Catalina 进行通信。
el 这个是上面javax中的el规范的实现
jasper 主要负责把jsp代码转换为java代码。
juli 日志相关的工具
naming 命名空间相关的内容
tomcat 各种辅助工具,包括 websocket 的实现。

Tomcat架构设计

Tomcat的前身为Catalina,Catalina又是一个轻量级的Servlet容器
在美国,catalina是一个很美的小岛。所以Tomcat作者的寓意可能是想把Tomcat设计成一个优雅美丽且轻量级的web服务器。
Tomcat从4.x版本开始除了作为支持Servlet的容器外,额外加入了很多的功能,比如:jsp、el、naming等等,所以说Tomcat不仅仅是Catalina。

从组件的角度看

Tomcat 要实现 2 个核心功能:

  1. 处理 Socket 连接,负责网络字节流与 Request 和 Response 对象的转化。
  2. 加载和管理 Servlet,以及具体处理 Request 请求。

因此 Tomcat 设计了两个核心组件连接器(Connector)和容器(Container)来分别做这两件事情。连接器负责对外交流,容器负责内部处理。
Connector 连接器Container 容器可以说是 Tomcat 架构里最重要的两部分。

  • Server
    表示整个tomcat服务器,它提供了一种优雅的方式来启动和停止整个系统,不必单独启停连接器和容器。
    它是Tomcat构成的顶级构成元素,所有一切均包含在Server中,包含多组服务(Service),负责管理和启动各个Service。

    • Service
      表示服务,Server可以运行多个Service服务。比如一个Tomcat里面可运行订单服务、支付服务、用户服务等等;

      Server的实现类StandardServer可以包含一个到多个Services,Service的实现类为StandardService调用了容器(Container)接口,其实是调用了Servlet Engine(引擎),而且StandardService类中也指明了该Service归属的Server。

      • 核心组件:Connector 连接器
        表示连接器, 它将Service和Container连接起来
        注册在Service中,指定当前Service要监听的端口port。
        用于监听端口,接受来自客户端的请求,并将请求转交给Engine处理,同时将来自Engine的答复返回给客户端。
        • ProtocolHandler
        • Endpoint
        • Processor
        • Adapter
      • 核心组件:Container 容器
        表示容器,可以看做Servlet容器。
        引擎(Engine)、主机(Host)、上下文(Context)和Wraper均继承自Container接口,所以它们都是容器。
        • Engine 引擎
          引擎是Servlet 的顶层容器,用来管理多个虚拟站点,一个 Service 最多只能有一个 Engine。
        • Host 虚拟主机
          虚拟主机,负责 web 应用的部署和 Context 的创建。可以给 Tomcat 配置多个虚拟主机地址,而一个虚拟主机下可以部署多个 Web 应用程序。
        • Context 上下文
          Web 应用上下文,包含多个 Wrapper,负责 web 配置的解析、管理所有的 Web 资源。一个Context对应一个 Web 应用程序。
        • Wrapper 包装器
          表示一个 Servlet,最底层的容器,是对 Servlet 的封装,负责 Servlet 实例的创建、执行和销毁。
      • Service其他内部组件
        Manager – 管理器,用于管理会话Session
        Logger – 日志器,用于管理日志
        Loader – 加载器,和类加载有关,只会开放给Context所使用
        Pipeline – 管道组件,配合Valve实现过滤器功能
        Valve – 阀门组件,配合Pipeline实现过滤器功能
        Realm – 认证授权组件

web.xml

从一个完整请求的角度看

假设来自客户的请求为:http://localhost:8080/test/index.jsp

  • 请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector获得
  • Connector把该请求交给它所在的Service的Engine来处理,并等待Engine的回应
  • Engine获得请求localhost:8080/test/index.jsp,匹配它所有虚拟主机Host
  • Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机)
  • localhost Host获得请求/test/index.jsp,匹配它所拥有的所有Context
  • Host匹配到路径为/test的Context(如果匹配不到就把该请求交给路径名为””的Context去处理)
  • path=”/test”的Context获得请求/index.jsp,在它的mapping table中寻找对应的servlet
  • Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类,构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法
  • Context把执行完了之后的HttpServletResponse对象返回给Host
  • Host把HttpServletResponse对象返回给Engine
  • Engine把HttpServletResponse对象返回给Connector
  • Connector把HttpServletResponse对象返回给客户browser

简而言之,Connector 连接器接收连接请求,创建Request和Response对象用于和请求端交换数据,然后分配线程让Engine(也就是Servlet容器)来处理这个请求,并把产生的Request和Response对象传给Engine。当Engine处理完请求后,通过Connector将响应返回给客户端。

Tomcat如何接收到一个http请求

Connector组件Endpoint中的Acceptor监听客户端套接字连接并接收Socket。

深入了解:Tomcat生命周期

为了统一管理Tomcat中的核心组件的生命周期,专门设计了LifeCycle接口来统一管理。

LifeCycle接口设计

深入了解:Tomcat初始化与启动

startup.bat -> catalina.bat 中 找到启动的主类为 org.apache.catalina.startup.Bootstrap

深入了解:核心组件Connector

深入了解:核心组件Container

Container是容器的父接口,所有子容器都必须实现这个接口,简单来说就是服务器部署的项目是运行在Container中的。
Container里面的Servlet获取到Connector传递过来对应的的Request对象和Response对象进行相应的操作。

Mapper组件机制及请求处理机制

https://blog.csdn.net/NaShiYu/article/details/111773908

Tomcat 类加载机制

打破双亲委派

为什么Tomcat的类加载器不是双亲委派模型
Java默认的类加载机制是通过双亲委派模型来实现的,而Tomcat实现的方式又和双亲委派模型有所区别。

原因在于一个Tomcat容器允许同时运行多个Web程序,每个Web程序依赖的类又必须是相互隔离的。
因此,如果Tomcat使用双亲委派模式来加载类的话,将导致Web程序依赖的类变为共享的。

举个例子,假如我们有两个Web程序,一个依赖A库的1.0版本,另一个依赖A库的2.0版本,他们都使用了类xxx.xx.Clazz,其实现的逻辑因类库版本的不同而结构完全不同。那么这两个Web程序的其中一个必然因为加载的Clazz不是所使用的Clazz而出现问题!而这对于开发来说是非常致命的!

  • Common类加载器,负责加载Tomcat和Web应用都复用的类
  • Catalina类加载器,负责加载Tomcat专用的类,而这些被加载的类在Web应用中将不可见
  • Shared类加载器,负责加载Tomcat下所有的Web应用程序都复用的类,而这些被加载的类在Tomcat中将不可见
  • WebApp类加载器,负责加载具体的某个Web应用程序所使用到的类,而这些被加载的类在Tomcat和其他的Web应用程序都将不可见
  • Jsp类加载器,每个jsp页面一个类加载器,不同的jsp页面有不同的类加载器,方便实现jsp页面的热插拔