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,这个有空我们可以再写一篇文章解析一下!

原文地址:https://www.cnblogs.com/coldridgeValley/p/5816408.html