美文网首页
Tomcat学习笔记之启动分析(Bootstrap)(一)

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

作者: 夏目手札 | 来源:发表于2019-05-07 21:48 被阅读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);
    }

}

该方法分为两个部分:

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

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

参考地址

Tomcat源码解读:ClassLoader的设计

相关文章

网友评论

      本文标题:Tomcat学习笔记之启动分析(Bootstrap)(一)

      本文链接:https://www.haomeiwen.com/subject/yrapgqtx.html