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

Tomcat学习笔记之启动分析(Catalina)(二)

作者: 夏目手札 | 来源:发表于2019-05-07 21:49 被阅读0次

    Catalina启动分析

    1. #load()方法:

    public void load() {
    
        if (loaded) {
            return;
        }
        loaded = true;
    
        long t1 = System.nanoTime();
        //1. 读取环境变量中java.io.tmpdir,没有打印错误日志
        initDirs();
    
        // Before digester - it may be needed
        initNaming();
    
        // Set configuration source
        ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));
        File file = configFile();
    
        // 2. 解析server.xml文件,并设置Catalina,Server对应属性值
        Digester digester = createStartDigester();
    
        try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
            InputStream inputStream = resource.getInputStream();
            InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
            inputSource.setByteStream(inputStream);
            digester.push(this);
            digester.parse(inputSource);
        } catch (Exception e) {
            log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
            if (file.exists() && !file.canRead()) {
                log.warn(sm.getString("catalina.incorrectPermissions"));
            }
            return;
        }
        //3. 设置Server
        getServer().setCatalina(this);
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
    
        //4. 设置输出
        initStreams();
    
        //5. 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(sm.getString("catalina.initError"), e);
            }
        }
    
        long t2 = System.nanoTime();
        if(log.isInfoEnabled()) {
            log.info(sm.getString("catalina.init", Long.valueOf((t2 - t1) / 1000000)));
        }
    }
    

    主要流程如下:

    • 解析conf/server.xml并创建Server对象,赋值Catalina,Server的相关属性。
    • 执行Server#init()初始化。

    2. start方法:

    public void start() {
        //1. Server为空重新load一次
        if (getServer() == null) {
            load();
        }
        //2. 如果Server还为空,直接返回
        if (getServer() == null) {
            log.fatal(sm.getString("catalina.noServer"));
            return;
        }
    
        long t1 = System.nanoTime();
    
        //3. 执行Server#start()
        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(sm.getString("catalina.startup", Long.valueOf((t2 - t1) / 1000000)));
        }
    
        //4. 注册shutdown钩子,用来执行Catalina#stop()
        if (useShutdownHook) {
            if (shutdownHook == null) {
                shutdownHook = new CatalinaShutdownHook();
            }
            Runtime.getRuntime().addShutdownHook(shutdownHook);
            LogManager logManager = LogManager.getLogManager();
            if (logManager instanceof ClassLoaderLogManager) {
                ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                        false);
            }
        }
        //5. #init()之后await为true,启动一个Socket等待接受shutdown命令,用来停止Tomcat
        if (await) {
            await();
            stop();
        }
    }
    

    主要流程如下:

    • 判断Server是否为空,为空重新执行一次#load()。
    • 执行Server#start(),启动Server。
    • 注册shutdown钩子,用来执行Catalina#stop()。
    • 启动一个Socket等待接受shutdown命令,用来停止Tomcat。

    3. #createStartDigester()方法:

     protected Digester createStartDigester() {
            long t1=System.currentTimeMillis();
            // Initialize the digester
            Digester digester = new Digester();
            // 设置为false表示解析xml时不需要进行DTD的规则校验
            digester.setValidating(false);
            // 是否进行节点设置规则校验,如果xml中相应节点没有设置解析规则会在控制台显示提示信息
            digester.setRulesValidation(true);
            Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
            // Ignore className on all elements
            List<String> objectAttrs = new ArrayList<>();
            objectAttrs.add("className");
            fakeAttributes.put(Object.class, objectAttrs);
            // Ignore attribute added by Eclipse for its internal tracking
            List<String> contextAttrs = new ArrayList<>();
            contextAttrs.add("source");
            fakeAttributes.put(StandardContext.class, contextAttrs);
            // Ignore Connector attribute used internally but set on Server
            List<String> connectorAttrs = new ArrayList<>();
            connectorAttrs.add("portOffset");
            fakeAttributes.put(Connector.class, connectorAttrs);
            digester.setFakeAttributes(fakeAttributes);
            digester.setUseContextClassLoader(true);
    
            // addObjectCreate方法的意思是碰到xml文件中的Server节点则创建一个StandardServer对象
            digester.addObjectCreate("Server",
                                     "org.apache.catalina.core.StandardServer",
                                     "className");
            // 根据Server节点中的属性信息调用相应属性的setter方法,以上面的xml文件为例则会调用setPort、setShutdown方法,入参分别是8005、SHUTDOWN
            digester.addSetProperties("Server");
            // 将Server节点对应的对象作为入参调用栈顶对象的setServer方法,这里的栈顶对象即下面的digester.push方法所设置的当前类的对象this,就是说调用StandardServer类的setServer方法
            digester.addSetNext("Server",
                                "setServer",
                                "org.apache.catalina.Server");
            
            digester.addObjectCreate("Server/GlobalNamingResources",
                                     "org.apache.catalina.deploy.NamingResourcesImpl");
            digester.addSetProperties("Server/GlobalNamingResources");
            digester.addSetNext("Server/GlobalNamingResources",
                                "setGlobalNamingResources",
                                "org.apache.catalina.deploy.NamingResourcesImpl");
            // 碰到xml的Server节点下的Listener节点时取className属性的值作为实例化类实例化一个对象
            digester.addRule("Server/Listener",
                    new ListenerCreateRule(null, "className"));
            digester.addSetProperties("Server/Listener");
            digester.addSetNext("Server/Listener",
                                "addLifecycleListener",
                                "org.apache.catalina.LifecycleListener");
    
            digester.addObjectCreate("Server/Service",
                                     "org.apache.catalina.core.StandardService",
                                     "className");
            digester.addSetProperties("Server/Service");
            digester.addSetNext("Server/Service",
                                "addService",
                                "org.apache.catalina.Service");
    
            //省略部分...
            return digester;
    
        }
    

    一般来说 Java 里解析 xml 文件有两种方式:一种是 Dom4J 之类将文件全部读取到内存中,在内存里构造一棵 Dom 树的方式来解析。一种是 SAX 的读取文件流,在流中碰到相应的xml节点触发相应的节点事件回调相应方法,基于事件的解析方式,优点是不需要先将文件全部读取到内存中。
    Digester 本身是采用 SAX 的解析方式,在其上提供了一层包装,对于使用者更简便友好罢了。最早是在 struts 1 里面用的,后来独立出来成为 apache 的 Commons 下面的一个单独的子项目。Tomcat 里又把它又封装了一层,这里不做多介绍。

    这里Catalina的启动分析结束,接下来看Server的启动。

    总结

    通过上面的介绍,我们了解到Catalina类主要完成了server.xml的解析以及相应属性的赋值,然后调用最顶层容器Server的#init()和#start()方法开始了整个Tomcat的加载之旅,下面一个一个来分析。

    相关文章

      网友评论

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

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