• Tomcat源代码阅读#1:classloader初始化


    Bootstrap

    通过Tomcat的启动脚本能够看到启动的入口是在Bootstrap,来看下Bootstrapmain方法,

        /**
         * Main method and entry point when starting Tomcat via the provided
         * scripts.
         *
         * @param args Command line arguments to be processed
         */
        public static void main(String args[]) {
    
            if (daemon == null) {
                ////////////////////////////////////////
                // 1. Bootstrap初始化
                ////////////////////////////////////////
                // 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);
                    ////////////////////////////////////////
                    // 2. org.apache.catalina.startup.Catalina#load
                    ////////////////////////////////////////
                    daemon.load(args);
                    ////////////////////////////////////////
                    // 3. org.apache.catalina.startup.Catalina#start
                    ////////////////////////////////////////
                    daemon.start();
                } 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);
            }
    
        }

    来看下init方法,

        /**
         * Initialize daemon.
         */
        public void init()
            throws Exception
        {
            ////////////////////////////////////////
            // 1. 设置catalina.home与catalina.base
            ////////////////////////////////////////
            // Set Catalina path
            setCatalinaHome();
            setCatalinaBase();
    
            ////////////////////////////////////////
            // 2. classloader初始化
            ////////////////////////////////////////
            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");
            ////////////////////////////////////////
            // 3. 使用catalinaLoader载入Catalina
            ////////////////////////////////////////
            Class<?> startupClass =
                catalinaLoader.loadClass
                ("org.apache.catalina.startup.Catalina");
            Object startupInstance = startupClass.newInstance();
    
            // Set the shared extensions class loader
            if (log.isDebugEnabled())
                log.debug("Setting startup class properties");
            ////////////////////////////////////////
            // 4. 将Catalina的parentClassLoader设置为sharedLoader
            ////////////////////////////////////////
            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;
    
        }

    能够看到须要先初始化ClassLoader。然后才去载入最重要的服务器类。也就是org.apache.catalina.startup.Catalina。有三个ClassLoader,

        protected ClassLoader commonLoader = null;
        protected ClassLoader catalinaLoader = null;
        protected ClassLoader sharedLoader = null;

    以下来看下ClassLoader初始化的代码,

        private void initClassLoaders() {
            try {
                ////////////////////////////////////////
                // commonLoader的parent置为null
                ////////////////////////////////////////
                commonLoader = createClassLoader("common", null);
                ////////////////////////////////////////
                // 假设没有配置common.loader,则commonLoader就是AppClassLoader
                ////////////////////////////////////////
                if( commonLoader == null ) {
                    // no config file, default to this loader - we might be in a 'single' env.
                    commonLoader=this.getClass().getClassLoader();
                }
                catalinaLoader = createClassLoader("server", commonLoader);
                sharedLoader = createClassLoader("shared", commonLoader);
            } catch (Throwable t) {
                handleThrowable(t);
                log.error("Class loader creation threw exception", t);
                System.exit(1);
            }
        }
        private ClassLoader createClassLoader(String name, ClassLoader parent)
            throws Exception {
            ////////////////////////////////////////
            // 从catalina.base/conf/catalina.properties读取配置
            ////////////////////////////////////////
            String value = CatalinaProperties.getProperty(name + ".loader");
    
            ////////////////////////////////////////
            // 默认情况下catalinaLoader与sharedLoader配置为空
            // 所以三个ClassLoader事实上是同一个引用
            ////////////////////////////////////////
            if ((value == null) || (value.equals("")))
                return parent;
    
            value = replace(value);
    
            ////////////////////////////////////////
            // 加入配置的classpath
            ////////////////////////////////////////
            List<Repository> repositories = new ArrayList<Repository>();
    
            StringTokenizer tokenizer = new StringTokenizer(value, ",");
            while (tokenizer.hasMoreElements()) {
                String repository = tokenizer.nextToken().trim();
                if (repository.length() == 0) {
                    continue;
                }
    
                // Check for a JAR URL repository
                try {
                    @SuppressWarnings("unused")
                    URL url = new URL(repository);
                    repositories.add(
                            new Repository(repository, RepositoryType.URL));
                    continue;
                } catch (MalformedURLException e) {
                    // Ignore
                }
    
                // Local repository
                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));
                }
            }
    
            ////////////////////////////////////////
            // 差点儿相同是new URLClassLoader
            ////////////////////////////////////////
            ClassLoader classLoader = ClassLoaderFactory.createClassLoader
                (repositories, parent);
    
            // Retrieving MBean server
            MBeanServer mBeanServer = null;
            if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
                mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0);
            } else {
                mBeanServer = ManagementFactory.getPlatformMBeanServer();
            }
    
            // Register the server classloader
            ObjectName objectName =
                new ObjectName("Catalina:type=ServerClassLoader,name=" + name);
            mBeanServer.registerMBean(classLoader, objectName);
    
            return classLoader;
    
        }

    ClassLoader的配置放在catalina.properties

    #
    #
    # List of comma-separated paths defining the contents of the "common"
    # classloader. Prefixes should be used to define what is the repository type.
    # Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
    # If left as blank,the JVM system loader will be used as Catalina's "common"
    # loader.
    # Examples:
    #     "foo": Add this folder as a class repository
    #     "foo/*.jar": Add all the JARs of the specified folder as class
    #                  repositories
    #     "foo/bar.jar": Add bar.jar as a class repository
    common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
    
    #
    # List of comma-separated paths defining the contents of the "server"
    # classloader. Prefixes should be used to define what is the repository type.
    # Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
    # If left as blank, the "common" loader will be used as Catalina's "server"
    # loader.
    # Examples:
    #     "foo": Add this folder as a class repository
    #     "foo/*.jar": Add all the JARs of the specified folder as class
    #                  repositories
    #     "foo/bar.jar": Add bar.jar as a class repository
    server.loader=
    
    #
    # List of comma-separated paths defining the contents of the "shared"
    # classloader. Prefixes should be used to define what is the repository type.
    # Path may be relative to the CATALINA_BASE path or absolute. If left as blank,
    # the "common" loader will be used as Catalina's "shared" loader.
    # Examples:
    #     "foo": Add this folder as a class repository
    #     "foo/*.jar": Add all the JARs of the specified folder as class
    #                  repositories
    #     "foo/bar.jar": Add bar.jar as a class repository
    # Please note that for single jars, e.g. bar.jar, you need the URL form
    # starting with file:.
    shared.loader=

    能够看到仅仅有commonLoader才有配置,所以createClassLoader("server", commonLoader);createClassLoader("shared", commonLoader);返回的都会是commonLoader

    另一点值得注意的是,commonLoaderparentnull,也就是说当commonLoader进行类载入要托付给父类载入器时。将会绕过AppClassLoaderExtClassLoader,直接交给BootstrapClassLoader(不明确的同学能够參考这里)。

    Catalina

    使用catalinaLoader载入了Catalina之后。会调用Catalina#load方法,

        public void load() {
    
            long t1 = System.nanoTime();
    
            ////////////////////////////////////////
            // 设置catalina.home与catalina.base
            ////////////////////////////////////////
            initDirs();
    
            // Before digester - it may be needed
            initNaming();
    
            ////////////////////////////////////////
            // 创建server.xml的解析器
            ////////////////////////////////////////
            // Create and execute our Digester
            Digester digester = createStartDigester();
    
            ////////////////////////////////////////
            // 读取catalina.base/conf/server.xml
            ////////////////////////////////////////
    
            InputSource inputSource = null;
            InputStream inputStream = null;
            File file = null;
            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 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;
            }
    
            ////////////////////////////////////////
            // 解析server.xml
            ////////////////////////////////////////
            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 {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
    
            getServer().setCatalina(this);
    
            // Stream redirection
            initStreams();
    
            ////////////////////////////////////////
            // org.apache.catalina.Server初始化
            ////////////////////////////////////////
            // 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");
            }
    
        }

    load方法做的事情就是通过server.xml的配置去初始化Server。随后的Catalina#start事实上就是去启动Server

        public void start() {
    
            if (getServer() == null) {
                load();
            }
    
            if (getServer() == null) {
                log.fatal("Cannot start server. Server instance is not configured.");
                return;
            }
    
            long t1 = System.nanoTime();
    
            ////////////////////////////////////////
            // 启动org.apache.catalina.Server
            ////////////////////////////////////////
            // Start the new server
            try {
                getServer().start();
            } catch (LifecycleException e) {
                log.fatal(sm.getString("catalina.serverStartFail"), e);
                try {
                    getServer().destroy();
                } catch (LifecycleException e1) {
                    log.debug("destroy() failed for failed Server ", e1);
                }
                return;
            }
    
            long t2 = System.nanoTime();
            if(log.isInfoEnabled()) {
                log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
            }
    
            // Register shutdown hook
            if (useShutdownHook) {
                if (shutdownHook == null) {
                    shutdownHook = new CatalinaShutdownHook();
                }
                Runtime.getRuntime().addShutdownHook(shutdownHook);
    
                // If JULI is being used, disable JULI's shutdown hook since
                // shutdown hooks run in parallel and log messages may be lost
                // if JULI's hook completes before the CatalinaShutdownHook()
                LogManager logManager = LogManager.getLogManager();
                if (logManager instanceof ClassLoaderLogManager) {
                    ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                            false);
                }
            }
    
            if (await) {
                await();
                stop();
            }
        }

    alright,今天就先到这吧。

    參考资料

  • 相关阅读:
    MFC9.0 Outlook控件的标题显示无法修改
    VS2010 单文档+多视图+Outlook风格
    在VS2010中使用Outlook工具栏
    在MFC中添加用户自定义消息
    读书笔记——Windows环境下32位汇编语言程序设计(13)关于EXCEPTION_DEBUG_INFO结构体
    读书笔记——Windows环境下32位汇编语言程序设计(9)ANSII字符大小写转大写
    读书笔记——Windows环境下32位汇编语言程序设计(6)使用浮点指令进行64位除法
    读书笔记——Windows环境下32位汇编语言程序设计(5)模态对话框
    读书笔记——Windows环境下32位汇编语言程序设计(3)一些基础知识
    设置自己的RadASM颜色
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/7098612.html
Copyright © 2020-2023  润新知