9.2 Servlet体系结构&工作方式
Servlet体系结构基础知识Java Web应用是基于Servlet规范写的,而Servlet规范又是基于以下几个类运转的:Servlet顶层类关联图:从图中可以看到,有三个类ServletConfig、ServletRequest、ServletResponse是与Servlet主动关联的,而且这三个类都是通过容器传递给Servlet滴:ServletConfig 是在 Servlet 初始化
Servlet体系结构
基础知识
Java Web应用是基于Servlet规范写的,而Servlet规范又是基于以下几个类运转的:
Servlet顶层类关联图:
从图中可以看到,有三个类ServletConfig、ServletRequest、ServletResponse是与Servlet主动关联的,而且这三个类都是通过容器传递给Servlet滴:
- ServletConfig 是在 Servlet 初始化时就传给 Servlet 了
- ServletRequest、ServletResponse用户请求达到后,调用 Servlet 时传递过来的。
补充一下知识:
Servlet 的运行模式是一个典型的握手型的交互式运行模式。
握手型的交互式就是两个模块为了交换数据通常都会准备一个交易环境,这个环境一直跟随个这个交易过程直到这个交易完成为止的方式。
对应到Servlet运行里面就是:
ServletConfig & ServletContext
首先你要知道,Servletxxx那都是接口,都是规范,实际运行的时候是要传对象的。
那么ServletConfig & ServletContext传给Servlet真正的对象是什么?
要回答这个问题,首先看他们俩在Tomcat容器的类关系图:
-
ServletConfig:
其中StandardWrapperFacade 是 StandardWrapper 的门面类(参考门面设计模式),而且他们都实现了 ServletConfig 接口。也就是说,Servlet拿到的xxxConfig实际上是StandardWrapperFacade对象(facade——门面)
(StandardWrapperFacade能够保证从 StandardWrapper 中拿到 ServletConfig 所规定的数据,而又不把 ServletConfig 不关心的数据暴露给 Servlet,就很棒!这也是门面设计的目的——封装容器中的数据)
-
ServletContext:
同样的,Servlet拿到的xxxContext实际上是ApplicationContextFacade对象。
(通过 ServletContext 可以拿到 Context 容器中一些必要信息,比如应用的工作路径,容器支持的 Servlet 最小版本等)
ServletRequest & ServletResponse
同样的问题,ServletRequest & ServletResponse传给Servlet真正的对象是什么?
我们自己通常使用的都是 HttpServletRequest 和 HttpServletResponse
在Tomcat容器的类关系图:
-
Tomcat 一接受到请求首先将会创建Request(org.apache.coyote.Request ) 和Response(org.apache.coyote.Response),
这两个类是 Tomcat 内部使用的描述一次请求和相应的信息类它们是一个轻量级的类,它们作用就是简单解析,将这个请求快速的分配给后续线程去处理,所以它们的对象很小,很容易被 JVM 回收。
-
接下去Tomcat交给一个用户线程,请求开始被处理时,又创建Request(org.apache.catalina.connector.Request) 和 Response(org.apache.catalina.connector.Response) 对象。
这两个对象一直贯穿整个 Servlet 容器直到要传给 Servlet。
当然,为了封装数据,最后传给 Servlet 的是门面类 RequestFacade 和 RequestFacade
看图理解:
这里的图只描述了传入,也就是刚刚讲的东西,当然还有传出,因此箭头应该是双向的。
Servlet工作方式
当用户从浏览器向服务器发起一个请求,通常会包含如下信息:http://hostname: port /contextpath/servletpath
现在知道这个URL对应的Servlet叫什么,那Tomcat是如何准确地将这个请求给工程中相应的Servlet呢?
-
Tomcat7.0 中,这种映射工作有专门一个类来完成的,这个就是Mapper(org.apache.tomcat.util.http.Mapper) (这个类保存了 Tomcat 的 Container 容器中的所有子容器的信息)
-
当 Request(org.apache.catalina.connector.Request) 类在进入 Container 容器之前(上面图有画),Mapper 会根据这次请求的 hostname 和 contextpath ,将 host 和Context 容器设置到 Request.mappingData 属性中。
所以当 Request 进入 Container 容器之前,它要访问那个子容器这时就已经确定了。
图解一波:
Request 的 Mapper 类关系图:
注意到MapperListener类,我们会发现在9.1中讲到Tomcat启动过程时的第19步就是这个:
这是 MapperListener 的 init 方法
public void init() {
//不重要的
findDefaultHost();
Engine engine = (Engine) connector.getService().getContainer();
engine.addContainerListener(this);
Container[] conHosts = engine.findChildren();
//将host及下面的子容器注册到中,只要容器有变,就会被通知,MapperListener.mapper属性也会随即被修改
for (Container conHost : conHosts) {
Host host = (Host) conHost; //虽然是host类,但还是看做Container容器理解
if (!LifecycleState.NEW.equals(host.getState())) {
//互相添加对方
host.addLifecycleListener(this); //host加入this监听器
registerHost(host);//注册host到this这里
}
}
}
和前面整合一下,看看Request请求是如何到达Wrapper来处理的(为了方便理解:Wrapper≈Servlet):
(疑难词:valve——n. 阀;真空管;活门)
到达之后,就是大家都知道的,执行Servlet.Service()方法,就开始我们的工作啦!
至此,我们鱼塘里的鱼儿(Servlet)终于动起来啦!
更多推荐
所有评论(0)