Tomcat源码阅读之Server.xml文件的处理与Catalina启动流程

本文介绍了Tomcat启动过程中Catalina的角色,包括解析Server.xml,创建并初始化对象,如Server, Service, Connector和Container。重点探讨了Catalina的load方法和 Digester对象的使用,以及XML配置文件处理规则。理解这些有助于深入理解Tomcat服务器的结构和启动机制。" 128158267,1192969,理解FAT12文件系统,"['文件系统', '磁盘管理', 'FAT', '操作系统']
摘要由CSDN通过智能技术生成

上一篇文章初步看了一下Tomcat启动的入口,而且粗略了解了Tomcat的classLoader的设计。。。。

其实对于启动过程,Bootstrap对象只能算是一个入口吧,例如它初始化了一些目录,创建了最顶层的3个classLoader,然后对于接下来的启动过程就交给了Catalina对象来搞了。。。

另外,在Bootstrap对象中,将当前的线程classLoader设置为了Catalina(也就是专属于Tomcat服务器的classLoader),然后调用了创建的Catalina对象的setParentClassLoader方法,将sharedLoader传了进去,这里也就是属于web应用程序那条线的classLoader,。。。。。把这部分记住就可以开始下面的内容了。。。。


接下来就可以开始说Catalina对象的工作了。。。简单概括一下它的工作:

(1)解析配置的xml文件

(2)根据配置文件创建对象,例如Server,service,connector,container啥的。。

(3)初始化上面创建的对象

(4)启动上面创建的对象。。。

这四部完成,整个tomcat服务器就算是启动了。。。。


其实这重要的事情就是对配置xml进行解析处理。。。。

这里先来看看Catalina的start方法吧:

    //创建一个tomcatServer的实例
    public void start() {

        if (getServer() == null) {
            load();   //加载server,这里其实主要是解析xml文件,读取里面的元素定义,用于生成server对象,并且初始化server对象
        }

        if (getServer() == null) {
            log.fatal("Cannot start server. Server instance is not configured.");
            return;
        }

        long t1 = System.nanoTime();

        // Start the new server
        try {
            getServer().start();  //调用server的start,最终启动tomcat服务器,其实server要做的是启动里面的service
        } 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();
        }
    }

这里先是调用load方法,用于解析xml文件,创建相应的对象,然后在调用server的start方法来真正的启动tomcat服务器,。。那么这里就主要来看看这个load方法做了什么事情吧。。。。

    public void load() {

        long t1 = System.nanoTime();

        initDirs();  //初始化一些目录参数

        // Before digester - it may be needed

        initNaming();

        // Create and execute our Digester
        Digester digester = createStartDigester();  //创建处理xml文件的对象,并会生成相应的处理规则,例如如何创建server对象

        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
            file = configFile();   //获取server.xml
            inputStream = new FileInputStream(file);   //读取server.xml
            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);
            digester.push(this);   //这里先将当前对象放到Digester的栈底,但会server生成之后会调用当前对象的setServer方法来保存起来
            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);  //设置sever的catalina对象
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());  //设置server的目录
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

        // Stream redirection
        initStreams();

        // Start the new server
        try {
            getServer().init();   //初始化创建的server对象,其实这里主要是初始化service
        } 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");
        }

    }

代码虽然很多,但是这里只要关注最核心的代码就好了。。首先初始化了一些目录的参数。。。。然后创建了Digester对象,这个对象是用于解析配置文件的关键对象。。。接着获取server.xml文件的输入流,然后利用刚刚创建的Digester对象进行解析。。。。。

那么这里关键就是要搞定Digester对象的运行原理了。。。。先来看看createStartDigester方法都干了什么事情吧。。。

   protected Digester createStartDigester() {
        long t1=System.currentTimeMillis();
        // Initialize the digester
        Digester digester = new Digester();   //这个对象用于解析处理xml文件
        digester.setValidating(false);
        digester.setRulesValidation(true);
        HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<>();
        ArrayList<String> attrs = new ArrayList<>();
        attrs.add("className");
        fakeAttributes.put(Object.class, attrs);
        digester.setFakeAttributes(fakeAttributes);
        digester.setUseContextClassLoader(true);

        // Configure the actions we will be using
        digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");   //创建一个创建server对象的规则
        digester.addSetProperties("Server");  //创建一个设置属性的规则,那么创建完了server对象之后就会设置配置文件后面的属性
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");   //当server元素执行完了之后,执行的操作。。。这里其实是调用前面那个对象的方法

        digester.addObjectCreate("Server/GlobalNamingResources",
                                 "org.apache.catalina.deploy.NamingResourcesImpl");  //创建全局名字资源
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources",
                            "setGlobalNamingResources",
                            "org.apache.catalina.deploy.NamingResourcesImpl");   //其实这里实在server对象上面设置resource

        digester.addObjectCreate("Server/Listener",
                                 null, // MUST be specified in the element
                                 "className");   //根据xml文件配置来创建listener
        digester.addSetProperties("Server/Listener");  //设置listener对象配置的属性
        digester.addSetNext("Server/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");  //这里其实是在server对象上面添加lifecyclelistener

        digester.addObjectCreate("Server/Service",
                                 "org.apache.catalina.core.StandardService",
                                 "className");    //创建service对象
        digester.addSetProperties("Server/Service");  //设置service的属性
        digester.addSetNext("Server/Service",
                            "addService",
                            "org.apache.catalina.Service");   //在server上面添加调用addService方法

        //为service创建listener,不一定有
        digester.addObjectCreate("Server/Service/Listener",
                                 null, // MUST be specified in the element
                                 "className");    //根据配置文件参数来创建service的listener
        digester.addSetProperties("Server/Service/Listener");
        digester.addSetNext("Server/Service/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        //Executor
        //创建service的executor,这个不一定有
        digester.addObjectCreate("Server/Service/Executor",
                         "org.apache.catalina.core.StandardThreadExecutor",
                         "className");
        digester.addSetProperties("Server/Service/Executor");

        digester.addSetNext("Server/Service/Executor",
                            "addExecutor",
                            "org.apache.catalina.Executor");

        //为servie添加connector规则
        digester.addRule("Server/Service/Connector",
                         new ConnectorCreateRule());    //这个规则会创建connector,而且如果有属性执行executor的话,还会设置connector的executor
        digester.addRule("Server/Service/Connector",  //<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/>
                         new SetAllPropertiesRule(new String[]{"executor"}));   //这里会将所有的属性都设置,除了executor
        digester.addSetNext("Server/Service/Connector",
                            "addConnector",
                            "org.apache.catalina.connector.Connector");  //在service上面添加这个connector


        //这里是设置connector的listener
        digester.addObjectCreate("Server/Service/Connector/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties("Server/Service/Connector/Listener");
        digester.addSetNext("Server/Service/Connector/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");

        // Add RuleSets for nested elements
        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));   //engine元素的定义的处理,这里主要是创建eingie
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));  //engine里面host的定义
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));  //一些context的配置处理
        addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));

        // When the 'engine' is found, set the parentClassLoader.
        digester.addRule("Server/Service/Engine",
                         new SetParentClassLoaderRule(parentClassLoader));  //如果有发现engine,那么设置container(Engine对象)的parentClassloader,shareloader
        addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");

        long t2=System.currentTimeMillis();
        if (log.isDebugEnabled()) {
            log.debug("Digester for server.xml created " + ( t2-t1 ));
        }        
        return (digester);

    }

Tomcat对xml配置文件的处理感觉还挺麻烦的,相对于jetty来说,jetty是将整个配置文件看成了类似于一个脚本来解释处理,但是Tomcat则是为每一个xml元素设置相应的处理规则。。。这里就举其中一个规则来说吧:

        digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");   //创建一个创建server对象的规则
        digester.addSetProperties("Server");  //创建一个设置属性的规则,那么创建完了server对象之后就会设置配置文件后面的属性
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");   //当server元素执行完了之后,执行的操作。。。这里其实是调用前面那个对象的方法

这个意思就是说遇到Server这个元素的的时候创建了3个规则,第一个规则是创建server对象的规则,这里

org.apache.catalina.core.StandardServer

参数表示要创建的server的类型,然后还有一个参数是”className“,待会在处理的时候,会看Server元素是否在属性域设置了className,如果有的话,那么将会使用指定的类型来创建Server对象,而不是上面那个默认的类型。。。

接着创建了一个规则,这个规则用于设置创建的Server对象的属性,也就是根据XML文件的Server元素后面的属性来相应的设置当前的Server对象的属性。。。。

然后还创建了一个SetNext规则,这个规则比较有意思。。。。它表示在整个Server这个XML元素处理完了之后要做的动作,调用setServer方法,那么调用的是谁的setServer方法呢。。?其实调用的当前这个Catalina对象的SetServer方法将已经创建的Server对象保存起来。。。


这里也就是简单的介绍了一下规则的作用,其实Digester的工作原理还是挺简单的,这里就不细说了。。。可以去看一下代码,很简单就能够明白。。。。基本上XML文件解析处理要用到的所有对象都在digester包中。。。。。


这部分搞明白了。。。就基本上能够搞清楚tomcat的server.xml的每一个配置项都要做什么事情了。。。


那么当Server对象创建完成之后就可以进行接下来的初始化和启动过程了。。。。这部分的内容以后再说吧。。。

通过阅读的源码,可以将Tomcat服务器的结构大概用下图来描述:



一个服务器可能会定义多个server对象,每个server对象对应多个service对象,而每个service对象对应多个connector对象,每个service对象包含一个container对象。。。


大概的层次关系如上图。。。


评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值