Tomcat第二篇——启动过程
Posted 搬砖小松鼠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Tomcat第二篇——启动过程相关的知识,希望对你有一定的参考价值。
一、启动类
Tomcat的启动涉及到两个类:BootStrap和Catalina类。Catalina类用于启动和关闭Server对象,并通过Digester来解析conf目录下的Server.xml文件。BootStrap类则是一个入口类,入口是main方法。下面来看看源码中Tomcat的启动过程。
二、如何调试源码
这里介绍一种比较简单的方法:导入jar包调试法。
找一个自己配过tomcat的web项目,在pom中引入Tomcat的嵌入jar包,如下图所示。其中版本设置为你自己下载的tomcat版本即可。
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>7.0.84</version>
</dependency>
三、启动过程
BootStrap类
首先在BootStrap类的main方法中打一个断点,启动项目就会停在如下断点:
1.调用init方法
BootStrap的init方法会做以下的事情
(1)设置Catalina的一些目录信息。setCatalinaHome、setCatalinaBase
(2)初始化类加载器。initClassLoaders会初始化Tomcat的三个类加载器:CommonClassLoader、CatalinaClassLoader、SharedClassLoader
(3)用CatalinaClassLoader加载Catalina类,并通过反射得到Catalina的实例,再通过反射设置父加载器,最后将catalina实例赋值给Daemon
执行完初始化之后,会回到main执行后续的逻辑
public void init()
throws Exception
// Set Catalina path
setCatalinaHome();
setCatalinaBase();
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
Class<?> startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.newInstance();
// Set the shared extensions class loader
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;
2.依据传入的args类型,分别执行不同的动作方法
前面截图中已经看到参数是“start”,因此会走到如下逻辑:
3.接下来看看load和start中做了些什么
load方法中通过反射调用了daemon的load方法,也就是Catalina的load方法
start方法和load方法类似,这里不再赘述
总体看来,BootStrap做的工作也不多,初始化了几个类加载器,然后将所有的load、start等动作通过反射反映到Catalina上面
Catalina类
1.首先看下被Bootstrap调用的load方法
(1)创建一个Digester对象。createStartDigester方法里面点进去看看就是按照xml中配置的结构
(2)读取并解析配置文件。configFile默认就是server.xml,首先将当前对象Catalina设置为根对象
(3)解析完之后就得到了Server对象,并设置Server对象的Catalina参数未当前对象
(4)调用Server的init方法进行初始化。
Server对象是StandardServer的实例,它内部的initInternal方法会遍历该Server下的Service实例的init方法。
Service的initInternal方法又会初始化Container、Executor和Connectors。
Connector会调用各个ProtocolHandler的init方法。Protocol是处理各种协议处理的父接口,里面又会调用各个处理协议的Endpoint的init方法,并绑定端口
这样依次各个组件都得到了初始化
/**
* Start a new server instance.
*/
public void load()
long t1 = System.nanoTime();
initDirs();
// Before digester - it may be needed
initNaming();
// Create and execute our Digester
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
catch (Exception e)
if (log.isDebugEnabled())
log.debug(sm.getString("catalina.configFail", file), e);
if (inputStream == null)
try
inputStream = getClass().getClassLoader()
.getResourceAsStream(getConfigFile());
inputSource = new InputSource
(getClass().getClassLoader()
.getResource(getConfigFile()).toString());
catch (Exception e)
if (log.isDebugEnabled())
log.debug(sm.getString("catalina.configFail",
getConfigFile()), e);
// This should be included in catalina.jar
// Alternative: don't bother with xml, just create it manually.
if( inputStream==null )
try
inputStream = getClass().getClassLoader()
.getResourceAsStream("server-embed.xml");
inputSource = new InputSource
(getClass().getClassLoader()
.getResource("server-embed.xml").toString());
catch (Exception e)
if (log.isDebugEnabled())
log.debug(sm.getString("catalina.configFail",
"server-embed.xml"), e);
if (inputStream == null || inputSource == null)
if (file == null)
log.warn(sm.getString("catalina.configFail",
getConfigFile() + "] or [server-embed.xml]"));
else
log.warn(sm.getString("catalina.configFail",
file.getAbsolutePath()));
if (file.exists() && !file.canRead())
log.warn("Permissions incorrect, read permission is not allowed on the file.");
return;
try
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
catch (SAXParseException spe)
log.warn("Catalina.start using " + getConfigFile() + ": " +
spe.getMessage());
return;
catch (Exception e)
log.warn("Catalina.start using " + getConfigFile() + ": " , e);
return;
finally
try
inputStream.close();
catch (IOException e)
// Ignore
getServer().setCatalina(this);
// Stream redirection
initStreams();
// Start the new server
try
getServer().init();
catch (LifecycleException e)
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
throw new java.lang.Error(e);
else
log.error("Catalina.start", e);
long t2 = System.nanoTime();
if(log.isInfoEnabled())
log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
StandardServer中的initInternal方法:
StandardService中的initInternal方法:
Connector中的init方法,这里protocolHandler取决于xml中的配置,如处理bio的Http11Protocol
Protocolhandler的init实现是在AbstractProtocol
AbstractProtocol中的init实现
AbstractEndPoint中的init方法,调用bind
2.start方法和load中的init方法类似,不再赘述
对于Connector,各个EndPoint实现中会启动acceptor线程,每个Acceptor就会监听Sorket的连接
以上是关于Tomcat第二篇——启动过程的主要内容,如果未能解决你的问题,请参考以下文章