【Spring MVC + Tomcat】追本溯源,Spring MVC是如何和Tomcat关联到一块的呢?

发布时间 2023-04-17 15:59:06作者: 酷酷-

1  前言

我们应该知道的是Spring MVC其实就是一个Servlet,而我i们平时使用最多的Servlet容器就是Tomcat,那么两者是如何关联到一起的呢,这节我们就来看下。

2  请求过程

我们先来从上层视角浅化的了解一下请求的处理过程,如下图:

(1)用户和Tomcat间建立连接并通过网络发送请求报文给Tomcat。
(2)Tomcat接收用户的报文然后解析报文,并用解析结果生成一个HttpServletRequest对象。
(3)Tomcat同时生成一个空的HttpServletResponse对象,并使用这两个对象去调用SpringMVC。
(4)SpringMVC从HttpServletRequest对象中获取参数,然后进行业务处理。
(5)SpringMVC把业务处理的结果放入HttpServletResponse中,处理流程回到Tomcat中。
(6)Tomcat把HttpServletResponse对象中的内容转化为报文并通过网络发回给用户。
(7)用户接收到报文并使用浏览器展示出来。
用户和Tomcat分别位于不同的地方,所有交互过程使用的是Socket交互。Tomcat和SpringMVC位于一个进程内,所以可以直接调用。
把Spring MVC看作一个整体,和它密切相关的就是HttpServletRequest和HttpServletResponse两个对象,前者用于获取参数信息,后者用于存放处理结果。
抬高视角后,我们发现了SpringMVC的边界,SpringMVC只需要处理好界内事情即可,剩余的由Tomcat来负责。

3  关系溯源

Tomcat早就有了,SpringMVC后来才出现,它俩之间没有什么关系。那Tomcat为啥能够调用到SpringMVC呢?
那是因为它们都是Java Web规范下的产物。Java Web规范简单来说主要就是Servlet。Servlet这个词就是Server和Applet的结合体,意为运行在服务器上的小型程序。
Servlet本身是一个Java接口,不过我们通常把任何实现了这个接口的类也称作Servlet。Servlet有一个最大的特点就是它可以和指定的URL相关联。
当我们请求一个URL的时候,最终会来到和它关联的Servlet里面。所以Servlet是负责处理请求的地方,但是如何从URL来到Servlet里呢,这是Servlet管不了的。
负责这个事情的叫做Servlet容器。Servlet类就运行在Servlet容器中,容器负责接受请求,Servlet负责处理请求。这样的话所有环节就都通顺了。
因此Servlet容器就是Tomcat了,Servlet类就是SpringMVC了。所以说SpringMVC是基于Servlet构建起来的。这一切都是源于Servlet规范。

4  桥梁

按照Servlet规定,SpringMVC应用应该被打成war包,然后放入Tomcat下面,当Tomcat成功启动后,一切自然OK。这是怎么做到的呢?
那是因为Servlet在它们之间定义了一座小桥,其实就是一个接口。这个接口由SpringMVC来实现,由Tomcat来调用这个实现。
请看这个接口,如下图:

接口只有一个方法,方法的第一个参数是“感兴趣”的类的集合,哪些类才算是感兴趣的呢?又如何指定呢?继续往下看。
看SpringMVC对该接口的实现类,如下图:

在类的上面通过@HandlesTypes注解指定的类就是感兴趣的类。这里指定的是一个叫做WebApplicationInitializer(web应用初始化器)的接口,那它的所有实现类就是感兴趣的类。
那Tomcat如何知道有这么一个实现类存在呢?因为Servlet又给出了规范,规范定天下。规范规定:
在war包里的META-INF/services目录下,必须有个名为javax.servlet.ServletContainerInitializer的文件,文件内容必须是一个实现了该接口的类的全名。且这个实现类上可以使用@HandlesTypes注解,来指定你感兴趣的类。
这样Tomcat只要从每个war包里读出这个实现类的全名,然后就可以使用反射实例化它,接着调用它的onStartup方法就行了。
这样Tomcat的启动流程通过这座小桥就来到了SpringMVC里,然后带动了SpringMVC的启动,启动成功后就可以对外提供服务了。

5  上下文路径

一个war包就是一个应用,Tomcat下面可以放多个war包,也就是Tomcat同时支持运行多个应用。那这多个应用之间不会起冲突吗?Tomcat不会搞混吗?
肯定是不会的,因为有一个叫做ContextPath(上下文路径)的东西存在,它就相当于应用的名字,来把它们分开,同时它也是URL的一部分,所以不同应用的URL也是不可能一样的。
同时,Tomcat还定义了类加载器,使一个应用不会加载到别的应用的类,这样保证了安全性。也就是说,即使两个应用中都定义了完全一样的类,那也不是同一个类。
一个Tomcat运行多个应用,如下图:

Tomcat接收到用户的请求,根据URL中的上下文路径和Servlet路径等信息选择出正确的应用里面的适合的Servlet去调用。
Tomcat既然要调用Servlet,那我们必须先把Servlet注册到Tomcat里才行。Tomcat会为每个应用创建一个上下文,称为Servlet上下文(ServletContext)。
每个应用都运行在自己的这个上下文中。如下图:

一个应用主要就是一些Servlet和Filter,所以只需把这些Servlet和Filter注册到这个ServletContext里就可以了。
传统上,Servlet和Filter的配置信息都会放在应用的web.xml文件中,所以Tomcat在启动时会去读这个文件内容,自动进行相关注册。
所以应用信息、上下文路径、Servlet信息和它对应的URL信息都会存储在已启动好的Tomcat内部,这样当一个请求过来后,根据请求的URL信息就匹配出适合的Servlet,然后Tomcat就会去调用它。

6  小结

好啦,到这里我们大致了解了Spring MVC 和 Tomcat的关系,以及两者的互通桥梁,最重要的生态就是规范标准才促成了两者的融合哈,有理解不对的地方欢迎指正哈。