美文网首页技术点滴
Tomcat源码学习(一)——Bootstarp入口类

Tomcat源码学习(一)——Bootstarp入口类

作者: 笨小天 | 来源:发表于2018-10-25 10:14 被阅读0次

    Tomcat作为平时工作中出镜率最高的web容器,今天我们就来对其源码一探究竟。通过github获取了tomcat的最新代码,获取地址如下

    https://github.com/apache/tomcat
    这里使用idea的git直接从github上拉去了最新源码,同时方便获取最新修改。

    获得源码后,首先来探索一下Tomcat的启动过程,那就要来看看他的启动入口类org.apache.catalina.startup.BootStrap。
    找到了入口类,从main方法开始入手,代码如下:

    public static void main(String args[]) {
            // 同步初始化Bootstrap对象,不太理解这里为什么要加同步锁
            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);
            }
    
        }
    

    上述是tomcat的main方法,如果我只看tomcat的start过程,代码可以简化为:

       if (daemon == null) {
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.init();
        daemon = bootstrap;
      }
      String command = "start";
      if (command.equals("start")) {
        daemon.setAwait(true);
        daemon.load(args);
        daemon.start();
        if (null == daemon.getServer()) {
          System.exit(1);
        }
      }
    

    从上面代码就可以清晰的看出来tomcat 启动流程,首先创建bootstrap对象,并执行init方法初始化bootstrap,初始化完成后将新创建的bootstrap实例赋值给daemon对象,在启动时,daemon会先调用Bootstrap的load方法和start方法来完成Tomcat的启动工作。

    从main方法中可以发现启动过程中主要的三个方法init,load和start。依次来看一下这三个方法的实现,来了解一下tomcat启动具体做了些什么。

    public void init() throws Exception {
    
            initClassLoaders(); //类加载器初始化
    
            Thread.currentThread().setContextClassLoader(catalinaLoader);
    
            SecurityClassLoad.securityClassLoad(catalinaLoader);
    
          
            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);
    
            catalinaDaemon = startupInstance;
    
        }
    

    可以看到init方法是设置了类加载器,同时通过类加载器,生成了一个Catalina对象实例,同时设置了catalina对象的父类加载器。并将生成的catalina对象赋值给了bootstrap对象的catalinaDaemon属性。上述操作,总的来看init方法做了一件事情,就是给bootstarp的catalinaDanmon属性进行了初始化。
    再来看看load方法做了什么事情,核心代码如下:

    
            Method method =
                catalinaDaemon.getClass().getMethod(methodName, paramTypes);
            if (log.isDebugEnabled())
                log.debug("Calling startup class " + method);
            method.invoke(catalinaDaemon, param);
    
        
    

    可以看到load方法其实是通过反射的方法调动了catalina的load方法。进行基础信息的加载。catalina的load将在后续文章中详细介绍。

      if( catalinaDaemon==null ) init();
    
            Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
            method.invoke(catalinaDaemon, (Object [])null);
    

    start方法也很简单,依然是通过反射的方法调用了catalina的start 方法。

    翻看了其他的Bootstarp类方法,大部分都是通过反射去调用了对应的Catalina类的对应方法来完成对应操作。

    可以看到Bootstrap类如其名,作为tomcat的入口类,同时起到了一个引导的作用,通过阅读和分析,也引出了tomcat启动过程中一个重要的类Catalina。

    对于源码中同步疑问,在万能的stackoverflow提问后,理解了为什么要对daemon的初始化过程进行同步操作
    传送门

    相关文章

      网友评论

        本文标题:Tomcat源码学习(一)——Bootstarp入口类

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