Tomcat源码解析:6、Digester XML原理解析

概要

在Tomcat中使用了大量的xml文件去配置一些重要的信息,以达到程序与配置的解耦,比如server.xml、web.xml。解析XML文件的技术,Tomcat使用了Digester框架去解析xml。我们可能对Digester比较陌生,比较常用的解析xml的技术有JDK自带的SAX、还有比较出名的第三分Dom4j等等。Dom4j和SAX解析的区别

  • Dom4j是把一个xml文件全部读取到内存中,构建成一个DOM树来解析,所以Dom4j适合读取比较小的xml文件。
  • SAX是基于文件流来解析xml文件的,在读取xml文件流时,SAX会通过节点来触发相应的操作,也可以说SAX是基于文件流的事情触发机制来解析xml文件的。

而Digester它是基于SAX技术,也可以理解为对SAX技术的包装。它的作用是将XML的节点信息转化为对象。下面我们以Tomcat解析server.xml文件为例,讲解这个技术的使用和原理

Digester

Tomcat是在Catalina的load方法里进行解析server.xml,具体关于Tomcat是什么时候执行Catalina的load方法,并且整个load方法的过程,可以看我之前讲Tomcat的初始化。现在我们主要看下Tomcat是如何利用Digester去解析server.xml,并且如何利用server.xml去创建相关的组件

首先我们看下Tomcat自带的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" />
  <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>

      <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>

Catalina
public void load() {
		// 省略一些其他代码,只留下Digester相关的代码
		
		/*
		 * 创建Digester对象,并且添加了一系列的Rule
		 */
        Digester digester = createStartDigester();

        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
		
		/*
		 * 获取server.xml的文件流
		 */
        file = configFile();
        inputStream = new FileInputStream(file);
        
        /*
         * 把文件流转换为InputSource 
         */
        inputSource = new InputSource(file.toURI().toURL().toString());
		
		inputSource.setByteStream(inputStream);
		
		/*
		 * 把当前对象,也就是Catalina,压入栈中,这个栈很重要,后面会说
		 */
        digester.push(this);
        
        /*
         * 开启解析server.xml,把xml的节点信息转换为对象,并且进行一些对象的属性配置等等
         */
        digester.parse(inputSource);

        // 省略一些其他代码,只留下Digester相关的代码
    }

首先我们来看下createStartDigester()方法,它主要创建Digester对象,并且为其添加了一系列的Rule

protected Digester createStartDigester() {

        long t1=System.currentTimeMillis();
        // 创建Digester对象
        Digester digester = new Digester();
        digester.setValidating(false);
        digester.setRulesValidation(true);
		/*
		 * 设置需要忽略的一些属性,在这里只添加了className属性
		 */
        HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<>();
        ArrayList<String> attrs = new ArrayList<>();
        attrs.add("className");
        fakeAttributes.put(Object.class, attrs);
        digester.setFakeAttributes(fakeAttributes);

		/*
		 * 设置UseContextClassLoader=true,则从Thread.currentThread().getContextClassLoader();获取ClassLoader
		 */
        digester.setUseContextClassLoader(true);
		
		/**
		 * 下面设置了一些解析节点的Rule
		 */
		
        /**
         * 
         */
        digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.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标签下的Listener的Rule
         * 
         *      
         *     
         */
        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");

        /**
         * 
         */
        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的Rule
         * 
         *      
         *              
         *      
         *     
         */
        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");

        /**
         * 解析Service标签下的Executor的Rule
         *  
         *      
         *              
         *      
         *     
         */
        //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");


        /**
         * 解析Service标签下的Connector的Rule
         * 
         *      
         *              
         *      
         *    
         */
        digester.addRule("Server/Service/Connector",
                         new ConnectorCreateRule());
        digester.addRule("Server/Service/Connector",
                         new SetAllPropertiesRule(new String[]{"executor"}));
        digester.addSetNext("Server/Service/Connector",
                            "addConnector",
                            "org.apache.catalina.connector.Connector");

        /**
         * 解析Connector标签下的Listener的Rule
         * 
         *      
         *              
         *                      
         *              
         *      
         *    
         */

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

        /**
         * 这些是Engine、Host、Valve标签解析
         */
        // 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/"));

        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);

    }

由上面代码可知,我们可以总结用的比较多的几个方法有

  • addObjectCreate
  • addSetProperties
  • addSetNext
  • addRuleSet

下面我们看下上面常见的几个方法

addObjectCreate

	public void addObjectCreate(String pattern, String className, String attributeName) {
		//  实际上调用了addRule方法,并且创建了ObjectCreateRule对象
		addRule(pattern, new ObjectCreateRule(className, attributeName));
	}

	public void addRule(String pattern, Rule rule) {
		// 把当前Digester实例设置到传入的Rule中
		rule.setDigester(this);
		// 首先调用getRules(),然后调用RulesBase的add方法,保存rule
	    getRules().add(pattern, rule);
	}
	
	public Rules getRules() {
		// 第一次进来这个rules 会为空,创建RulesBase对象
		if (this.rules == null) {
	    	this.rules = new RulesBase();
	    	// 为RulesBase设置Digester实例
	        this.rules.setDigester(this);
	    }
	    return (this.rules);
	}
	
    public void add(String pattern, Rule rule) {
    	/*
    	 * pattern可以理解为节点路径,可以回去看上面的createStartDigester的方法
    	 * 比如说:
    	 * 	Server 是父节点,那么pattern = “Server”
    	 * 	GlobalNamingResources 是 Server 子节点,那么pattern = “Server/GlobalNamingResources”
    	 * 以此类推
    	 */
        int patternLength = pattern.length();
        
        /*
         * 如果pattern是以/结尾,那么需要把结尾的/去掉
         */
        if (patternLength>1 && pattern.endsWith("/")) {
            pattern = pattern.substring(0, patternLength-1);
        }

		/*
		 * protected HashMap> cache = new HashMap<>();
		 * 从cache中取出当前pattern的Rule集合,
		 * 
		 */
        List<Rule> list = cache.get(pattern);
        
        /*
         * 如果list为空,那么需要初始化,并且把list放入cache中
         */
        if (list == null) {
            list = new ArrayList<>();
            cache.put(pattern, list);
        }
        // 把rule添加到list
        list.add(rule);
        
        /*
         * 同时把rule添加到rules中
         * protected ArrayList rules = new ArrayList<>();
         */
        rules.add(rule);
        if (this.digester != null) {
            rule.setDigester(this.digester);
        }
        if (this.namespaceURI != null) {
            rule.setNamespaceURI(this.namespaceURI);
        }

    }

根据上述代码,我们可以得出结论就是

  • 创建了一个ObjectCreateRule
  • 把ObjectCreateRule放到cache中保存起来,以pattern为key,ObjectCreateRule为rule

addSetProperties

    public void addSetProperties(String pattern) {
    	/*
    	 * addRule,跟上面addObjectCreate的addRule是一样的,就先不分析了
    	 */
        addRule(pattern, new SetPropertiesRule());
    }
    

根据上述代码,我们可以得出结论就是

  • 创建了一个SetPropertiesRule
  • 把SetPropertiesRule放到cache中保存起来,以pattern为key,SetPropertiesRule为rule

addSetNext

    public void addSetNext(String pattern, String methodName, String paramType) {

        addRule(pattern, new SetNextRule(methodName, paramType));

    }

根据上述代码,我们可以得出结论就是

  • 创建了一个SetNextRule
  • 把SetNextRule放到cache中保存起来,以pattern为key,SetNextRule为rule

addRuleSet

    public void addRuleSet(RuleSet ruleSet) {

        String oldNamespaceURI = getRuleNamespaceURI();
        String newNamespaceURI = ruleSet.getNamespaceURI();
        if (log.isDebugEnabled()) {
            if (newNamespaceURI == null) {
                log.debug("addRuleSet() with no namespace URI");
            } else {
                log.debug("addRuleSet() with namespace URI " + newNamespaceURI);
            }
        }
        setRuleNamespaceURI(newNamespaceURI);
        /*
         * 实际上就是调用了ruleSet的addRuleInstances方法
         * 并且传入了Digester对象
         */
        ruleSet.addRuleInstances(this);
        setRuleNamespaceURI(oldNamespaceURI);

    }

以HostRuleSet的addRuleInstances方法为例

HostRuleSet:
	@Override
    public void addRuleInstances(Digester digester) {

        digester.addObjectCreate(prefix + "Host",
                                 "org.apache.catalina.core.StandardHost",
                                 "className");
        digester.addSetProperties(prefix + "Host");
        digester.addRule(prefix + "Host",
                         new CopyParentClassLoaderRule());
        digester.addRule(prefix + "Host",
                         new LifecycleListenerRule
                         ("org.apache.catalina.startup.HostConfig",
                          "hostConfigClass"));
        digester.addSetNext(prefix + "Host",
                            "addChild",
                            "org.apache.catalina.Container");

        digester.addCallMethod(prefix + "Host/Alias",
                               "addAlias", 0);

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

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

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

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

    }

所以addRuleSet的作用,其实就是一个Rule的集合,为了更好统一管理单个标签和其子标签的Rule,单一职责原则。

从上面得出,我们看到比较多的就是三个Rule,分别为:

  • ObjectCreateRule
  • SetPropertiesRule
  • SetNextRule

这三个Rule,后面在说,我们回来Catalina的load方法,在里面调用了Digester的parse方法开始解析xml文件

Digester:
    public Object parse(InputSource input) throws IOException, SAXException {
        configure();
        // 调用了getXMLReader().parse(input);开始解析server.xml
        getXMLReader().parse(input);
        return (root);
    }    

我们看下Digester的源码,我们之前说了它是对SAX技术的包装,那么自然就有几个比较典型的方法

  • startDocument:开始解析XML的时候调用
  • startElement:解析节点的开始标签时调用
  • characters:解析节点内容的时候调用
  • endElement:解释节点的结束标签时调用
  • endDocument:整个XML解析完毕时调用
public class Digester extends DefaultHandler2 {
	
	/*
	 * 存储当前节点的rule集合的栈
	 */
    protected ArrayStack<List<Rule>> matches = new ArrayStack<>(10);
	
	/*
	 * 存储当前节点的对象的栈
	 */
    protected ArrayStack<Object> stack = new ArrayStack<>();
	
	/*
	 * 存储当前节点的内容
	 */
    protected StringBuilder bodyText = new StringBuilder();
    
    /*
     * 存储上一个节点的内容的栈
     */
    protected ArrayStack<StringBuilder> bodyTexts = new ArrayStack<>();
	
	/*
     * 下面只列取3个关键的方法startElement、characters、endElement,省略了一些其他的方法和属性
     *  以Server节点为例 
	 */

	/*
	 * qName:当前标签名
	 */
	@Override
	public void startElement(String namespaceURI, String localName, String qName, Attributes list)
            throws SAXException {
        boolean debug = log.isDebugEnabled();

        if (saxLog.isDebugEnabled()) {
            saxLog.debug("startElement(" + namespaceURI + "," + localName + "," + qName + ")");
        }

        list = updateAttributes(list);
		
		// 把上一个bodyText压入栈中
        bodyTexts.push(bodyText);
        // 新建一个当前节点的bodyText,用于存储当前节点的内容。每一个节点会对应一个新的bodyText 
        bodyText = new StringBuilder();
		
		// 获取当前节点名称
        String name = localName;
        if ((name == null) || (name.length() < 1)) {
            name = qName;
        }
		
		/*
		 * match:表示之前解析过的节点路径
		 * 比如说我现在有三个节点
		 * 
		 * 	
		 * 		
		 * 	
		 * 
		 * 如果当我解析到c的时候,那么match:a/b
		 */
        StringBuilder sb = new StringBuilder(match);
        // 如果match长度不为空,在match后面加上/
        if (match.length() > 0) {
            sb.append('/');
        }
        // 把当前节点添加上去
        sb.append(name);
        /*
         *  获取当前节点完整路径,如上面的节点就是a/b/c,把当前路径赋值给成员变量match保存起来
         */
        match = sb.toString();
        if (debug) {
            log.debug("  New match='" + match + "'");
        }

        /*
         * 根据match【也就是当前节点】,获取当前路径下的Rule集合。
         * 还记得之前调用addObjectCreate、addSetProperties、addSetNext等方法
         * 添加的Rule,就在这里起作用了
         */
        List<Rule> rules = getRules().match(namespaceURI, match);
        // 把获取的rule集合压入matches栈中
        matches.push(rules);
        if ((rules != null) && (rules.size() > 0)) {
        	/*
        	 * 依次调用该节点下的Rule集合的begin方法
        	 * 上面我们所有常见的三个Rule中,
        	 * ObjectCreateRule、SetPropertiesRule有重写begin方法
        	 * 后面我们会分析这两个Rule
        	 */        	
            for (int i = 0; i < rules.size(); i++) {
                try {
                    Rule rule = rules.get(i);
                    if (debug) {
                        log.debug("  Fire begin() for " + rule);
                    }
                    rule.begin(namespaceURI, name, list);
                } catch (Exception e) {
                    log.error("Begin event threw exception", e);
                    throw createSAXException(e);
                } catch (Error e) {
                    log.error("Begin event threw error", e);
                    throw e;
                }
            }
        } else {
            if (debug) {
                log.debug("  No rules found matching '" + match + "'.");
            }
        }

    }


	@Override
	public void characters(char buffer[], int start, int length) throws SAXException {

        if (saxLog.isDebugEnabled()) {
            saxLog.debug("characters(" + new String(buffer, start, length) + ")");
        }
		/* 
		 * 把当前节点的内容,append到bodyText
		 * 上面startElement说过每个节点会创建一个bodyText【StringBuilder】去存储节点内容
		 */
        bodyText.append(buffer, start, length);

    }

	@Override
    public void endElement(String namespaceURI, String localName, String qName)
            throws SAXException {

        bodyText = updateBodyText(bodyText);

        // 获取当前节点名称
        String name = localName;
        if ((name == null) || (name.length() < 1)) {
            name = qName;
        }
	
		/*
		 * 从matches栈顶取出当前节点的Rule,并且从栈顶移除
		 * pop:取出栈顶的值,并且移除栈顶的值
		 * peek:取出栈顶的值,但是不移除栈顶的值
		 */
        List<Rule> rules = matches.pop();
        if ((rules != null) && (rules.size() > 0)) {
            // 获取当前节点的内容
            String bodyText = this.bodyText.toString();
            // 循环当前的rule,并且调用rule的body方法
            for (int i = 0; i < rules.size(); i++) {
                try {
                    Rule rule = rules.get(i);
                    if (debug) {
                        log.debug("  Fire body() for " + rule);
                    }
                    rule.body(namespaceURI, name, bodyText);
                } catch (Exception e) {
                    log.error("Body event threw exception", e);
                    throw createSAXException(e);
                } catch (Error e) {
                    log.error("Body event threw error", e);
                    throw e;
                }
            }
        } else {
            if (debug) {
                log.debug("  No rules found matching '" + match + "'.");
            }
            if (rulesValidation) {
                log.warn("  No rules found matching '" + match + "'.");
            }
        }
		
		// 将当前bodyText从bodyTexts栈顶移除
        bodyText = bodyTexts.pop();

        if (rules != null) {
        	 // 循环当前的rule,并且调用rule的end方法
            for (int i = 0; i < rules.size(); i++) {
                int j = (rules.size() - i) - 1;
                try {
                    Rule rule = rules.get(j);
                    if (debug) {
                        log.debug("  Fire end() for " + rule);
                    }
                    rule.end(namespaceURI, name);
                } catch (Exception e) {
                    log.error("End event threw exception", e);
                    throw createSAXException(e);
                } catch (Error e) {
                    log.error("End event threw error", e);
                    throw e;
                }
            }
        }

        // Recover the previous match expression
        int slash = match.lastIndexOf('/');
        if (slash >= 0) {
            match = match.substring(0, slash);
        } else {
            match = "";
        }

    }

}

下面我们来分析下比较常见的几个Rule【ObjectCreateRule、SetPropertiesRule、SetNextRule】

ObjectCreateRule

它重写了Rule的begin、end方法,所以我们主要看这两个方法。

public class ObjectCreateRule extends Rule {
	
	// 省略一些成员变量、方法
	
	public ObjectCreateRule(String className,
                            String attributeName) {

        this.className = className;
        this.attributeName = attributeName;

    }

	@Override
	public void begin(String namespace, String name, Attributes attributes)
            throws Exception {
		
		/*
		 * 把className赋值给realClassName ,比如server对应的:org.apache.catalina.core.StandardServer
		 */
        String realClassName = className;
        
        /*
         * 如果attributeName不为空。因为有些className要从xml文件中读取的,比如server.xml的Listener
         * 举例:
         * 	1、有时候上层调用addObjectCreate会给className传入null
         * 		digester.addObjectCreate("Server/Listener", null, "className");
         * 	 那么这时候这个className=null,attributeName = "className",
         * 
         *  2、digester.addObjectCreate("Server","org.apache.catalina.core.StandardServer","className");
         *   那么这时候这个className="org.apache.catalina.core.StandardServer",attributeName = "className"
         * 
         */
        if (attributeName != null) {
        	/*
        	 * 那么从节点中获取该属性的值
        	 */
            String value = attributes.getValue(attributeName);
            /*
             * 如果获取到的值不为空,那么该值就是真正的className。             
             */ 
            if (value != null) {
                realClassName = value;
            }
        }
		
		/*
		 * 如果获取到的realClassName为空,那么说明没有设置className,则直接抛出空指针
		 */
        if (realClassName == null) {
            throw new NullPointerException("No class name specified for " +
                    namespace + " " + name);
        }

        // 利用classLoader创建对象
        Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
        Object instance = clazz.getConstructor().newInstance();
        // 并且把当前创建的对象,压入digester的栈中。Digester有一个成员变量存储对象的栈,叫做stack。可以回去看看
        digester.push(instance);
    }
    
	@Override
	public void end(String namespace, String name) throws Exception {
		// 从栈顶取出,并且移除
        Object top = digester.pop();
        if (digester.log.isDebugEnabled()) {
            digester.log.debug("[ObjectCreateRule]{" + digester.match +
                    "} Pop " + top.getClass().getName());
        }

    }
}

所以我们根据上述代码,我们可以知道ObjectCreateRule

  • 在begin方法中它主要是根据className创建对象,创建完后把对象压入Digester的栈顶。
  • 在end方法中,把Digester的栈顶元素移除

SetPropertiesRule

它重写了Rule的begin方法,所以我们主要看这个方法。

public class SetPropertiesRule extends Rule {
	public void begin(String namespace, String theName, Attributes attributes)
            throws Exception {

        /*
         * 从栈顶中取出之前在ObjectCreateRule创建的对象。
         */
        Object top = digester.peek();
        if (digester.log.isDebugEnabled()) {
            if (top != null) {
                digester.log.debug("[SetPropertiesRule]{" + digester.match +
                                   "} Set " + top.getClass().getName() +
                                   " properties");
            } else {
                digester.log.debug("[SetPropertiesRule]{" + digester.match +
                                   "} Set NULL properties");
            }
        }
		
		/*
		 * 遍历标签的元素
		 */
        for (int i = 0; i < attributes.getLength(); i++) {
            String name = attributes.getLocalName(i);
            if ("".equals(name)) {
                name = attributes.getQName(i);
            }
           	
           	// 获取元素的值
            String value = attributes.getValue(i);

            if (digester.log.isDebugEnabled()) {
                digester.log.debug("[SetPropertiesRule]{" + digester.match +
                        "} Setting property '" + name + "' to '" +
                        value + "'");
            }
            /*
             * digester.isFakeAttribute(top, name):如果当前的属性,是要被忽略的属性,那么就不进行设置。
             * 		还记得在Catalina.createStartDigester的方法中,创建了一个fakeAttributes的HashMap
             * 		里面添加了一个className,并且把这个fakeAttributes设置到Digester中。这个fakeAttributes
             * 		就在这时候起作用了,没印象可以回去看看。
             * 
             * IntrospectionUtils.setProperty(top, name, value):主要是利用反射把标签属性的值设置到对象中
             * 		这个方法源码,就不贴出来了,有兴趣可以去看,就是简单的反射调用属性的set方法然后进行设置。
             * 		举例:比如说属性为port,那么首先从方法中找到setPort方法,如果setPort方法不存在,那么它就
             * 		会找setProperty("name", "value")方法,如果都不存在,那么表明设置失败           
             */
            if (!digester.isFakeAttribute(top, name)
                    && !IntrospectionUtils.setProperty(top, name, value)
                    && digester.getRulesValidation()) {
                digester.log.warn("[SetPropertiesRule]{" + digester.match +
                        "} Setting property '" + name + "' to '" +
                        value + "' did not find a matching property.");
            }
        }

    }
}

所以我们根据上述代码,我们可以知道SetPropertiesRule

  • 在begin方法中,利用反射完成对象的属性注入。

SetNextRule

它重写了Rule的end方法,所以我们主要看这个方法。

public class SetNextRule extends Rule {
	
	public void end(String namespace, String name) throws Exception {

        // 取出栈顶的元素,但不移除。为了好记,这个取名为child 
        Object child = digester.peek(0);
        // 取出栈顶后面的一个元素,但不移除。为了好记,这个取名为parent 
        Object parent = digester.peek(1);
        if (digester.log.isDebugEnabled()) {
            if (parent == null) {
                digester.log.debug("[SetNextRule]{" + digester.match +
                        "} Call [NULL PARENT]." +
                        methodName + "(" + child + ")");
            } else {
                digester.log.debug("[SetNextRule]{" + digester.match +
                        "} Call " + parent.getClass().getName() + "." +
                        methodName + "(" + child + ")");
            }
        }

        /*
         * 把child的对象利用反射注入到parent中
         * 举例:以Server节点为例,在Catalina.createStartDigester中
         * 	    调用了digester.addSetNext("Server",
         *                   "setServer",
         *                  "org.apache.catalina.Server");
         * 		而且在Catalina.load方法中,调用了digester.push(this);把Catalina的对象压入Digester的对象栈中
         * 		
         * 		所以IntrospectionUtils.callMethod1方法的意思就是:反射调用Catalina.setServer(Server server)方法
         */
        IntrospectionUtils.callMethod1(parent, methodName,
                child, paramType, digester.getClassLoader());

    }
}

所以我们根据上述代码,我们可以知道SetNextRule

  • 在end方法中,把child的对象注入到parent中

整个Digester 解析XML的原理,就是这样了。理解Digester能更好知道Tomcat在初始化时基于server.xml创建了哪些对象。

你可能感兴趣的:(Tomcat源码解析,Tomcat)