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

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, List> fakeAttributes = new HashMap<>();
        // Ignore className on all elements
        List objectAttrs = new ArrayList<>();
        objectAttrs.add("className");
        fakeAttributes.put(Object.class, objectAttrs);
        // Ignore attribute added by Eclipse for its internal tracking
        List contextAttrs = new ArrayList<>();
        contextAttrs.add("source");
        fakeAttributes.put(StandardContext.class, contextAttrs);
        // Ignore Connector attribute used internally but set on Server
        List 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)(二))