根据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。
参考文章: