连接器负责外部交流,容器负责内部处理。
具体来说就是,连接器处理 Socket 通信和应用层协议解析,得到 Servlet 请求;而容器则负责处理 Servlet 请求。

Servlet 容器的设计

在 Tomcat 里,容器就是用来装载 Servlet 的。那 Tomcat 的 Servlet 容器是如何设计的呢?
image.png
Wrapper 表示一个 Servlet。
Context 表示一个 Web 应用程序,可能包含多个 Servlet;
Host 代表的是一个虚拟主机,可以配置多个虚拟主机地址,部署多个 Web 应用程序;
但一般只部署一个应用。
Engine 表示引擎,用来管理多个虚拟站点,一个 Service 最多只能有一个 Engine。

Tomcat 的server.xml配置文件:
image.png

Servlet 容器的实现

这些容器具有父子关系,形成一个树形结构。Tomcat 使用了设计模式中的组合模式来管理这些容器。

所有容器组件都实现了 Container 接口,组合模式可以使得用户对单容器对象和组合容器对象的使用具有一致性。单容器对象指的是最底层的 Wrapper,组合容器对象指的是上面的 Context、Host 或者 Engine。

Container 接口扩展了 Lifecycle 接口,Lifecycle 接口用来统一管理各组件的生命周期。

Tomcat 会创建一个 Service 组件和一个 Engine 容器组件。在 Engine 容器下创建两个 Host 子容器,在每个 Host 容器下创建两个 Context 子容器。

定位 Servlet

Tomcat 用 Mapper 组件确定具体的 Wrapper 来处理 Servlet 请求
Mapper 组件的功能就是将用户请求的 URL 定位到一个 Servlet。
Mapper 保存了容器组件与访问路径的映射关系,比如Host 容器里配置的域名、Context 容器里的 Web 应用路径,以及 Wrapper 容器里 Servlet 映射的路径,

比如用户访问 url:http://user.shopping.com:8080/order/buy。Tomcat 如何定位到一个 Servlet 呢?
image.png

Tomcat 的 HTTP 连接器监听 8080 端口、AJP 连接器监听 8009 端口。

Mapper 组件根据域名选定 Host,根据 URL 的路径来匹配 Web 应用的路径,进而确定 Context 和 Wrapper(Servlet)。

并不只有 Servlet 处理请求,查找路径上的父子容器都会对请求做一些处理。调用过程具体是使用 Pipeline-Valve 管道(责任链模式)实现。
image.png

多个 Context 对比

Tomcat 内的 Context 组件跟 Servlet 规范中的 ServletContext 接口有什么区别?
跟 Spring 中的 ApplicationContext 又有什么关系?

1)Servlet规范中ServletContext表示web应用的上下文环境,而web应用对应tomcat的概念是Context,ServletContext自然是tomcat的Context具体实现的一个成员变量。

2)tomcat内部实现:ServletContext对应tomcat实现是org.apache.catalina.core.ApplicationContext,Context容器对应tomcat实现是org.apache.catalina.core.StandardContext。ApplicationContext是StandardContext的一个成员变量。

3)Spring的ApplicationContext之前已经介绍过,tomcat启动过程中ContextLoaderListener会监听到容器初始化事件,它的contextInitialized方法中,Spring会初始化全局的Spring根容器ApplicationContext,初始化完毕后,Spring将其存储到ServletContext中。

Servlet规范中ServletContext是tomcat的Context的一个成员。
Spring的ApplicationContext是Servlet规范中ServletContext的一个属性。

设计模式

组合模式:一个对象包含逻辑上的子对象。如果你需要维护一堆具有父子关系的实体,可以考虑使用组合模式。
https://www.runoob.com/design-pattern/composite-pattern.html

责任链模式:一个请求处理的过程中有很多处理者依次对请求进行处理,每个处理者负责做自己相应的处理,处理完之后将再调用下一个处理者继续处理。