package org.apache.catalina.startup;
src file:http://svn.apache.org/repos/asf/tomcat/tc6.0.x/tags/TOMCAT_6_0_42/java/org/apache/catalina/startup/Bootstrap.java
goal:了解tomcat启动的大致流程
欢迎各位指正其中的错误和不足
入口main方法
public static void main(String args[]) {
if (daemon == null) {
daemon = new Bootstrap();
try {
daemon.init();//init(完成了加载环境变量和类加载器的工作)
} catch (Throwable t) {
t.printStackTrace();
return;
}
}
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);
// 信息: Initialization processed in 3024454 ms
daemon.load(args);
// 启动server
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
t.printStackTrace();
}
}
以上是bootstrap类的main方法,里边逐步调用了一些用于初始化的方法.下面一一解读:
init()方法:
/**
* Initialize daemon.
*/
public void init() throws Exception {
// Set Catalina path
setCatalinaHome();// 加载Catalina.Home环境变量(catalina.home)
setCatalinaBase();// 加载Catalina.Base环境变量(catalina.base)
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;
// 找到org.apache.catalina.startup.Catalina#setParentClassLoader方法
Method method = startupInstance.getClass().getMethod(methodName, paramTypes);
// 设置java.lang.ClassLoader为父类加载器
method.invoke(startupInstance, paramValues);
catalinaDaemon = startupInstance;// 设置Catalina进程
}
init()方法
- 对环境变量的读取,主要是读取 catalina.home\catalina.base
- 初始化类加载器,默认使用当前类的类加载器 this.getClass().getClassLoader();
- 加载了Catalina类 classLoader.loadClass("org.apache.catalina.startup.Catalina")
- 反射设置父类加载器 org.apache.catalina.startup.Catalina#setParentClassLoader设置java.lang.ClassLoader为ParentClassLoader
daemon.setAwait(true)方法:
/**
* Set flag.
*/
public void setAwait(boolean await) throws Exception {
Class paramTypes[] = new Class[1];
paramTypes[0] = Boolean.TYPE;
Object paramValues[] = new Object[1];
paramValues[0] = new Boolean(await);
// 获取Catalina类(其实是其父类Embedded)中的setAwait方法
Method method = catalinaDaemon.getClass().getMethod("setAwait", paramTypes);
// 使用await参数调用
method.invoke(catalinaDaemon, paramValues);
}
setAwait方法
- 这个方法并没有具体的深入研究,不过从字面意义上来看,应该是设置了一个锁.欢迎各位指正.
daemon.load(args)方法:
/**
* Load daemon.
*/
private void load(String[] arguments) throws Exception {
// Call the load() method
String methodName = "load";
Object param[];
Class paramTypes[];
if (arguments==null || arguments.length==0) {
paramTypes = null;
param = null;
} else {
paramTypes = new Class[1];
paramTypes[0] = arguments.getClass();
param = new Object[1];
param[0] = arguments;
}
// 获取Catalina的load方法
Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes);
if (log.isDebugEnabled())
log.debug("Calling startup class " + method);
// 调用(启动一个新的服务器实例)
method.invoke(catalinaDaemon, param);
}
daemon.load方法
通过反射调用Catalina的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;
try {
file = configFile();//获取server.xml位置
inputStream = new FileInputStream(file);
inputSource = new InputSource("file://" + file.getAbsolutePath());
} catch (Exception e) {
;
}
// 如果不能获取server.xml,则使用ClassLoader再查找一次
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader().getResourceAsStream(getConfigFile());
inputSource = new InputSource(getClass().getClassLoader().getResource(getConfigFile()).toString());
} catch (Exception 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 ((inputStream == null) && (file != null)) {
log.warn("Can't load server.xml from " + 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);// 加载server.xml
inputStream.close();
} catch (Exception e) {
log.warn("Catalina.start using "
+ getConfigFile() + ": " , e);
return;
}
// Stream redirection
initStreams();// 设置log
// Start the new server
if (getServer() instanceof Lifecycle) {
try {
getServer().initialize();
} 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()设置命名空间catalina.useNaming\java.naming.factory.url.pkgs\java.naming.factory.initial 这几个命名空间没有具体研究作用,同样欢迎补充
- 创建Digester
- 读取server.xml文件
- #digester.push(this)
- #digester.parse(inputSource)使用digester解析server.xml并按照配置创建Server对象
- #initStreams()设置log
- 调用#getServer().initialize()启动server
daemon.start()方法:
/**
* Start the Catalina daemon.
*/
public void start() throws Exception {
if (catalinaDaemon == null)
init();
Method method = catalinaDaemon.getClass().getMethod("start", (Class[]) null);
method.invoke(catalinaDaemon, (Object[]) null);
}
daemon.start方法
通过反射调用Catalina的start方法
/**
* Start a new server instance.
*/
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
if (getServer() instanceof Lifecycle) {
try {
((Lifecycle) getServer()).start();
} catch (LifecycleException e) {
log.error("Catalina.start: ", e);
}
}
long t2 = System.nanoTime();
if(log.isInfoEnabled())
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
try {
// 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);
}
}
} catch (Throwable t) {
// This will fail on JDK 1.2. Ignoring, as Tomcat can run
// fine without the shutdown hook.
}
if (await) {
await();
stop();
}
}
Catalina.start方法
- 逐步启动server的生命周期