Tomcat学习笔记之启动分析(Bootstrap)(一)

2019-05-07  本文已影响0人  夏目手札

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);
    }

}

该方法分为两个部分:

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;

    }

流程如下:

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整个类加载器的结构如下:

Tomcat类加载器结构
我们知道Catalina是由catalinaLoader加载的,而Bootstrap是由AppClassLoader加载的,所以在Bootstrap中我们无法直接调用Catalina的方法。

参考地址

Tomcat源码解读:ClassLoader的设计

上一篇 下一篇

猜你喜欢

热点阅读