zoukankan      html  css  js  c++  java
  • Digester在tomcat中的使用

    上一篇文章我们了解了Digester的基本使用方法,今天这篇文章我们来看下Digester类在tomcat中的具体使用,tomcat使用Digester类来解析相关的xml文件,包括web.xmlserver.xml,我们先讨论下server.xmlweb.xml下次讨论。

    在之前的tomcat启动过程源码讲解的时候,我们讲过Catalina类实例的load(),源码如下

    /**
     * Start a new server instance.
     */
    public void load() {
    
        long t1 = System.nanoTime();
    
        initDirs();
    
        // Before digester - it may be needed
    
        initNaming();
    
    	//1111111111111111
        // Create and execute our Digester
        Digester digester = createStartDigester();
    
        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
            try {
                file = configFile();
                inputStream = new FileInputStream(file);
                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 {
    			//222222222222222
                inputSource.setByteStream(inputStream);
                digester.push(this);
                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 {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
    
        getServer().setCatalina(this);
    
        // Stream redirection
        initStreams();
    
        // Start the new 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("Catalina.start", e);
            }
    
        }
    
        long t2 = System.nanoTime();
        if (log.isInfoEnabled()) {
            log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
        }
    
    }
    

    源码在1处创建了一个Digester实例,在2处进行了解析,我们一步一步看,先查看创建处

      /**
     * Create and configure the Digester we will be using for startup.
     */
    protected Digester createStartDigester() {
        long t1 = System.currentTimeMillis();
        // Initialize the digester
        Digester digester = new Digester();
        digester.setValidating(false);
        digester.setRulesValidation(true);
        HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<Class<?>, List<String>>();
        ArrayList<String> attrs = new ArrayList<String>();
        attrs.add("className");
        fakeAttributes.put(Object.class, attrs);
        digester.setFakeAttributes(fakeAttributes);
        digester.setUseContextClassLoader(true);
    
        // Configure the actions we will be using
    	//根据Server标签创建Server对象,实例是StandardServer,设置属性,并且调用前一个对象的setServer方法,将StandardServer对象设置进去,上一个对象是什么,等下再讲解
    	//1111111111
        digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");
    
    	//创建NamingResources对象,并且设置到StandardServer中
        digester.addObjectCreate("Server/GlobalNamingResources", "org.apache.catalina.deploy.NamingResources");
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources", "org.apache.catalina.deploy.NamingResources");
    	//遇到Listener标签,根据标签上className属性上类创建对应的对象,设置对应Listener的属性并且将该对象设置到StandardServer中
        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");
    	//遇到Service标签创建StandardService对象,然后设置其属性,然后将StandardService实例设置到StandardServer中
        digester.addObjectCreate("Server/Service", "org.apache.catalina.core.StandardService", "className");
        digester.addSetProperties("Server/Service");
        digester.addSetNext("Server/Service", "addService", "org.apache.catalina.Service");
    	
    	//遇到Service标签内部的Listener标签,跟上面一样创建对应的listener对象,然后设置到 StandardService对象中。
        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
    	//创建对应的Executor 然后设置到StandardService中
        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");
    	
    	//22222222
        digester.addRule("Server/Service/Connector", new ConnectorCreateRule());
        digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(new String[]{"executor"}));
    	//将Connecter实例添加到 StandardService中
        digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.connector.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
    	//33333333333
        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/"));
    	
    	//4444444444
        // 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的基础上非常容易读懂,简单的代码我都写了一些注释,下面我们稍微关注下,标注1,2,3的地方。

    标注1:

    之所以查看这段代码是因为,只调用了一次addObjectCreate就调用了addSetNext,这个也许有读者看不懂,但是其实这里有个小细节,就是在上面一段源码(Catalina)标注2的地方,先调用了Digester对象的一个push方法。大家都知道Digester对象实例内部有个stack专门用来存储临时对象,也就是解析的过程中被创建的对象,当解析结束的时候就会把这个对象pop出去,所以在解析的一开始,Digester对象的堆栈中就已经存在了一个对象,那么这个对象就是Catalina的实例,所以第一个addSetNext实际上是把第一个addObjectCreate创建的对象set到Catalina的实例里面去。

    标注2

    digester.addRule("Server/Service/Connector", new ConnectorCreateRule());
    digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(new String[]{"executor"}));
    //将Connecter实例添加到 StandardService中
    digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.connector.Connector");
    

    我们在讲解tomcat启动的时候,讲到过Connector的初始化,但是当时我们并不知道Connector是如何初始化,而且我们只查看了他的构造方法,知道Connector会根据协议的不同而去创建不同的对象。之所以需要关注代码2这里,是因为这里就隐藏了Connector初始化的秘密。

    addRule这个方法我们之前讲解过,这个方法会给在Digester对象内部的规则对象(Rules对象,变量叫rules)中的规则列表(一个hashmap)中针对摸个匹配模式添加一条规则!所以我们直接看ConnectorCreateRule对象即可。

    class ConnectorCreateRule extends Rule
    

    ConnectorCreateRule继承了Rule对象,复写了begin方法,我们来查看具体源码

        /**
     * Process the beginning of this element.
     *
     * @param namespace the namespace URI of the matching element, or an 
     *   empty string if the parser is not namespace aware or the element has
     *   no namespace
     * @param name the local name if the parser is namespace aware, or just 
     *   the element name otherwise
     * @param attributes The attribute list for this element
     */
    @Override
    public void begin(String namespace, String name, Attributes attributes)
            throws Exception {
        Service svc = (Service)digester.peek();
        Executor ex = null;
        if ( attributes.getValue("executor")!=null ) {
            ex = svc.getExecutor(attributes.getValue("executor"));
        }
        Connector con = new Connector(attributes.getValue("protocol"));
        if ( ex != null )  _setExecutor(con,ex);
        
        digester.push(con);
    }
    

    参照createStartDigester标注2之前的源码可以看到,digester内部stack里面栈顶的对象是StandardService,所以peek()返回的是StandardService,如果Connector标签上包含executor属性,就根据executor名字去StandardService内部去获取一个Executor,当然我们没有配置Executor相关属性所以就直接看下面的了,下面就直接创建了一个Connector对象,传递的参数是Connector标签上protocol的值。我们查看server.xml就知道protocol会有两种值,HTTP/1.1AJP/1.3,所以我们现在就找到了接收http请求的Connector的实例的初始化部分就是在这里完成的,最后digester对象将新创建的Connector实例又推到了栈顶,方便下次调用addSetNext的时候将创建的Connector实例设置到对应的StandardService中。

    注:peek()返回但不取出栈顶第一个元素 pop()返回并取出栈顶第一个元素。

    标注3

    	//33333333333
        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/"));
    

    addRuleSet()方法之前文章中也讲解过这个方法,需要的参数是是一个RuleSet的实现类,我们看下EngineRuleSet

    class EngineRuleSet extends RuleSetBase
    

    继承了RuleSetBase复写了addRuleInstances方法。

    prifix = "Server/Service/";
    
    @Override
    public void addRuleInstances(Digester digester) {
    	
    	//解析到Engine标签以后,创建StandardEngine对象,设置属性,并且设置到StandardService对象中(根据之前的代码)
        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.Container");
    
        //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
    	
    	//创建StandardEngine对象内部的Listener对象,并且设置属性,设置到Engine对象中
        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/"));
    	//创建StandardEngine对象内部的Value对象,设置相关属性,设置到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");
    
    }
    

    这一长串的代码其实做的就是一件事情,那就是完善创建StandardEngine对象并且设置内部各种属性和对象的引用。

    如果你继续查看HostRuleSet,ContextRuleSet的源码你会发现其实都是在做相似的事情,所以我们就对标注3的代码比较明了了

    标注3的代码给我们充分展示了tomcat内部比较重要的几个对象Engine,Host,Context他们是如何创建的,他们之间的引用关系是如何被设置的,可以看出Digester这个工具类解放了代码,可以使我们充分的利用xml文件特有的灵活,帮我们解决了对象之间的依赖关系,而我们不需要写代码,只需要配置下xml文件即可!

    前面说到Digester在启动时候会解析xml,其实在tomcat stop的时候也就是调用Catalinastop()的时候也会创建一个Digeser对象,不过比较简单就留给读者自行理解了,当然tomcat不仅在解析server.xml的时候使用了Digester,同时tomcat在解析web.xml的时候也使用了Digester,这个有空我们可以再写一篇文章解析一下!

  • 相关阅读:
    读《大道至简》第二章有感
    《大道至简》读后感
    JAVA课后作业
    大道至简第三章观后感
    JAVA作业之两数的加减乘除
    JAVA作业之动手动脑
    第一次Java实验
    大道至简第二章观后感
    大道至简第一章观后感
    Django__admin的配置
  • 原文地址:https://www.cnblogs.com/coldridgeValley/p/5816408.html
Copyright © 2011-2022 走看看