了解过tomcat的组成,tomcat的类加载器结构,接下来了解一下tomcat的启动过程。
Bootstrap 的 main 方法
public static void main(String args[]) {
synchronized (daemonLock) {
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();
if (null == daemon.getServer()) {
System.exit(1);
}
} 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);
}
}
main 方法可以分解为两部分:
-
实例化一个 Bootstrap 对象,并调用 init 方法,然后赋值给 daemon 变量.
-
根据传递进来的参数决定走哪一个命令,例如 : 当双击 startup.bat 时,传进来的是 start
本文主要分析 Bootstrap 类的 init, setAwait ,load 以及 start 方法。
init 方法
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\");
startupInstance = startupClass.getConstructor().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\");
paramValues[] = new [1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
catalinaDaemon = startupInstance;
}
init 方法中首先调用initClassLoaders方法初始化Comm er,SharedLoader,CatalinaLoader,然后将初始化好的catalinaLoader设置到当前线程,接着通过反射初始化org.apache.catalina.startup.Catalina类生成对象startupInstance ,并调用setParentClassLoader,将sharedLoader设置为父加载器。
为什么要通过反射调用初始化 Catalina 类?
这样做是为了解耦,Bootstrap 直接依赖JRE运行并为Tomcat应用服务器创建共享类加载器,用于构造Catalina和整个tomcat服务器,而tomcat的主要业务代码在Catalina中,实现了启动入口和核心环境的解耦。
通过观察tomcat编译好的包,我们也可以发现bootstrap在bin目录下,而catalina在lib目录下。
有关initClassLoaders在类加载器篇章中介绍过,这里不再分析。
SecurityClassLoad.securityClassLoad(catalinaLoader)用于线程安全的加载tomcat容器所需的class, 只有当以安全模式启动 tomcat 的时候才会起作用具体实现代码如下:
public static void securityClassLoad(ClassLoader loader) throws Exception {
securityClassLoad(loader, true);
}
static void securityClassLoad(ClassLoader loader, boolean requireSecurityManager)
throws Exception {
if (requireSecurityManager && System.getSecurityManager() == null) {
return;
}
loadCorePackage(loader);
loadCoyotePackage(loader);
loadLoaderPackage(loader);
loadRealmPackage(loader);
loadServletsPackage(loader);
loadSessionPackage(loader);
loadUtilPackage(loader);
loadJavaxPackage(loader);
loadConnectorPackage(loader);
loadTomcatPackage(loader);
}
setAwait 方法
public void setAwait(boolean await)
throws Exception {
Class<?> paramTypes[] = new Class[1];
paramTypes[0] = Boolean.TYPE;
paramValues[] = new [1];
paramValues[0] = Boolean.valueOf(await);
Method method =
catalinaDaemon.getClass().getMethod(\"setAwait\", paramTypes);
method.invoke(catalinaDaemon, paramValues);
}
setAwait 方法通过反射调用了catalina的setAwait方法,该方法设置的await值是留给后面用的标志位,当Catalina将Tomcat的所有组件启动之后,会检查await属性,如果为true,会调用Catalina.await():
public void start() {
......
if (await) {
await();
stop();
}
}
而Catalina.await()又会调用其StandardServer的await 方法:
public void await() {
getServer().await();
}
StandardServer.await()方法包含一个 while 循环,此循环用于监听指定 socket 端口 (默认为 8005) 的连接,当某个连接传入的参数为”SHUTDOWN”(默认为”SHUTDOWN”)时,终止此while循环(端口号和终止while循环的参数,在server. 的Server标签设置)。
我们都知道在 JVM 中, 当不存在任何一个非守护线程的线程时,jvm也就会停止。
Server.await()用来维持Bootstrap的main方法(main thread)处于运行状态,而线程池中监听http请求的线程是守护线程(daemon thread)。
当Tomcat的指定端口接收到关闭命令时,Server.await()内的while循环终止,然后Catalina会调用stop()方法,关闭Tomcat的所有组件,最终Bootstrap的main thread终止,Tomcat关闭。
load 方法
private void load(String[] arguments)
throws Exception {
// Call the load() method
String methodName = \"load\";
param[];
Class<?> paramTypes[];
if (arguments==null || arguments.length==0) {
paramTypes = null;
param = null;
} else {
paramTypes = new Class[1];
paramTypes[0] = arguments.getClass();
param = new [1];
param[0] = arguments;
}
Method method =
catalinaDaemon.getClass().getMethod(methodName, paramTypes);
if (log.isDebugEnabled())
log.debug(\"Calling startup class \" + method);
method.invoke(catalinaDaemon, param);
}
该方法通过反射调用catalina的load方法。
catalina 的 load 方法如下:
public void load() {
if (loaded) {
return;
}
loaded = true;
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 {
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 , just create it manually.
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream(\"server- . \");
inputSource = new InputSource
(getClass().getClassLoader()
.getResource(\"server- . \").toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString(\"catalina.configFail\",
\"server- . \"), e);
}
}
}
if (inputStream == null || inputSource == null) {
if (file == null) {
log.warn(sm.getString(\"catalina.configFail\",
getConfigFile() + \"] or [server- . ]\"));
} 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 {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// Ignore
}
}
}
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalina (Bootstrap.getCatalina File());
// 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\");
}
}
catalina的load方法首先初始化目录(initDirs)和初始化命名服务(initNaming),然后是createStartDigester,为 的标签即解析模式增加处理规则rule:
protected Digester createStartDigester() {
long t1=System.currentTimeMillis();
// Initialize the digester
Digester digester = new Digester();
digester.setValidating(false);
digester.setRulesValidation(true);
Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
// Ignore className on all elements
List<String> Attrs = new ArrayList<>()
继续阅读与本文标签相同的文章
卓象程序员:HTML5新增的主体结构元素
-
史上最强多线程面试44题和答案:线程锁+线程池+线程同步等
2026-05-18栏目: 教程
-
9月最新184道阿里、百度、腾讯、头条Java面试题合集
2026-05-18栏目: 教程
-
美团携手世界粮食计划署共推“拒绝隐性饥饿”健康饮食倡导行动
2026-05-18栏目: 教程
-
圆通回应“承诺达”解散:由直营模式改回加盟商授权经营
2026-05-18栏目: 教程
-
2019 年度 “CCF 杰出会员” 公布,清华北大等86人当选
2026-05-18栏目: 教程
