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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

文章首发于: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配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <Server port="8005" shutdown="SHUTDOWN">
      <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
      <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
      <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
      <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
      <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
      <GlobalNamingResources>
        <Resource name="UserDatabase" auth="Container"
                  type="org.apache.catalina.UserDatabase"
                  description="User database that can be updated and saved"
                  factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
                  pathname="conf/tomcat-users.xml" />
      </GlobalNamingResources>
      <Service name="Catalina">
        <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443" />
        <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
        <Engine name="Catalina" defaultHost="localhost">
          <Realm className="org.apache.catalina.realm.LockOutRealm">
            <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                   resourceName="UserDatabase"/>
          </Realm>
          <Host name="localhost"  appBase="webapps"
                unpackWARs="true" autoDeploy="true">
            <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                   prefix="localhost_access_log" suffix=".txt"
                   pattern="%h %l %u %t "%r" %s %b" />
          </Host>
        </Engine>
      </Service>
    </Server>

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

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<Class<?>, List<String>> fakeAttributes = new HashMap<>();
            List<String> 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之间的关系。


来源:https://www.jianshu.com/u/9632919f32c3

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Tomcat源码分析【五】启动过程分析之配置文件解析与组件注入

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏