天天看点

深入理解Tomcat系列之七:详解URL请求

前言

这里分析一个实际的请求是如何在tomcat中被处理的,以及最后是怎么样找到要处理的servlet的?当我们在浏览器中输入<code>http://hostname:port/contextpath/servletpath</code>,前面的hostname与port用于建立tcp连接,由于http也是基于tcp协议的,所以这里涉及tcp连接的三次握手。后面的contextpath与servletpath则是与服务器进行请求的信息,contextpath指明了与服务器中哪个context容器进行交互,服务器会根据这个url与对应的context容器建立连接,那么这个过程是如何实现的呢?

在tomcat7(本文也是基于tomcat7)中主要通过一个映射来完成的,这个映射的工作交给org.apache.tomcat.util.http.mapper.mapper类来完成的,这个类保存了container容器所有子容器的信息,在请求从connector交给container容器之前,mapper会根据hostname和port将host容器与context容器设置到request的mappingdata属性中,这样在connector的请求进入container容器之前就知道了交给哪个容器了。

这段代码如下:

代码清单5-4:

下面启动服务器,在浏览器中输入http://localhost:8080/examples/jsp/jsp2/el/composite.jsp,断点调试可以mappingdata.host属性为<code>localhost</code>,mappingdata.contextpath.setstring(context.name)中context.name为<code>examples</code>,mappingdata.wrapperpath为<code>/jsp/jsp2/el/composite.jsp</code>,这验证了mappingdata属性的有效性,那么mappingdata属性是如何设置到request对象的属性中的呢?

通过org.apache.catalina.connector.request的源码可以知道,其是通过setcontextpath方法与sethost方法设置进去的,其源码如下:

代码清单5-5:

由于请求是从connector传过来的,而coyoteadapter是connector中处理请求的最后一个类,那么设置这两个属性的代码肯定在coyoteadapter类中,果不其然:

代码清单5-6:

intenalmap方法执行就是代码清单5-4的内容,这样就把从connector传入请求,并设置request对象的mappingdata属性的整个流程就打通了。还有一个疑问是为什么mapper类中可以拥有container所有子容器的信息呢?答案需要回到tomcat启动过程图的第21步的startintenal方法了:

代码清单5-7:

这段代码就是将mapperlistener作为一个监听者加到整个container容器的每一个子容器中,这样任何一个子容器发生变化,mapperlistener都将被通知,响应的mappingdata属性也会改变。最后可以总结访问请求地址为http://localhost:8080/examples/composite.jsp的处理过程:

在端口8080启动server,并通知service完成启动,service通知connector完成初始化和启动的过程

connector首先收到这个请求,会调用protocolhandler完成http协议的解析,然后交给socketprocessor处理,解析请求头,再交给coyoteadapter解析请求行和请求体,并把解析信息封装到request和response对象中

把请求(此时应该是request对象,这里的request对象已经封装了http请求的信息)交给container容器

container容器交给其子容器——engine容器,并等待engine容器的处理结果

engine容器匹配其所有的虚拟主机,这里匹配到host

请求被移交给hostname为localhost的host容器,host匹配其所有子容器context,这里找到contextpath为/examples的context容器。如果匹配不到就把该请求交给路径名为”“的context去处理

请求再次被移交给context容器,context继续匹配其子容器wrapper,由wrapper容器加载composite.jsp对应的servlet,这里编译的servlet是basic_002dcomparisons_jsp.class文件

context容器根据后缀匹配原则*.jsp找到composite.jsp编译的java类的class文件

connector构建一个org.apache.catalina.connector.request以及org.apache.catalina.connector.response对象,使用反射调用servelt的service方法

context容器把封装了响应消息的response对象返回给host容器

host容器把response返回给engine容器

engine容器返回给connector

connetor容器把response返回给浏览器

浏览器解析response报文

显示资源内容

根据前面的内容,其中的映射关系是由mapperlistener类完成的。