Анализ исходного кода tomcat (подробное объяснение второго процесса запуска tomcat)

сервер Tomcat Bootstrap Начать бизнес

Как мы все знаем, каждое приложение имеет уникальную запись (то есть основную функцию), поэтому сервер tomcat, разработанный на Java, не является исключением. Найдите эту запись, поймите конкретный процесс загрузки каждого компонента и поймите реализацию всего приложения. , Процесс очень помогает. Классы, связанные с запуском tomcat, расположены в пути к пакету catalina.startup, а запись — это функция main() в классе Bootstrap. Начальный класс Bootstrap в основном завершает три аспекта, а именно:

① Установите два пути catalinaHome и catalinaBase в блоке статического кода;

②Инициализация трех загрузчиков классов: общего, серверного и общего;

③ Используйте механизм отражения для создания экземпляра класса org.apache.catalina.startup.Catalina.

1. Установите catalinaHome и catalinaBase

catalinaHome — это каталог установки tomcat, а catalinaBase — это рабочий каталог tomcat; основная функция этих двух каталогов заключается в том, что при развертывании нескольких экземпляров tomcat на одном компьютере вместо установки нескольких копий tomcat вы можете совместно использовать код tomcat , способ достижения. Например, при развертывании двух экземпляров tomcat на одном компьютере вам нужно всего лишь создать два базовых каталога, base1 и base2, а затем скопировать общий каталог в каталоге установки tomcat в эти два каталога. Изменяя номера портов в файле server.xml в каталоге conf, два сервера tomcat могут быть запущены одновременно.В это время каталоги bin и lib в каталоге установки tomcat (т. е. tomcatHome) являются общими.

Листинг кода (код для установки catalinaHome и catalinaBase в Bootstrap)
static {
        //获取tomcat的安装目录
        String userDir = System.getProperty("user.dir");
        String home = System.getProperty(Globals.CATALINA_HOME_PROP);
        File homeFile = null;
        if (home != null) {
            File f = new File(home);
            try {
                homeFile = f.getCanonicalFile();
            } catch (IOException ioe) {
                homeFile = f.getAbsoluteFile();
            }
        }

        if (homeFile == null) {
            //bootstrap.jar的根目录,其实就是tomcat按照路径下的bin文件夹
            File bootstrapJar = new File(userDir, "bootstrap.jar");
            if (bootstrapJar.exists()) {
                File f = new File(userDir, "..");
                try {
                    homeFile = f.getCanonicalFile();
                } catch (IOException ioe) {
                    homeFile = f.getAbsoluteFile();
                }
            }
        }

        if (homeFile == null) {
            // Second fall-back. Use current directory
            File f = new File(userDir);
            try {
                homeFile = f.getCanonicalFile();
            } catch (IOException ioe) {
                homeFile = f.getAbsoluteFile();
            }
        }

        catalinaHomeFile = homeFile;
        System.setProperty(
                Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());

        // Then base
        String base = System.getProperty(Globals.CATALINA_BASE_PROP);
        if (base == null) {
            catalinaBaseFile = catalinaHomeFile;
        } else {
            File baseFile = new File(base);
            try {
                baseFile = baseFile.getCanonicalFile();
            } catch (IOException ioe) {
                baseFile = baseFile.getAbsoluteFile();
            }
            catalinaBaseFile = baseFile;
        }
        System.setProperty(
                Globals.CATALINA_BASE_PROP, catalinaBaseFile.getPath());
    }

2. Инициализируйте загрузчики классов общие, серверные, общие

Существует три основных типа настраиваемых для tomcat загрузчиков классов: общие, серверные и совместно используемые, которые используются для загрузки классов и файлов jar в разных местах и ​​реализуют разделение и совместное использование файлов классов между различными веб-приложениями и системами tomcat. Основные проблемы, которые необходимо решить: библиотеки классов Java, используемые несколькими веб-приложениями, развернутыми на одном сервере tomcat, могут быть отделены друг от друга (разные версии одной и той же библиотеки классов); несколько веб-приложений, развернутых на одном сервере tomcat. используемая библиотека может использоваться совместно друг с другом; сервер гарантирует, что развернутое веб-приложение не повлияет на его собственную безопасность (системные классы tomcat могут быть загружены только через загрузчик классов сервера, а веб-приложение загружается WebAppClassLoader под общим загрузчик классов). ); реализует функцию HotSwap (путем замены загрузчика классов JsperLoader).

commonClassLoader

Определение commonClassLoader в catalina.properties:common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"Видно, что загружаемые библиотеки классов — это файлы в lib под каталогами catalina.base и catalina.home. common — это загрузчик классов, совместно используемый серверами и веб-приложениями, а также родительский загрузчик сервера и общего доступа. Реализация в Bootstrap выглядит следующим образом

private void initClassLoaders() {
        try {
            commonLoader = createClassLoader("common", null);
            if( commonLoader == null ) {
                commonLoader=this.getClass().getClassLoader();
            }
            catalinaLoader = createClassLoader("server", commonLoader);  //设置commonLoader为catalinaLoader和sharedLoader的父加载器。
            sharedLoader = createClassLoader("shared", commonLoader);
        } catch (Throwable t) {
            handleThrowable(t);
            log.error("Class loader creation threw exception", t);
            System.exit(1);
        }
    }

Продолжайте отслеживать исходный код createClassLoader следующим образом:

private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {
        String value = CatalinaProperties.getProperty(name + ".loader");
        if ((value == null) || (value.equals("")))
            return parent;
        value = replace(value);
        List<Repository> repositories = new ArrayList<>();
        String[] repositoryPaths = getPaths(value);
        for (String repository : repositoryPaths) {
            try {
                @SuppressWarnings("unused")
                URL url = new URL(repository);
                repositories.add(new Repository(repository, RepositoryType.URL));
                continue;
            } catch (MalformedURLException e) {
                // Ignore
            }
            if (repository.endsWith("*.jar")) {
                repository = repository.substring
                    (0, repository.length() - "*.jar".length());
                repositories.add(new Repository(repository, RepositoryType.GLOB));
            } else if (repository.endsWith(".jar")) {
                repositories.add(new Repository(repository, RepositoryType.JAR));
            } else {
                repositories.add(new Repository(repository, RepositoryType.DIR));
            }
        }

        return ClassLoaderFactory.createClassLoader(repositories, parent);
    }

catalinaClassLoader

Путь, заданный catalinaClassLoader в catalina.properties, пуст.server.loader=, поэтому путь загрузки его класса совпадает с его родительским загрузчиком commonClassLoader. После инициализации catalinaClassLoader установите его в качестве загрузчика классов текущего потока в методе init, а затем завершите загрузку класса rg.apache.catalina.startup.Catalina.

 Thread.currentThread().setContextClassLoader(catalinaLoader);
 SecurityClassLoad.securityClassLoad(catalinaLoader);
 Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");

sharedClassLoader

Путь, заданный catalinaClassLoader в catalina.properties, также пуст, поэтому путь загрузки его класса по умолчанию совпадает с его родительским загрузчиком commonClassLoader.

Создайте экземпляр класса org.apache.catalina.startup.Catalina, используя отражение

Класс Catalina играет важную роль в запуске tomcat.Как начальный класс жизненного цикла tomcat, основная ответственность Catalina заключается в анализе файла server.xml, завершении запуска и завершения работы таких компонентов, как сервер, служба, и Connector, примите команду tomcat stop и закройте сервер tomcat. Создайте экземпляр org.apache.catalina.startup.Catalina с помощью механизма отражения в Bootstrap, код выглядит следующим образом

    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    Object startupInstance = startupClass.getConstructor().newInstance();

В классе Catalina файл server.xml анализируется путем создания Digester и генерируются экземпляры сервера, службы, соединителя и контейнера Только два контейнера, Engine и Host, настраиваются в xml.

protected String configFile = "conf/server.xml";  //设置server.xml的路径
...
 protected Digester createStartDigester() {
        long t1=System.currentTimeMillis();
        Digester digester = new Digester();
        digester.setValidating(false);
        digester.setRulesValidation(true);
        Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
        List<String> attrs = new ArrayList<>();
        attrs.add("className");
        fakeAttributes.put(Object.class, attrs);
        digester.setFakeAttributes(fakeAttributes);
        digester.setUseContextClassLoader(true);
        digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");
    //省略一大段设置其他组件的代码,形式和StandardServer的生成一样,关于Digester类解析xml生成Java实例可参考《深入剖析tomcat》第15章。
}

Запись класса Catalina также является методом запуска.В Bootstrap метод запуска Catalina вызывается путем отражения.

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

    }

В методе start вызывается метод load для создания Digester и генерации каждого компонента; определение класса 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();      //获取到server.xml 文件并解析
                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 xml, just create it manually.
            if (inputStream == null) {
                try {
                    inputStream = getClass().getClassLoader()
                            .getResourceAsStream("server-embed.xml");
                    inputSource = new InputSource
                    (getClass().getClassLoader()
                            .getResource("server-embed.xml").toString());
                } catch (Exception e) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("catalina.configFail",
                                "server-embed.xml"), e);
                    }
                }
            }


            if (inputStream == null || inputSource == null) {
                if  (file == null) {
                    log.warn(sm.getString("catalina.configFail",
                            getConfigFile() + "] or [server-embed.xml]"));
                } 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);   //对server.xml数据流解析
                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);       //配置server,启动类为当前Catalina类,Home和base在Bootstrap中定义
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

        // Stream redirection
        initStreams();

        // 启动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");
        }
    }

Tomcat запускается путем реализации компонентов сервера, службы, соединителя и контейнера через файл конфигурации server.xml, поэтому, как реализованы эти компоненты, в следующих статьях будет продолжена работа над конкретной реализацией каждого компонента tomcat.

Анализ исходного кода Tomcat (первый анализ исходного кода Tomcat (первая статья начинается с общей архитектуры))
Анализ исходного кода Tomcat (анализ третьего принципа запроса Tomcat — анализ исходного кода соединителя)
Анализ исходного кода tomcat (Часть 4 анализа принципа обработки запросов tomcat — Анализ исходного кода контейнера)