标签:nta 网络 对象 and 类加载 应用信息 png etc 目录
2019年我在公众号写了《品Spring》和《玩转SpringBoot》这两个系列的文章。但平心而论大部分开发者还是使用SpringMVC最多。所以我打算写一个SpringMVC系列,就叫《聊透SpringMVC》,因为我越来越觉得聊一件事如果没聊透,那就等于没聊。
抬高视角
要看透一件事情,必须要抬高自己的视角,因为有一句诗就是“不识庐山真面目,只缘身在此山中”。
请看一个高层次的请求处理过程,如下图01:
第一步:用户和Tomcat间建立连接并通过网络发送请求报文给Tomcat。
第二步:Tomcat接收用户的报文然后解析报文,并用解析结果生成一个HttpServletRequest对象。
第三步:Tomcat同时生成一个空的HttpServletResponse对象,并使用这两个对象去调用SpringMVC。
第四步:SpringMVC从HttpServletRequest对象中获取参数,然后进行业务处理。
第五步:SpringMVC把业务处理的结果放入HttpServletResponse中,处理流程回到Tomcat中。
第六步:Tomcat把HttpServletResponse对象中的内容转化为报文并通过网络发回给用户。
第七步:用户接收到报文并使用浏览器展示出来。
用户和Tomcat分别位于不同的地方,所有使用Socket交互。Tomcat和SpringMVC位于一个进程内,所以可以直接调用。
把SpringMVC看作一个整体,和它密切相关的就是HttpServletRequest和HttpServletResponse两个对象,前者用于获取参数信息,后者用于存放处理结果。
抬高视角后,我们发现了SpringMVC的边界,SpringMVC只需要处理好界内事情即可,剩余的由Tomcat来负责。
追本溯源
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规范。
一座小桥
按照Servlet规定,SpringMVC应用应该被打成war包,然后放入Tomcat下面,当Tomcat成功启动后,一切自然OK。这是怎么做到的呢?
那是因为Servlet在它们之间定义了一座小桥,其实就是一个接口。这个接口由SpringMVC来实现,由Tomcat来调用这个实现。
请看这个接口,如下图02:
接口只有一个方法,方法的第一个参数是“感兴趣”的类的集合,哪些类才算是感兴趣的呢?又如何指定呢?继续往下看。
请看SpringMVC对该接口的实现类,如下图03:
在类的上面通过@HandlesTypes注解指定的类就是感兴趣的类。这里指定的是一个叫做WebApplicationInitializer(web应用初始化器)的接口,那它的所有实现类就是感兴趣的类。
那Tomcat如何知道有这么一个实现类存在呢?因为Servlet又给出了规范,哈哈,规范走天下啊。规范规定:
在war包里的META-INF/services目录下,必须有个名为javax.servlet.ServletContainerInitializer的文件,文件内容必须是一个实现了该接口的类的全名。且这个实现类上可以使用@HandlesTypes注解,来指定你感兴趣的类。
这样Tomcat只要从每个war包里读出这个实现类的全名,然后就可以使用反射实例化它,接着调用它的onStartup方法就行了。
这样Tomcat的启动流程通过这座小桥就来到了SpringMVC里,然后带动了SpringMVC的启动,启动成功后就可以对外提供服务了。
上下文路径
一个war包就是一个应用,Tomcat下面可以放多个war包,也就是Tomcat同时支持运行多个应用。那这多个应用之间不会起冲突吗?Tomcat不会搞混吗?
肯定是不会的,因为有一个叫做ContextPath(上下文路径)的东西存在,它就相当于应用的名字,来把它们分开,同时它也是URL的一部分,所以不同应用的URL也是不可能一样的。
同时,Tomcat还定义了类加载器,使一个应用不会加载到别的应用的类,这样保证了安全性。也就是说,即使两个应用中都定义了完全一样的类,那也不是同一个类。
一个Tomcat运行多个应用,如下图04:
Servlet上下文
Tomcat接收到用户的请求,根据URL中的上下文路径和Servlet路径等信息选择出正确的应用里面的适合的Servlet去调用。
Tomcat既然要调用Servlet,那我们必须先把Servlet注册到Tomcat里才行。Tomcat会为每个应用创建一个上下文,称为Servlet上下文(ServletContext)。
每个应用都运行在自己的这个上下文中。如下图05:
一个应用主要就是一些Servlet和Filter,所以只需把这些Servlet和Filter注册到这个ServletContext里就可以了。
传统上,Servlet和Filter的配置信息都会放在应用的web.xml文件中,所以Tomcat在启动时会去读这个文件内容,自动进行相关注册。
所以应用信息、上下文路径、Servlet信息和它对应的URL信息都会存储在已启动好的Tomcat内部,这样当一个请求过来后,根据请求的URL信息就匹配出适合的Servlet,然后Tomcat就会去调用它。
(END)
标签:nta 网络 对象 and 类加载 应用信息 png etc 目录
原文地址:https://blog.51cto.com/15049788/2561495