最近在看Tomcat的源码,下面用博客记下看源码的一些心得。
Tomcat是从org.apache.catalina.startup.Bootstrap#main()开始启动. 大致分为三个步骤,即init、load和start。代码如下:
Java代码
public static void main(String args[]) {
try {
// Attempt to load JMX class
new ObjectName("test:foo=bar");
} catch (Throwable t) {
System.out.println(JMX_ERROR_MESSAGE);
try {
// Give users some time to read the message before exiting
Thread.sleep(5000);
} catch (Exception ex) {
}
return;
}
if (daemon == null) {
daemon = new Bootstrap();
try {
daemon.init(); ★1
} catch (Throwable t) {
t.printStackTrace();
return;
}
}
try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[0] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[0] = "stop";
daemon.stop();
} else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args); ★2
// 反射调用Catalina的start方法
daemon.start(); ★3
} else if (command.equals("stop")) {
daemon.stopServer(args);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
从以上可以很清楚的看出tomcat是通过参数的不同进行相应的命令调用。
★1 启动、初始化(加载类)
启动之前要进行相应的init()初始化,进行相应的环境设置以及包的加,以下是init()方法。(org.apache.catalina.startup.Bootstrap.init())
Java代码
public void init()
throws Exception
{
setCatalinaHome();//设置Catalina安装目录
setCatalinaBase();//设置Catalina工作目录
initClassLoaders();//加载jar包
// 将classload设置进线程,以便我们使用时进行调用
Thread.currentThread().
setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// 加载启动类和调用它的process方法
if (log.isDebugEnabled())
log.debug("Loading startup class");
Class startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
// 设置共享扩张类加载器
if (log.isDebugEnabled())
log.debug("Setting startup class properties");
String methodName = "setParentClassLoader";
Class paramTypes[] = new Class[1];
paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName,
paramTypes);
method.invoke(startupInstance, paramValues);
catalinaDaemon = startupInstance;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
在加载jar的时候,需要初始化classloader,代码如下:(org.apache.catalina.startup.Bootstrap)
Java代码
private void initClassLoaders() {
try {
commonLoader = createClassLoader("common", null);
catalinaLoader= createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
tomcat中的加载方式是:
|-------commonLoader (common)-> System Loader
|-------sharedLoader (shared)-> commonLoader -> System Loader
|-------catalinaLoader(server) -> commonLoader -> System Loader
Common是公共类加载器,负责加载tomcat内部和web应用程序可以看到的类(%CATALINA_HOME%/bin/common下的jar文件),Catalina负责加载的是tomcat内部使用的类(%CATALINA_HOME%/server下的jar文件),这些类对web应用程序不可见。Shared负责加载的是web应用程序之间共享的类(%CATALINA_BASE%/shared下的jar文件),这些类对于tomcat内部是不可见的。如果%CATALINA_HOME%/conf/catalina.Properties中没有指定Common的搜索路径,则用当前的类的类加载器即系统类加载器作为Common。
★2 装载相应的资源
下面主要讲解tomcat的load()方法。下图是Catalina.load方法的时序图。

(1) 从上面的时序图可以看出首先调用Catalina类的load()方法,具体代码如下:
(org.apache.catalina.startup.Catalina)。
Java代码
public void load() {
initDirs();
// Before digester - it may be needed
initNaming();
// Create and execute our Digester
Digester digester = createStartDigester();
try {
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource); //对server.xml进行解析
inputStream.close();
}
......
// Start the new server
if (server instanceof Lifecycle) {
try {
server.initialize(); //server初始化工作
} catch (LifecycleException e) {
log.error("Catalina.start", e);
}
}
long t2 = System.currentTimeMillis();
log.info("Initialization processed in " + (t2 - t1) + " ms");
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
(2) 在上面的load()方法中需要进行server的初始化工作,下图为Catalina.initialize的时序图,从图中可以看出server初始化所完成的工作。

至此,load方法结束,初期化的工作结束,下面开始进入start方法。
★3 容器启动
容器启动时,会调用Catalina.start(),下图为它的时序图。从图中可以看出StandardService的start方法被调用后会分别对Container和Connector进行start方法的调用。

1. Bootstrap调用Catalina的start方法
Catalina.start()方法(org.apache.catalina.startup.Catalina.start())
Java代码
public void start() {
// 启动server
if (server instanceof Lifecycle) {
try {
((Lifecycle) server).start();
......
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
2. Catalina调用StandardServer的start方法
StandardServer.start() (org.apache.catalina.core.StandardServer.start() )
Java代码
public void start() throws LifecycleException {
synchronized (services) {
for (int i = 0; i < services.length; i++) {
if (services[i] instanceof Lifecycle)
((Lifecycle) services[i]).start();
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
3. StandardServer调用StandardService的start方法
Java代码
org.apache.catalina.core.StandardService.start() )
public void start() throws LifecycleException {
if (container != null) {
synchronized (container) {
if (container instanceof Lifecycle) {
// standardEngine的启动
((Lifecycle) container).start();
}
}
//两个connector的启动,8080和8009
synchronized (connectors) {
for (int i = 0; i < connectors.length; i++) {
if (connectors[i] instanceof Lifecycle)
((Lifecycle) connectors[i]).start();
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
以上StandardService.start()方法主要实现了两个功能,standardEngine的启动和connector的启动,下面分别来介绍。
#p#
下面是standardEngine的启动和connector的启动
● standardEngine的启动
(1) 首先是StandardEngine.start()被调用
Java代码
public void start() throws LifecycleException {
// Standard container startup
//进行logger,manager,cluster,realm,resource的启动
super.start();
}
- 1.
- 2.
- 3.
- 4.
- 5.
(2) super.start()--->org.apache.catalina.core.ContainerBase#start()
Java代码
public synchronized void start() throws LifecycleException {
//(省略) server.xml中配置应用组件的启动
//StandardHost容器的启动,
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof Lifecycle)
((Lifecycle) children[i]).start();
}
//StandardPipeline的启动(容器与容器间的管道)
if (pipeline instanceof Lifecycle)
((Lifecycle) pipeline).start();
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
(3) StandardHost.start()被调用
Java代码
public synchronized void start() throws LifecycleException {
//返回到以上的containerBase#start执行pipeline
super.start();
}
- 1.
- 2.
- 3.
- 4.
(4) StandardPipeline#start
Java代码
public synchronized void start() throws LifecycleException {
// 将会调用HostConfig#start方法
lifecycle.fireLifecycleEvent(START_EVENT, null);
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
(5) HostConfig#start
Java代码
public void start() {
//部暑webapps
deployApps();
}
- 1.
- 2.
- 3.
- 4.
(6) HostConfig#deployApps
Java代码
protected void deployApps() {
File appBase = appBase();
File configBase = configBase();
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list());
// Deploy WARs, and loop if additional descriptors are found
deployWARs(appBase, appBase.list());
// Deploy expanded folders
deployDirectories(appBase, appBase.list());
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
(7) deployWARs
Java代码
protected void deployWARs(File appBase, String[] files) {
……
deployWAR(contextPath, dir, file);
}
- 1.
- 2.
- 3.
- 4.
(8) deployWAR
Java代码
protected void deployWAR(String contextPath, File war, String file) {
if (context instanceof Lifecycle) {
// (省略)
Class clazz = Class.forName(host.getConfigClass());
LifecycleListener listener =
(LifecycleListener) clazz.newInstance();
((Lifecycle) context).addLifecycleListener(listener);
}
context.setPath(contextPath);
context.setDocBase(file);
//以下这一步跟进去,,StandardContext的启动
host.addChild(context);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
(9) StandardContext#start
在Context的启动过程中,主要完成了以下任务。
----------------------------------------------------------------------------------------------------------------------
a) 设置web app的具体目录webappResources。
b) postWorkDirectory (),创建临时文件目录。Tomcat下面有一个work目录,用来存放临时文件。
c) 触发START_EVENT事件监听,在这个事件监听里面会启动ContextConfig的start()事件,ContextConfig是用来配置web.xml的。
d) 为context创建welcome files,通常是这三个启动文件:index.html、index.htm、index.jsp
e) 配置filter
f) 启动带有
g) 注册JMX。
----------------------------------------------------------------------------------------------------------------------
至此,Container启动完毕,下面是connector的启动。
● connector的启动
(1) org.apache.catalina.connector.Connector.start()
Java代码
public void start() throws LifecycleException {
// Http11Protocol的启动
protocolHandler.start();
}
- 1.
- 2.
- 3.
- 4.
(2) Http11Protocol#start
Java代码
public void start() throws Exception {
try {
//到了终点的启动
endpoint.start();
} catch (Exception ex) {
log.error(sm.getString("http11protocol.endpoint.starterror"), ex);
throw ex;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
(3) JIoEndPoint#start
Java代码
public void start()
throws Exception {
for (int i = 0; i < acceptorThreadCount; i++) {
//这里的acceptor是一个线程,里面是一个serversocket的启动
Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
acceptorThread.setPriority(threadPriority);
acceptorThread.setDaemon(daemon);
acceptorThread.start();
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
(4) Acceptor#run
Java代码
public void run() {
// Accept the next incoming connection from the server socket
try {
//这里进行了accept(),等待客户端消息,进行接收
Socket socket = serverSocketFactory.acceptSocket(serverSocket);
serverSocketFactory.initSocket(socket);
// Hand this socket off to an appropriate processor
if (!processSocket(socket)) {
// Close socket right away
try {
socket.close();
} catch (IOException e) {
// Ignore
}
}
}catch ( IOException x ) {
if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);
} catch (Throwable t) {
log.error(sm.getString("endpoint.accept.fail"), t);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
至此Connector.start方法调用完毕。整个server启动完毕。
#p#
本次讲解一下Tomcat请求处理的流程,不当之处还请comment。
一. Tomcat 总体结构
Tomcat采用模块化管理,下面是 Tomcat 的总体结构图:

从上图中可以看出 Tomcat 的核心是两个组件:Connector 和 Container。下面是一些概念的介绍。
① Server
一个server代表了整个catalina servlet容器,在Tomcat里面的Server的用处是启动和监听服务端事件(诸如重启、关闭等命令)。
② Service
Service是由一个或多个Connector与一个Engine的组合。
③ Connector
Connector将在某个指定的端口上监听客户的请求,把从socket传递过来的数据,封装成Request,传递给Engine来处理,并从Engine处获得响应并返回给客户。
Tomcat通常会用到两种Connector:
a) Http Connector 在端口8080处侦听来自客户browser的http请求。
b) AJP Connector 在端口8009处侦听来自其它WebServer(Apache)的servlet/jsp代理请求。
二、请求处理过程解析
1. Connector处理请求
Connector处理请求的流程大致如下:

Connector组件启动后,会侦听相关的端口的客户端请求。
(1) 接受一个新的连接请求(org.apache.tomcat.util.net.TcpWorkerThread)
Java代码
void runIt(Object[] perThrData){
Socket s = null;
try {
s = endpoint.acceptSocket(); //获取一个请求
} finally {
if (endpoint.isRunning()) {
endpoint.tp.runIt(this);
// 此处启动另一个TcpWorkerTread去接受其他请求,此线程处理已接受的请求
}
}
TcpConnection con = null;
con = (TcpConnection) perThrData[0];
con.setEndpoint(endpoint);
con.setSocket(s);endpoint.getConnectionHandler().processConnection(con,(Object[]) perThrData[1]);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
(2) 新接收的请求被传到Http11ConnectionHandler中处理。(org.apache.coyote.http11.Http11Protocol.Http11ConnectionHandler)
Java代码
void processConnection(TcpConnection connection, Object[] thData){
Http11Processor processor=null;
processor=(Http11Processor)thData[Http11Protocol.THREAD_DATA_PROCESSOR];
socket=connection.getSocket();
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
processor.setSocket(socket );
processor.process(in, out);
//processor是org.apache.coyote.http11.Http11Processor 的 一个实例
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
(3) 在 Http11Processor 中处理 http11 协议相关的信息(org.apache.coyote.http11.Http11Processor)
Java代码
void process(InputStream input, OutputStream output) throws IOException{
~~略~~
inputBuffer.setInputStream(input);
outputBuffer.setOutputStream(output);
inputBuffer.parseHeaders();
//http11 协议头在此方法中被取出
adapter.service(request, response);
//adapter 是org.apache.catalina.connector.CoyoteAdapter 的 一个实例
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
接下来的流程交由容器进行处理。
2. 容器处理请求
容器交由Pipeline处理,这个Pipeline里面会放置一些vavle,请求沿着pipeline传递下去并且vavle对其进行相关的处理。比如说日志等,valve还可以自定义,具体需要查看server.xml配置文件。相关类图如下:

Tomcat的主要处理组件Engine、Host、Context和Wrapper的实现都会实现Pipeline接口,实际对请求的处理是一个Adpater,Tomcat中Adapter的实现是CoyoteAdapter,因此容器请求处理的入口是CoyoteAdapter的service方法。
1. CoyoteAdapter.service
--组装好请求处理链
--StandardEngine. getPipeline().getFirst().invoke(request, response);
--StandardEngineValve.invoke
2. StandardEngineValve.invoke
--Host.getPipeline().getFirst().invoke(request, response);
--StandardHostValve.invoke
3. StandardHostValve.invoke
--Context. getPipeline().getFirst().invoke(request, response);
--StandardContextValve.invoke
4. StandardContextValve.invoke
--ServletRequestListener.requestInitialized
--Wrapper.getPipeline().getFirst().invoke(request, response);
--StandardWrapperValve.invoke
-- ServletRequestListener.requestDestroyed
5. StandardWrapperValve.invoke
--组装Filter+Servlet
--处理请求
(1) Connector传来的请求调用CoyoteAdapter.service()方法。(org.apache.catalina.connector.CoyoteAdapter)
Java代码
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res)
throws Exception {
~~略~~
if (request == null) {
request = (Request) connector.createRequest();
request.setCoyoteRequest(req);
response = (Response) connector.createResponse();
response.setCoyoteResponse(res);
//创建request、response对象
~~略~~
}
try {
if (postParseRequest(req, request, res, response)) {
connector.getContainer().getPipeline().getFirst().invoke(request, response);
//此处的Container是StandardEngine对象
~~略~~
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
(2) 默认StandardEngine的Pipeline会有StandardEngineValve处理单元(参照StandardEngine构造函数)。(org.apache.catalina.core.StandardEngineValve)
Java代码
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Host to be used for this Request
Host host = request.getHost();
if (host == null) {
response.sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
// Ask this Host to process this request
host.getPipeline().getFirst().invoke(request, response);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
(3) 同样的,StandardHost的Pipeline会有StandardHostValve处理单元。StandardHostValve如何处理请求跟StandardEngineValve类似,接下来请求进入到StandardContextValve.invoke
(4) 同样的,StandardContext的Pipeline会有StandardContextValve处理单元。
Java代码
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Disallow any direct access to resources under WEB-INF or META-INF MessageBytes requestPathMB = request.getRequestPathMB();
if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/META-INF"))
|| (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
|| (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
String requestURI = request.getDecodedRequestURI();
notFound(requestURI, response);
return;
}
// Wait if we are reloading
while (context.getPaused()) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
;
}
}
// Select the Wrapper to be used for this Request
Wrapper wrapper = request.getWrapper();
if (wrapper == null) {
String requestURI = request.getDecodedRequestURI();
notFound(requestURI, response);
return;
}
//ServletRequestListener. requestInitialized
~~略~~
wrapper.getPipeline().getFirst().invoke(request, response);
//ServletRequestListener.requestDestroyed
~~略~~
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
(5) 同样的,StandardWrapper这个Pipeline会有StandardWrapperValve这个处理单元。在invoke()方法调用Filter的同时,servlet.service()方法也将会被调用。
(org.apache.catalina.core.StandardWrapperValve)
Java代码
void invoke(Request request, Response response, ValveContext valveContext)
throws IOException, ServletException{
Servlet servlet = null;
HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
//org.apache.catalina.Request被封装成javax.servlet.http.HttpServletRequest.
HttpServletResponse hres =(HttpServletResponse) response.getResponse();
// org.apache.catalina.Response被封装成javax.servlet.http.HttpServletResponse.
servlet = wrapper.allocate(); // 装载servlet
if ((servlet != null) && (filterChain != null)) {
filterChain.doFilter(hreq, hres); //调用此servlet的filterchain
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
(6) 调用servlet的filterchain 处理 request和response
(org.apache.catalina.core.ApplicationFilterChain)
Java代码
void doFilter(ServletRequest request, ServletResponse response) throws
IOException, ServletException{
~~略~~
internalDoFilter(request,response);
~~略~~
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
(7) 调用internalDoFilter()处理请求。(org.apache.catalina.core.ApplicationFilterChain)
Java代码
void internalDoFilter(ServletRequest request, ServletResponse response) throws
IOException, ServletException{
// 此处省略filter 处理的代码,filter 被一个一个调用。
// 如果http请求的是一个jsp页面, 下面的 servlet 会是 org.apache.jasper.servlet.JspServlet 类的一个实例
// 若是 html 页面, 下面的 servlet 会是 org.apache.catalina.servlets.DefaultServlet 类的一个实例
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse)) {
servlet.service((HttpServletRequest) request, (HttpServletResponse) response);
servlet.service(request, response);
} else {
servlet.service(request, response);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
至此,servlet.service()方法被调用。
【编辑推荐】