首先明确Tomcat的职责是什么,也就是说Tomcat做了哪些事情?
我觉得下面几件事情是Tomcat必须要做的比较重要的事情。
- 监听某个端口,捕获HTTP请求。
- 将HTTP请求转换为相应的Request对象,当然也应该需要创建一个Response对象
- 然后初始化一个Servlet对象,或者说加载一个Servlet对象,将相应的Request对象和Response对象传递进去,之后调用Servlet中的service函数。
Tomcat的架构图
上面一张图片比较详细,下面放一张简单一点的架构图。
Tomcat的架构浅析
通过图片可以看到Tomcat是分成好几个模块的。最外面是一个Server,一个Server可以有好几个Service服务,一个Service服务会有几个Connector组件,但是只会有一个Container组件,另外还有好几个不是那么核心的组件,比如说管理Session的组件,写日志的组件。
其中也提到了最核心的组件(Component)就是Connector和Container。
Connector的作用就是监听某一个端口,然后接受HTTP请求,将HTTP请求封装成Servlet需要使用的HttpServletRequest对象和HttpServletResponse对象。也就是说将纯文本的Http请求解析出来了之后变成可以方便使用的Request对象,而Response对象需要作为一个返回值的容纳地,也是需要传递给Servlet*的。
Container的作用就是根据Request中URL中的信息加载相应的Servlet对象,之后就是调用相应的方法,讲结果封装在Response对象中,最后当然也是转换为HTTP的文本返回给客户端。
A Simple Servlet Container
当有了基本的架构概念了之后,我觉得可以直接看一些简单的小例子,来看一下在代码中这些组件是如何关联作用在一起的,下面就把How Tomcat Works中的一个第五章中的小例子贴出来。(其实前面几个章节有更加简单的例子,感兴趣的可以看下。)
package ex05.pyrmont.startup;
import ex05.pyrmont.core.SimpleContext;
import ex05.pyrmont.core.SimpleContextMapper;
import ex05.pyrmont.core.SimpleLoader;
import ex05.pyrmont.core.SimpleWrapper;
import ex05.pyrmont.valves.ClientIPLoggerValve;
import ex05.pyrmont.valves.HeaderLoggerValve;
import org.apache.catalina.Context;
import org.apache.catalina.Loader;
import org.apache.catalina.Mapper;
import org.apache.catalina.Pipeline;
import org.apache.catalina.Valve;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.http.HttpConnector;
public final class Bootstrap2 {
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
//继承了Wrapper,每一个实例里面都是会有一个Servlet的
Wrapper wrapper1 = new SimpleWrapper();
//设置Servlet的映射地址
wrapper1.setName("Primitive");
//设置Servlet的名字
wrapper1.setServletClass("PrimitiveServlet");
Wrapper wrapper2 = new SimpleWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet");
//context是一个容器可以包含wrapper这个最底层的容器
Context context = new SimpleContext();
context.addChild(wrapper1);
context.addChild(wrapper2);
Valve valve1 = new HeaderLoggerValve();
Valve valve2 = new ClientIPLoggerValve();
//容器中除了其他容器之外还有Valve
//另外要注意的是每一个context都是实现了Pipeline和Context接口的
((Pipeline) context).addValve(valve1);
((Pipeline) context).addValve(valve2);
//这个mapper是做什么的呢?
Mapper mapper = new SimpleContextMapper();
mapper.setProtocol("http");
context.addMapper(mapper);
Loader loader = new SimpleLoader();
//容器中还需要加载器,通过反射加载真正的Servlet对象
context.setLoader(loader);
// context.addServletMapping(pattern, name);
//context里面初始化了一个HashMap,存储映射和Servlet名字
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern");
//因为connector封装好Reqeust之后会调用容器,所以将容器的声明给Connector
connector.setContainer(context);
try {
connector.initialize();
//connector开始监听端口,要明白底层肯定使用ServerSocket来实现的
connector.start();
// make the application wait until we press a key.
System.in.read();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
BootStrap是一个Tomcat启动的程序入口。可以看到源代码中初始化了这么几个对象,一个HttpConnector,两个Wrapper,一个Context,两个Valve,一个Loader等,具体可以看注释,还是把我的理解写在注释里面把。
再看一张图
从第一张架构图中可以看到container是分成下面几个容器的,engine
,host
,context
,wrapper
。关系是这样的一个engine
是可以有零个或者多个host
的,以此类推。
那么这些容器的作用是什么呢?其实是用来匹配相应的URL,也就是讲一个请求的URL来分成者三个部分,最后找到要请求的Servlet。也就是说request
是不断地在容器中传递的。其实容器中还有一个叫做Pipeline
的东西,暂时还是不是很明白这个东西的作用是什么,每一个Pipeline
都有很多的Valve
。(有一点是这样的,Pipeline
其实和过滤器是一个很像的东西)
ServletContext在哪里呢?
其实就是StandardContext中的一个全局变量。
Tomcat8.0.37中StandardContext.java
部分源码
public class StandardContext extends ContainerBase
implements Context, NotificationEmitter
/**
* The ServletContext implementation associated with this Context.
*/
protected ApplicationContext context = null;
/**
* Return the servlet context for which this Context is a facade.
*/
@Override
public ServletContext getServletContext() {
if (context == null) {
context = new ApplicationContext(this);
if (altDDName != null)
context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
}
return (context.getFacade());
}
最后
其实现在如果是用Springmvc这个框架的话,其实只是在web.xml中配置了一个拦截所有请求的前端Servlet,但是之后的事情比如Controller之类的东西都是Sringmvc在做了,而不是最原始的一个请求对应一个Servlet了。
最后的最后
因个人能力有限,以上内容肯定会有错误,所有欢迎交流讨论,另外如果觉得有帮助,可以点击一个喜欢,将这篇文章献给以后的自己和看到最后的你。
参考
JavaWeb学习之Servlet(四)----ServletConfig获取配置信息、ServletContext的应用
己
Tomcat容器结构及Pipeline机制 -我们到底能走多远系列(13)
tomcat架构分析概览
粗浅看 Tomcat系统架构分析
How do servlets work? Instantiation, sessions, shared variables and multithreading
Tomcat源码分析之容器整体结构