Tomcat启动过程源码解读

根据Tomcat源码来看一下Tomcat启动过程都做了什么

部分代码为主要流程代码,删去了try-catch以及一些校验逻辑,方便理解主流程

先来一张启动过程时序图,了解一下启动顺序

Tomcat启动的入口类:org.apache.catalina.startup.Bootstrap#main

main方法是整个tomcat启动时的入口。在main方法中,使用bootstrap.init()来初始化类加载器和创建Catalina实例,然后再启动Catalina线程。

public static void main(String args[]) {

if (daemon == null ) {

// Don't set daemon until init() has completed

Bootstrap bootstrap = new Bootstrap();

try {

bootstrap.init();

} catch (Throwable t) {

handleThrowable(t);

t.printStackTrace();

return ;

}

daemon = bootstrap;

} else {

// When running as a service the call to stop will be on a new

// thread so make sure the correct class loader is used to prevent

// a range of class not found exceptions.

Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);

}

try {

String command = "start" ;

if (args.length > 0 ) {

command = args[args.length - 1 ];

}

if (command.equals( "startd" )) {

args[args.length - 1 ] = "start" ;

daemon.load(args);

daemon.start();

} else if (command.equals( "stopd" )) {

args[args.length - 1 ] = "stop" ;

daemon.stop();

} else if (command.equals( "start" )) {

daemon.setAwait( true );

daemon.load(args);

daemon.start();

} else if (command.equals( "stop" )) {

daemon.stopServer(args);

} else if (command.equals( "configtest" )) {

daemon.load(args);

if ( null ==daemon.getServer()) {

System.exit( 1 );

}

System.exit( 0 );

} else {

log.warn( "Bootstrap: command \"" + command + "\" does not exist." );

}

} catch (Throwable t) {

// Unwrap the Exception for clearer error reporting

if (t instanceof InvocationTargetException &&

t.getCause() != null ) {

t = t.getCause();

}

handleThrowable(t);

t.printStackTrace();

System.exit( 1 );

}

}


bootstrap.init()方法,用于初始化容器相关,首先创建类加载器,然后通过反射创建org.apache.catalina.startup.Catalina实例:

public void init() throws Exception {

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;

}


之后Bootstrap的demon.start()方法就会调用Catalina的start方法。

Catalina实例执行start方法。这里有两个点,一个是load()加载server.xml配置、初始化Server的过程,一个是getServer().start()开启服务、初始化并开启一系列组件、子容器的过程。

org.apache.catalina.startup.Catalina#start

public void start() {

if (getServer() == null ) {

load();

}

if (getServer() == null ) {

log.fatal( "Cannot start server. Server instance is not configured." );

return ;

}

long t1 = System.nanoTime();

// Start the new server

try {

getServer().start();

} catch (LifecycleException e) {

log.fatal(sm.getString( "catalina.serverStartFail" ), e);

try {

getServer().destroy();

} catch (LifecycleException e1) {

log.debug( "destroy() failed for failed Server " , e1);

}

return ;

}

long t2 = System.nanoTime();

if (log.isInfoEnabled()) {

log.info( "Server startup in " + ((t2 - t1) / 1000000 ) + " ms" );

}

// Register shutdown hook

if (useShutdownHook) {

if (shutdownHook == null ) {

shutdownHook = new CatalinaShutdownHook();

}

Runtime.getRuntime().addShutdownHook(shutdownHook);

// If JULI is being used, disable JULI's shutdown hook since

// shutdown hooks run in parallel and log messages may be lost

// if JULI's hook completes before the CatalinaShutdownHook()

LogManager logManager = LogManager.getLogManager();

if (logManager instanceof ClassLoaderLogManager) {

((ClassLoaderLogManager) logManager).setUseShutdownHook(

false );

}

}

if (await) {

await();

stop();

}

}


load方法解析server.xml配置文件,并加载Server、Service、Connector、Container、Engine、Host、Context、Wrapper一系列的容器。加载完成后,调用getServer().start()来开启一个新的Server。

下面先看load方法怎么加载组件和容器的:

/**

* 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 ;

file = configFile();

inputStream = new FileInputStream(file);

inputSource = new InputSource(file.toURI().toURL().toString());

inputSource.setByteStream(inputStream);

digester.push( this );

digester.parse(inputSource);

getServer().setCatalina( this );

getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());

getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

// Stream redirection

initStreams();

// Start the new server

getServer().init();

}


首先利用Digester类解析server.xml文件,得到容器的配置,并创建相应的对象,并关联父子容器。依次创建的是StandardServer、StandardService、StandardEngine、StandardHost。

然后拿到StandardServer实例调用init()方法初始化Tomcat容器的一系列组件。一些容器初始化的的时候,都会调用其子容器的init()方法,初始化它的子容器。顺序是StandardServer、StandardService、StandardEngine、Connector。每个容器都在初始化自身相关设置的同时,将子容器初始化。

这里插入一个Tomcat中生命周期的概念。在初始化、开启一系列组件、容器的过程中,由tomcat’管理的组件和容器,都有一个共同的特点,都实现了org.apache.catalina.Lifecycle接口,由Tomcat管理其生命周期。Lifecycle提供一种统一的管理对象生命周期的接口。通过Lifecycle、LifecycleListener、LifecycleEvent,Catalina实现了对tomcat各种组件、容器统一的启动和停止的方式。

在Tomcat服务开启过程中启动的一些列组件、容器,都继承了org.apache.catalina.util.LifecycleBase这个抽象类,其中的init()、start() 方法、stop() 方法,为其子类实现了统一的start和stop管理。方法中具体的initInternal()、startInternal() 和stopInternal() 方法,交由子类自己实现。

看一下LifecycleBase的init()和start()的实现吧:

org.apache.catalina.util.LifecycleBase#start

public final synchronized void init() throws LifecycleException {

if (!state.equals(LifecycleState.NEW)) {

invalidTransition(Lifecycle.BEFORE_INIT_EVENT);

}

try {

setStateInternal(LifecycleState.INITIALIZING, null , false );

initInternal();

setStateInternal(LifecycleState.INITIALIZED, null , false );

} catch (Throwable t) {

ExceptionUtils.handleThrowable(t);

setStateInternal(LifecycleState.FAILED, null , false );

throw new LifecycleException(

sm.getString( "lifecycleBase.initFail" ,toString()), t);

}

}

public final synchronized void start() throws LifecycleException {

if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||

LifecycleState.STARTED.equals(state)) {

if (log.isDebugEnabled()) {

Exception e = new LifecycleException();

log.debug(sm.getString( "lifecycleBase.alreadyStarted" , toString()), e);

} else if (log.isInfoEnabled()) {

log.info(sm.getString( "lifecycleBase.alreadyStarted" , toString()));

}

return ;

}

if (state.equals(LifecycleState.NEW)) {

init();

} else if (state.equals(LifecycleState.FAILED)) {

stop();

} else if (!state.equals(LifecycleState.INITIALIZED) &&

!state.equals(LifecycleState.STOPPED)) {

invalidTransition(Lifecycle.BEFORE_START_EVENT);

}

try {

setStateInternal(LifecycleState.STARTING_PREP, null , false );

startInternal();

if (state.equals(LifecycleState.FAILED)) {

stop();

} else if (!state.equals(LifecycleState.STARTING)) {

invalidTransition(Lifecycle.AFTER_START_EVENT);

} else {

setStateInternal(LifecycleState.STARTED, null , false );

}

} catch (Throwable t) {

ExceptionUtils.handleThrowable(t);

setStateInternal(LifecycleState.FAILED, null , false );

throw new LifecycleException(sm.getString( "lifecycleBase.startFail" , toString()), t);

}

}


可以看到,init()和start()方法里,调用了initInternal()方法、startInternal()方法和stop()方法,这三者最终会走子类的具体实现。

上面的StandardServer的初始化过程就是一个活生生的例子。在Catalina的load过程中,getServer().init()方法就是LifecycleBase中的init()方法,调用initInternal()时是走的StandardServer的实现,StandardServer的initInternal()中会调用StandardServer的init()方法,进行子容器的初始化。然后依次初始化。

看一下代码,了解一下StandardServer中的initInternal()实现。

/**

* Invoke a pre-startup initialization. This is used to allow connectors

* to bind to restricted ports under Unix operating environments.

*/

@Override

protected void initInternal() throws LifecycleException {

super .initInternal();

// Register global String cache

// Note although the cache is global, if there are multiple Servers

// present in the JVM (may happen when embedding) then the same cache

// will be registered under multiple names

onameStringCache = register( new StringCache(), "type=StringCache" );

// Register the MBeanFactory

MBeanFactory factory = new MBeanFactory();

factory.setContainer( this );

onameMBeanFactory = register(factory, "type=MBeanFactory" );

// Register the naming resources

globalNamingResources.init();

// Populate the extension validator with JARs from common and shared

// class loaders

if (getCatalina() != null ) {

ClassLoader cl = getCatalina().getParentClassLoader();

// Walk the class loader hierarchy. Stop at the system class loader.

// This will add the shared (if present) and common class loaders

while (cl != null && cl != ClassLoader.getSystemClassLoader()) {

if (cl instanceof URLClassLoader) {

URL[] urls = ((URLClassLoader) cl).getURLs();

for (URL url : urls) {

if (url.getProtocol().equals( "file" )) {

try {

File f = new File (url.toURI());

if (f.isFile() &&

f.getName().endsWith( ".jar" )) {

ExtensionValidator.addSystemResource(f);

}

} catch (URISyntaxException e) {

// Ignore

} catch (IOException e) {

// Ignore

}

}

}

}

cl = cl.getParent();

}

}

// Initialize our defined Services

for ( int i = 0 ; i < services.length; i++) {

services[i].init();

}

}


再举一个具体的例子:

回到刚才的启动过程中,getServer().start()开启服务的方法,实际就是上面提到的LifecycleBase中的start()方法。其中,会调用org.apache.catalina.core.StandardServer#initInternal方法,初始化Server并调用Service的init方法。org.apache.catalina.core.StandardServer在其实现的startInternal() 中,开启naming resources和services,调用service的start方法,开启所有service,调用其service的startInternal()方法。

下面分别看一下StandardServer中的initInternal()和startInternal()的实现:

org.apache.catalina.core.StandardServer#startInternal

protected void startInternal() throws LifecycleException {

fireLifecycleEvent(CONFIGURE_START_EVENT, null );

setState(LifecycleState.STARTING);

globalNamingResources.start();

// Start our defined Services

synchronized (servicesLock) {

for ( int i = 0 ; i < services.length; i++) {

services[i].start();

}

}

}


这里的service,是org.apache.catalina.core.StandardService的实例。

总结一下启动的Tomcat启动的过程

在Catalina的load方法里,就已经调用了StandardServer里的init方法,一层一层初始化了globalNamingResources,StandardService–》StandardEngine,executors,MapperListener,Connector–》CoyoteAdapter,protocolHandler。至此就将tomcat的catalina中的组件、容器初始化完成。 接下来就是调用start方法一层一层开启,StandardServer的startInternal方法,按层次start:globalNamingResources,StandardService–》StandardEngine,executors,MapperListener,Connector–》StandardHost,StandardContext,protocolHandler。顺序基本同init过程。StandardEngine在start时,会init子容器,并调用子容器的start方法。子容器依次这样init、start,就开启了StandardHost和StandardContext。

参考文章:

tomcat源码分析-Connector初始化与启动

tomcat源码分析-Container初始化与加载

tomcat源码分析-http请求在Container中的执行路线

tomcat源码解析(一)–启动与Server.xml文件的解析

坚持原创技术分享,您的支持将鼓励我继续创作!
0%