了解过tomcat的组成,tomcat的类加载器结构,接下来了解一下tomcat的启动过程。

Bootstrap 的 main 方法

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

    }

main 方法可以分解为两部分:

  1. 实例化一个 Bootstrap 对象,并调用 init 方法,然后赋值给 daemon 变量.

  2. 根据传递进来的参数决定走哪一个命令,例如 : 当双击 startup.bat 时,传进来的是 start

本文主要分析 Bootstrap 类的 init, setAwait ,load 以及 start 方法。

init 方法

public void init() throws Exception {

        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\");
          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\");
          paramValues[] = new  [1];
        paramValues[0] = sharedLoader;
        Method method =
            startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);

        catalinaDaemon = startupInstance;

    }

init 方法中首先调用initClassLoaders方法初始化Comm erSharedLoaderCatalinaLoader,然后将初始化好的catalinaLoader设置到当前线程,接着通过反射初始化org.apache.catalina.startup.Catalina类生成对象startupInstance ,并调用setParentClassLoader,将sharedLoader设置为父加载器。

为什么要通过反射调用初始化 Catalina 类?
这样做是为了解耦,Bootstrap 直接依赖JRE运行并为Tomcat应用服务器创建共享类加载器,用于构造Catalina和整个tomcat服务器,而tomcat的主要业务代码在Catalina中,实现了启动入口和核心环境的解耦。
通过观察tomcat编译好的包,我们也可以发现bootstrap在bin目录下,而catalina在lib目录下。

有关initClassLoaders在类加载器篇章中介绍过,这里不再分析。
SecurityClassLoad.securityClassLoad(catalinaLoader)用于线程安全的加载tomcat容器所需的class, 只有当以安全模式启动 tomcat 的时候才会起作用具体实现代码如下:

public static void securityClassLoad(ClassLoader loader) throws Exception {
        securityClassLoad(loader, true);
    }


    static void securityClassLoad(ClassLoader loader, boolean requireSecurityManager)
            throws Exception {

        if (requireSecurityManager && System.getSecurityManager() == null) {
            return;
        }

        loadCorePackage(loader);
        loadCoyotePackage(loader);
        loadLoaderPackage(loader);
        loadRealmPackage(loader);
        loadServletsPackage(loader);
        loadSessionPackage(loader);
        loadUtilPackage(loader);
        loadJavaxPackage(loader);
        loadConnectorPackage(loader);
        loadTomcatPackage(loader);
    }

setAwait 方法

public void setAwait(boolean await)
        throws Exception {

        Class<?> paramTypes[] = new Class[1];
        paramTypes[0] = Boolean.TYPE;
          paramValues[] = new  [1];
        paramValues[0] = Boolean.valueOf(await);
        Method method =
            catalinaDaemon.getClass().getMethod(\"setAwait\", paramTypes);
        method.invoke(catalinaDaemon, paramValues);

    }

setAwait 方法通过反射调用了catalina的setAwait方法,该方法设置的await值是留给后面用的标志位,当Catalina将Tomcat的所有组件启动之后,会检查await属性,如果为true,会调用Catalina.await():

public void start() {
......
        if (await) {
            await();
            stop();
        }
    }

而Catalina.await()又会调用其StandardServer的await 方法:

 public void await() {

        getServer().await();

    }

StandardServer.await()方法包含一个 while 循环,此循环用于监听指定 socket 端口 (默认为 8005) 的连接,当某个连接传入的参数为”SHUTDOWN”(默认为”SHUTDOWN”)时,终止此while循环(端口号和终止while循环的参数,在server. 的Server标签设置)。

我们都知道在 JVM 中, 当不存在任何一个非守护线程的线程时,jvm也就会停止。

Server.await()用来维持Bootstrap的main方法(main thread)处于运行状态,而线程池中监听http请求的线程是守护线程(daemon thread)。

当Tomcat的指定端口接收到关闭命令时,Server.await()内的while循环终止,然后Catalina会调用stop()方法,关闭Tomcat的所有组件,最终Bootstrap的main thread终止,Tomcat关闭。

load 方法

private void load(String[] arguments)
        throws Exception {

        // Call the load() method
        String methodName = \"load\";
          param[];
        Class<?> paramTypes[];
        if (arguments==null || arguments.length==0) {
            paramTypes = null;
            param = null;
        } else {
            paramTypes = new Class[1];
            paramTypes[0] = arguments.getClass();
            param = new  [1];
            param[0] = arguments;
        }
        Method method =
            catalinaDaemon.getClass().getMethod(methodName, paramTypes);
        if (log.isDebugEnabled())
            log.debug(\"Calling startup class \" + method);
        method.invoke(catalinaDaemon, param);

    }

该方法通过反射调用catalina的load方法。
catalina 的 load 方法如下:

public void load() {

        if (loaded) {
            return;
        }
        loaded = true;

        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 {
            try {
                file = configFile();
                inputStream = new FileInputStream(file);
                inputSource = new InputSource(file.toURI().toURL().toString());
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString(\"catalina.configFail\", file), e);
                }
            }
            if (inputStream == null) {
                try {
                    inputStream = getClass().getClassLoader()
                        .getResourceAsStream(getConfigFile());
                    inputSource = new InputSource
                        (getClass().getClassLoader()
                         .getResource(getConfigFile()).toString());
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString(\"catalina.configFail\",
                                getConfigFile()), e);
                    }
                }
            }

            // This should be included in catalina.jar
            // Alternative: don\'t bother with  , just create it manually.
            if (inputStream == null) {
                try {
                    inputStream = getClass().getClassLoader()
                            .getResourceAsStream(\"server- . \");
                    inputSource = new InputSource
                    (getClass().getClassLoader()
                            .getResource(\"server- . \").toString());
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString(\"catalina.configFail\",
                                \"server- . \"), e);
                    }
                }
            }


            if (inputStream == null || inputSource == null) {
                if  (file == null) {
                    log.warn(sm.getString(\"catalina.configFail\",
                            getConfigFile() + \"] or [server- . ]\"));
                } else {
                    log.warn(sm.getString(\"catalina.configFail\",
                            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);
            } catch (SAXParseException spe) {
                log.warn(\"Catalina.start using \" + getConfigFile() + \": \" +
                        spe.getMessage());
                return;
            } catch (Exception e) {
                log.warn(\"Catalina.start using \" + getConfigFile() + \": \" , e);
                return;
            }
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }

        getServer().setCatalina(this);
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalina (Bootstrap.getCatalina File());

        // Stream redirection
        initStreams();

        // Start the new server
        try {
            getServer().init();
        } 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),然后是createStartDigester,为 的标签即解析模式增加处理规则rule:

protected Digester createStartDigester() {
        long t1=System.currentTimeMillis();
        // Initialize the digester
        Digester digester = new Digester();
        digester.setValidating(false);
        digester.setRulesValidation(true);
        Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
        // Ignore className on all elements
        List<String>  Attrs = new ArrayList<>()

					
				
收藏 打印
您的足迹: