Tomcat源码分析【五】启动过程分析之配置文件解析与组件注入

文章首发于:clawhub.club


在执行catalina的load方法时,会执行配置Digester、读取配置文件、将Catalina作为digester的顶级容器、digester解析配置文件并注入各个组件。
简略修改了一下代码:

// Create and execute our Digester
//创建和配置将用于启动的Digester。
//配置解析server.xml中各个标签的解析类
Digester digester = createStartDigester();
        
//下面一大段都是为了conf/server.xml配置文件,失败就加载server-embed.xml
File file  = configFile();
InputSource inputStream = new FileInputStream(file);
InputSource inputSource = new InputSource(file.toURI().toURL().toString());
inputSource.setByteStream(inputStream);

//把Catalina作为一个顶级容器
digester.push(this);
//解析过程会实例化各个组件,比如Server、Container、Connector等
digester.parse(inputSource);

Digeter是apache的common项目,作用是将XML转化成对象,使用者直接从对象中获取xml的节点信息。
Digester是对SAX的包装,它也是基于文件流来解析xml文件,只不过这些解析操作对用户是透明的。
在分析createStartDigester之前很有必要贴一下server.xml配置:



  
  
  
  
  
  
    
  
  
    
    
    
      
        
      
      
        
      
    
  


这个是默认配置,我将注释之类去除了。

createStartDigester

/**
     * 创建和配置将用于启动的Digester。
     *
     * @return the main digester to parse server.xml
     */
    protected Digester createStartDigester() {
        long t1 = System.currentTimeMillis();
        // Initialize the digester
        Digester digester = new Digester();
        //设置验证解析器标志。
        digester.setValidating(false);
        //设置规则验证标志。
        digester.setRulesValidation(true);
        //假的属性
        Map, List> fakeAttributes = new HashMap<>();
        List attrs = new ArrayList<>();
        attrs.add("className");
        fakeAttributes.put(Object.class, attrs);
        //设置假属性。
        digester.setFakeAttributes(fakeAttributes);
        //确定是否使用上下文类加载器来解析/加载在各种规则中定义的类。
        digester.setUseContextClassLoader(true);

        // 配置我们将使用的操作
        //addObjectCreate:为指定的参数添加“对象创建”规则。
        digester.addObjectCreate("Server",
                "org.apache.catalina.core.StandardServer",
                "className");
        //为指定的参数添加“设置属性”规则。
        digester.addSetProperties("Server");
        //为指定的参数添加“set next”规则。
        digester.addSetNext("Server",
                "setServer",
                "org.apache.catalina.Server");

        // StandardServer.GlobalNamingResources
        digester.addObjectCreate("Server/GlobalNamingResources",
                "org.apache.catalina.deploy.NamingResourcesImpl");
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources",
                "setGlobalNamingResources",
                "org.apache.catalina.deploy.NamingResourcesImpl");

        //StandardServer.addLifecycleListener
        digester.addObjectCreate("Server/Listener",
                null, // MUST be specified in the element
                "className");
        digester.addSetProperties("Server/Listener");
        digester.addSetNext("Server/Listener",
                "addLifecycleListener",
                "org.apache.catalina.LifecycleListener");

        //StandardServer.addService:StandardService
        digester.addObjectCreate("Server/Service",
                "org.apache.catalina.core.StandardService",
                "className");
        digester.addSetProperties("Server/Service");
        digester.addSetNext("Server/Service",
                "addService",
                "org.apache.catalina.Service");

        //StandardService.addLifecycleListener
        digester.addObjectCreate("Server/Service/Listener",
                null, // MUST be specified in the element
                "className");
        digester.addSetProperties("Server/Service/Listener");
        digester.addSetNext("Server/Service/Listener",
                "addLifecycleListener",
                "org.apache.catalina.LifecycleListener");

        //Executor
        //StandardService.addExecutor
        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");


        //StandardService.addConnector
        digester.addRule("Server/Service/Connector",
                new ConnectorCreateRule());
        digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(
                new String[]{"executor", "sslImplementationName", "protocol"}));
        digester.addSetNext("Server/Service/Connector",
                "addConnector",
                "org.apache.catalina.connector.Connector");

        //Connector.addSslHostConfig
        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",
                "org.apache.tomcat.util.net.SSLHostConfig");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig",
                "addSslHostConfig",
                "org.apache.tomcat.util.net.SSLHostConfig");

        //SSLHostConfig.addCertificate
        digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                new CertificateCreateRule());
        digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
                new SetAllPropertiesRule(new String[]{"type"}));
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
                "addCertificate",
                "org.apache.tomcat.util.net.SSLHostConfigCertificate");

        //SSLHostConfig.setOpenSslConf
        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                "org.apache.tomcat.util.net.openssl.OpenSSLConf");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
                "setOpenSslConf",
                "org.apache.tomcat.util.net.openssl.OpenSSLConf");

        //OpenSSLConfCmd.addCmd
        digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
        digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd");
        digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
                "addCmd",
                "org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");

        //Connector.addLifecycleListener
        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");

        //Connector.addUpgradeProtocol
        digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
                null, // MUST be specified in the element
                "className");
        digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
        digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
                "addUpgradeProtocol",
                "org.apache.coyote.UpgradeProtocol");

        // Add RuleSets for nested elements
        //注册规则集中定义的一组规则实例。
        digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
        digester.addRuleSet(new EngineRuleSet("Server/Service/"));
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
        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));
        addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");

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

    }

先简单介绍一下Digester方法:

  • addObjectCreate
    创建对象
  • addSetProperties
    设置属性
  • addSetNext
    创建对象之间关系
  • addRule
    该方法会将一个Rule对象和它所匹配的模式添加到Digester对象的Rules集合中
  • addRuleSet
    调用addRuleInstances来解析xml标签

上述所有的步骤都是为了解析XML而准备,解析时根据XML内各标签的包含关系来创建对象,设置属性,关联对象。
备注:当前栈顶元素为Catalina实例。
下面简单看几个标签的解析规则:

Server

  //addObjectCreate:为指定的参数添加“对象创建”规则。
        digester.addObjectCreate("Server",
                "org.apache.catalina.core.StandardServer",
                "className");
        //为指定的参数添加“设置属性”规则。
        digester.addSetProperties("Server");
        //为指定的参数添加“set next”规则。
        digester.addSetNext("Server",
                "setServer",
                "org.apache.catalina.Server");

创建StandardServer实例对象,这是属性,调用上一个元素Catalina的setServer方法。
现在栈顶元素为StandardServer实例对象,也就是Server的外层元素是Catalina。

public void setServer(Server server) {
        this.server = server;
    }

Service

  //StandardServer.addService:StandardService
        digester.addObjectCreate("Server/Service",
                "org.apache.catalina.core.StandardService",
                "className");
        digester.addSetProperties("Server/Service");
        digester.addSetNext("Server/Service",
                "addService",
                "org.apache.catalina.Service");

会先创建StandardService实例,属性填充,并找到上层标签Server的实例StandardServer,执行addService方法。

 @Override
    public void addService(Service service) {
        //service持有Server的引用
        service.setServer(this);
        //服务锁,即service要一个一个的加入
        synchronized (servicesLock) {
            //services数组拷贝,将新来的service增加到最后
            Service results[] = new Service[services.length + 1];
            System.arraycopy(services, 0, results, 0, services.length);
            results[services.length] = service;
            services = results;
            //当前Server组件是available
            if (getState().isAvailable()) {
                try {
                    //调用service的生命周期方法start
                    service.start();
                } catch (LifecycleException e) {
                    // Ignore
                }
            }
            // Report this property change to interested listeners
            //将此属性更改报告给感兴趣的侦听器
            support.firePropertyChange("service", null, service);
        }
    }

可以看出Server与Service是一对多的关系。

Connector

 //StandardService.addConnector
        digester.addRule("Server/Service/Connector",
                new ConnectorCreateRule());
        digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(
                new String[]{"executor", "sslImplementationName", "protocol"}));
        digester.addSetNext("Server/Service/Connector",
                "addConnector",
                "org.apache.catalina.connector.Connector");

这里是通过ConnectorCreateRule来解析Connector节点,ConnectorCreateRule实现了Rule接口,解析的时候先执行begin方法,获取
digester的上层元素Service的实例StandardService,再创建Connector实例,设置一些属性之后将Connector入digester栈。解析结束后
调用end方法,将Connector实例出栈。也就是说当前栈顶是Service实例,执行StandardService的addConnector方法建立联系。

 public void addConnector(Connector connector) {
        //connector一个一个加入到当前Service组件中
        synchronized (connectorsLock) {
            connector.setService(this);
            Connector results[] = new Connector[connectors.length + 1];
            System.arraycopy(connectors, 0, results, 0, connectors.length);
            results[connectors.length] = connector;
            connectors = results;
            if (getState().isAvailable()) {
                try {
                    //生命周期函数
                    connector.start();
                } catch (LifecycleException e) {
                    log.error(sm.getString(
                            "standardService.connector.startFailed",
                            connector), e);
                }
            }
            // Report this property change to interested listeners
            support.firePropertyChange("connector", null, connector);
        }
    }

从这可以看出Service组件与Connector组件是一对多的关系。

Engine

 digester.addRuleSet(new EngineRuleSet("Server/Service/"));

EngineRuleSet实现了RuleSet接口,当解析xml时会调用addRuleInstances方法:

 public void addRuleInstances(Digester digester) {

        digester.addObjectCreate(prefix + "Engine",
                                 "org.apache.catalina.core.StandardEngine",
                                 "className");
        digester.addSetProperties(prefix + "Engine");
        digester.addRule(prefix + "Engine",
                         new LifecycleListenerRule
                         ("org.apache.catalina.startup.EngineConfig",
                          "engineConfigClass"));
        digester.addSetNext(prefix + "Engine",
                            "setContainer",
                            "org.apache.catalina.Engine");

        //Cluster configuration start
        digester.addObjectCreate(prefix + "Engine/Cluster",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties(prefix + "Engine/Cluster");
        digester.addSetNext(prefix + "Engine/Cluster",
                            "setCluster",
                            "org.apache.catalina.Cluster");
        //Cluster configuration end

        digester.addObjectCreate(prefix + "Engine/Listener",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties(prefix + "Engine/Listener");
        digester.addSetNext(prefix + "Engine/Listener",
                            "addLifecycleListener",
                            "org.apache.catalina.LifecycleListener");


        digester.addRuleSet(new RealmRuleSet(prefix + "Engine/"));

        digester.addObjectCreate(prefix + "Engine/Valve",
                                 null, // MUST be specified in the element
                                 "className");
        digester.addSetProperties(prefix + "Engine/Valve");
        digester.addSetNext(prefix + "Engine/Valve",
                            "addValve",
                            "org.apache.catalina.Valve");
    }

套路还是一样的,先解析一层,之后再返回上一层。最终会执行到StandardService的setContainer方法:

 public void setContainer(Engine engine) {
        //废掉老的Engine
        Engine oldEngine = this.engine;
        if (oldEngine != null) {
            oldEngine.setService(null);
        }
        //绑定新的engine
        this.engine = engine;
        if (this.engine != null) {
            this.engine.setService(this);
        }
        if (getState().isAvailable()) {
            if (this.engine != null) {
                try {
                    //生命周期函数
                    this.engine.start();
                } catch (LifecycleException e) {
                    log.warn(sm.getString("standardService.engine.startFailed"), e);
                }
            }
            // Restart MapperListener to pick up new engine.
            //重启MapperListener以获取新引擎。
            try {
                mapperListener.stop();
            } catch (LifecycleException e) {
                log.warn(sm.getString("standardService.mapperListener.stopFailed"), e);
            }
            try {
                mapperListener.start();
            } catch (LifecycleException e) {
                log.warn(sm.getString("standardService.mapperListener.startFailed"), e);
            }
            //关闭老的引擎
            if (oldEngine != null) {
                try {
                    oldEngine.stop();
                } catch (LifecycleException e) {
                    log.warn(sm.getString("standardService.engine.stopFailed"), e);
                }
            }
        }

        // Report this property change to interested listeners
        support.firePropertyChange("container", oldEngine, this.engine);
    }

可以看出Service与Engine引擎是一对一的关系。

Host

digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));

能看出来Host在Engine的下一层。最终会执行StandardEngine的addChild方法,将Host容器置于Engine内部。

Context

digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));

Context在Host的下一层,也会执行StandardHost的addChild方法,将Context容器置于Host容器内部。

发现没,这里没涉及到Wrapper容器,它是什么时候产生的呢?
这里先保留,等后面遇到再说。

至此,server.xml配置文件的解析差不多就了解了,也知道了Catalina中的Server组件是怎么注入的,还了解了Server、Service、Engine、Host、Context之间的关系。

你可能感兴趣的:(Tomcat源码分析【五】启动过程分析之配置文件解析与组件注入)