Bootstrap启动分析
Bootstrap是Tomcat的入口,正常情况下启动Tomcat就是调用Bootstrap的main方法,下面看具体代码:
1. #main()方法
//Bootstrap类
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);
}
}
该方法分为两个部分:
- 创建Bootstrap实例,并调用#init()方法初始化。
- 根据main方法的参数执行对应的命令,默认执行start方法。
2. Bootstrap.init()方法
public void init() throws Exception {
//1. 初始化 classLoader,主要包括commonLoader,catalinaLoader,sharedLoader
initClassLoaders();
//2.设置线程上下文类加载器为catalinaLoader
Thread.currentThread().setContextClassLoader(catalinaLoader);
//3.使用catalinaLoader预加载一些类
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
//4.使用catalinaLoader加载Catalina并实例化,设置其父加载器为sharedLoader
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object 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");
Object paramValues[] = new Object[1];
paramValues[0] = sharedLoader;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
//5.设置catalinaDaemon
catalinaDaemon = startupInstance;
}
流程如下:
-
初始化 classLoader,主要设置了commonLoader(common)、catalinaLoader(server)、sharedLoader(shared)其中后两个classLoader一般用的都是commonLoader。
- 放置在common目录中:类库可被Tomcat和所有的Web应用程序共同使用。
- 放置在server目录中:类库可被Tomcat使用,但对所有的Web应用程序都不可见,这也就是为什么要使用catalinaLoader去加载Catalina类。
- 放置在shared目录中:类库可被所有的Web应用程序共同使用,但对Tomcat自己不可见。
- 放置在/webapp/WEB-INF目录中:类库仅仅可以被此Web应用程序使用,对Tomcat和其他Web应用程序都不可见。
-
设置当前线程的contexntClassLoader为catalinaLoader。
-
使用catalinaLoader预加载一些类,具体的可以看#securityClassLoad()方法。
-
使用catalinaLoader加载 org.apache.catalina.startup.Catalina 类并实例化,并设置 其父classloader为sharedLoader。
-
设置catalinaDaemon为Catalina对象。
3. #load()和#start()方法
if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
}
从上面方法中知道,最后会去调用BootStrap的load和start方法,下面看代码:
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;
}
//catalinaDaemon就是#init()方法中加载Catalina对象
Method method =
catalinaDaemon.getClass().getMethod(methodName, paramTypes);
if (log.isDebugEnabled())
log.debug("Calling startup class " + method);
method.invoke(catalinaDaemon, param);
}
public void start()
throws Exception {
if( catalinaDaemon==null ) init();
Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
method.invoke(catalinaDaemon, (Object [])null);
}
#load()和#start()方法最后分别调用了#Catalina.load()和#Catalina.start(),这里Bootstrap类启动完成,下面来看Catalina类。
总结
1. 为什么需要使用反射去调用Catalina对象?
从上面#init()方法后,Tomcat整个类加载器的结构如下:
我们知道Catalina是由catalinaLoader加载的,而Bootstrap是由AppClassLoader加载的,所以在Bootstrap中我们无法直接调用Catalina的方法。
网友评论