源码学习,Tomcat源码分析

发布时间:2019-10-04  栏目:编程  评论:0 Comments

在Tomcat中解析xml是使用digester,在apache的common中的一个项目,采用类似模板匹配的机制进行匹配,再配合相对应的规则达到对xml的解析。rule包含了begin方法和end方法,当匹配到标签的开始或者结束就会调用这两种方法。接下来就具体讲解digester的几种方法

Tomcat作为一个Java应用, 其启动一定是需要一个main()
作为入口。而这个main() 函数就在类Bootstrap内部

在这篇文章中主要针对tomcat源码中Rule部分的解析;这部分功能主要涉及server.xml文件加载和tomcat容器中各组件初始化的过程。在我之前的文章中《tomcat源码解析(一)——Bootstrap和Catalina启动部分》和《tomcat源码解析(二)——xml解析过程分析》分别对启动过程和xml解析进行了分析。

addObjectCreate()

public void addObjectCreate(String pattern, String className) { addRule(pattern, new ObjectCreateRule(className));} public void addObjectCreate(String pattern, String className, String attributeName) { addRule(pattern, new ObjectCreateRule(className, attributeName)); // 添加了规则 ObjectCreateRule,和上一个函数稍有差别,没有属性名称} // ObjectCreateRule 的begin方法public void begin(String namespace, String name, Attributes attributes) throws Exception { String realClassName = className; if (attributeName != null) { // 这一步主要是为了兼容设置自定义的className(用户自定义的监听器等) String value = attributes.getValue(attributeName); if (value != null) { realClassName = value; } } if (digester.log.isDebugEnabled { digester.log.debug("[ObjectCreateRule]{" + digester.match + "}New " + realClassName); } if (realClassName == null) { throw new NullPointerException("No class name specified for " + namespace + " " + name); } // 调用自定义的类加载器生成一个对象,并压入到digester的栈中 Class<?> clazz = digester.getClassLoader().loadClass(realClassName); Object instance = clazz.newInstance(); digester.push;}

如上述的begin方法,当遇到了匹配该pattern的元素是,在其开始的位置,生成以该类名的实现的对象

Main 函数

  /**
     * Main method and entry point when starting Tomcat via the provided
     * scripts.
     *
     * @param args Command line arguments to be processed
     */
    public static void main(String args[]) {

        if (daemon == null) {
            // Don't set daemon until init() has completed
            Bootstrap bootstrap = new Bootstrap();
            try {
                bootstrap.init();
            } catch (Throwable t) {
                handleThrowable(t);
                t.printStackTrace();
                return;
            }
            daemon = bootstrap;
        } 
.................................省略代码......................

            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                daemon.load(args);
                daemon.start();
            } 
.................................省略代码......................
    }

可以看出对于第一次启动来讲,main函数主要是做了新建一个Bootstrap类,并调用其init()方法

  /**
     * Initialize daemon.
     * @throws Exception Fatal initialization error
     */
    public void init() throws Exception {

        initClassLoaders();

        Thread.currentThread().setContextClassLoader(catalinaLoader);

        SecurityClassLoad.securityClassLoad(catalinaLoader);

        // Load our startup class and call its process() method
        if (log.isDebugEnabled())
            log.debug("Loading startup class");
        // 通过反射找到一个Catalina类,并通过反射调用其startUp()方法
        Class<?> startupClass =
            catalinaLoader.loadClass
            ("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.newInstance();
        ........................省略代码.......................
         this.catalinaDaemon = startupInstance;

    }

从上面代码可以看出,Bootstrap
的init方法通过反射新建了一个Catalina类,并将这个类作为Boostrap的属性

    public void start()
        throws Exception {
        if( catalinaDaemon==null ) init();

        Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
        method.invoke(catalinaDaemon, (Object [])null);

    }

再来看一下Boostrapstart()方法。可以看出来。它也是通过反射来调起Catalina的start()方法。

catalina的start() 方法又调用了其load()方法,load 方法又调用了
createStartDigester()方法

public void start() {

        if (getServer() == null) {
            load();
        }
        . ......
}

对于createStartDigester(),

 /**
     * Create and configure the Digester we will be using for startup.
     * @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);
        HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<>();
        ArrayList<String> attrs = new ArrayList<>();
        attrs.add("className");
        fakeAttributes.put(Object.class, attrs);
        digester.setFakeAttributes(fakeAttributes);
        digester.setUseContextClassLoader(true);

        // Configure the actions we will be using
        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");

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

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


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

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

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

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

        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对server.xml设置的标签动作有5种调用:

  • addObjectCreate:遇到起始标签的元素,初始化一个实例对象入栈
  • addSetProperties:遇到某个属性名,使用setter来赋值
  • addSetNext:遇到结束标签的元素,调用相应的方法
  • addRule:调用rule的begin
    、body、end、finish方法来解析xml,入栈和出栈给对象赋值
  • addRuleSet:调用addRuleInstances来解析xml标签

从这些规则和xml中可以看到,Calatina的Server对象是StandardServer。StandardService包含了多个Connector(xml中有2个connector)和一个StandardEngine
Container。StandardEngine包含了一个Host Container。

之前的文章中遗留了几个问题,将在这篇文章中进行解答;问题如下:

addSetProperties

public void addSetProperties(String pattern) { addRule(pattern, new SetPropertiesRule;}public void begin(String namespace, String theName, Attributes attributes) throws Exception { // 获取到当前栈顶的元素 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 { String name = attributes.getLocalName; if ("".equals { name = attributes.getQName; } String value = attributes.getValue; if (digester.log.isDebugEnabled { digester.log.debug("[SetPropertiesRule]{" + digester.match + "} Setting property '" + name + "' to '" + value + "'"); } // 到这就获取到了具体属性的名称和其内容,调用setProperty,反射invoke设置其对象属性值 // fakeAttribute 伪造属性,例如“className”,这些属性都不应该进行复制操作的 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."); } }}

就是对栈顶元素进行赋值操作,调用对象的setXXX方法,如果不存在setXXX方法,则调用setProperty方法。

1、第一篇文章启动过程中Catalina类createStartDigester中设置过程及原因。

addCallMethod

 public void addCallMethod(String pattern, String methodName) { addRule(pattern, new CallMethodRule(methodName)); }

调用栈顶元素的方法

2、第二篇文章中关于rule,调用的

addSetNext

public void addSetNext(String pattern, String methodName, String paramType) { addRule(pattern, new SetNextRule(methodName, paramType));} public void end(String namespace, String name) throws Exception { Object child = digester.peek; Object parent = digester.peek; 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 + ")"); } } // Call the specified method IntrospectionUtils.callMethod1(parent, methodName, child, paramType, digester.getClassLoader;}

获取栈的顶部2个元素,并调用第二个元素的方法,参数为第一个元素,完成两个参数的绑定关系

读取servlet.xml文件完成对server、service、connect、engine、host、甚至context的初始化操作。关于servlet.xml文件的设置可以参考Tomcat
server.xml配置示例,接下来就挑选一些部分具体讲解其中的解析xml以及组件初始化操作

rule.begin(namespaceURI, name, list);

rule.body(namespaceURI, name, bodyText);

rule.end(namespaceURI, name);

rule.finish();

StandardServer

// 解析xml的规则digester.push; // 先往栈中插入catalina本身digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer","className");// 创建一个新的对象,StandardServerdigester.addSetProperties;// 初始化StandardServer的值digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");// 在进行addSetNext的方法时,栈的元素是// 顶部 StandardServer, Catalina 底部// 调用第二个元素Catalina的setServer方法,设置catalina的server为初始化完成的StandardServer对象// Catalina的setServer方法public void setServer(Server server) { this.server = server;}

通过上述操作就完成了对catalina的server对象的初始化以及赋值

的具体过程和分析方法和Digester类的stack作用。

Listener

// 栈元素信息// 顶部 StandardServer, Catalina 底部digester.addObjectCreate("Server/Listener", null, // MUST be specified in the element "className");// 第二个值为null,则在创建对象的时候,通过该xml结构的“className”属性获取到其具体的全类名称// 然后栈元素信息成为了// 顶部 Listener, StandardServer, Catalina 底部digester.addSetProperties("Server/Listener");digester.addSetNext("Server/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");// 调用addSetNext就是调用StandardServer的addLifecycleListener方法,把当前的Listener对象插入进去

从而实现了实例化一个监听器,然后插入到StandardServer组件中的目的

如果对tomcat使用SAX解析xml已经了解的可以较好理解本文的思路。

addRuleSet ContextRuleSet

独自管理一个单独的rule集合,然后添加到digester中

digester.addObjectCreate(prefix + "Context", "org.apache.catalina.core.StandardContext", "className");digester.addSetProperties(prefix + "Context");// 添加StandardContext实例,并进行初始化的值设置// 栈元素信息// 顶部 StandardContext, StandardHost,StandardEngine, StandardService, StandardServer, Catalina 底部digester.addRule(prefix + "Context", new LifecycleListenerRule ("org.apache.catalina.startup.ContextConfig", "configClass"));// 这里面有个LifecycleListenerRule规则需要注意下,其最后的代码是Class<?> clazz = Class.forName(className);LifecycleListener listener = (LifecycleListener) clazz.newInstance();c.addLifecycleListener;// c 是指的StandardContext,创建一个ContextConfig实例,然后添加至c中

通过上述操作就完成了对StandardContext的实现,并插入了一个监听者,再配合catalina.start()方法就可以把整个Tomcat挂载的所有组件全部启动

  • 整个文章的内容总结为就是在catalina类的load函数中的一小块,把所有依赖的组件全部装载完毕,等到服务组件依次启动顺带就可以更好的理解之前将的一篇URL映射学习的相关内容。
  • 不过还是有个疑问不太清楚mbeanfactory类的createStandardContext方法具体是由谁调用的?是有嵌入式的Tomcat的调用方还是?

对于Rule的分析首先需要查看org.apache.tomcat.util.digester.Rule的源码,内容比较简单,就不放源码内容了;从中可以看到一些比较重要的信息:

1、Rule.java是一个是抽象类,对于单继承的java来说,其子类的功能职责一目了然。

2、Rule.java类所有的方法都不是抽象方法,而且具体实现都是空的(代表其子类可以自由的选择覆盖父类的方法,也可以不覆盖)

3、Rule.java类的子类比较多,这里只介绍部分,其他的类似。

现在可以正式分析Catalina的启动部分源码createStartDigester():

关于createStartDigester的源码如下:

    /**
     * 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.setClassLoader(StandardServer.class.getClassLoader());

        // Configure the actions we will be using
        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.NamingResources");
        digester.addSetProperties("Server/GlobalNamingResources");
        digester.addSetNext("Server/GlobalNamingResources",
                            "setGlobalNamingResources",
                            "org.apache.catalina.deploy.NamingResources");

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

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


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




        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
        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/"));
        digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("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));
        digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("Server/Service/Engine/Cluster/"));

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

    }

以上代码的功能分析如下:

1、digester.setValidating(false);设置SAX解析xml的DTD校验和schema校验,调用源码可参考Digester类的getFactory()和Digester类的getParser();

2、digester.setRulesValidation(true);看源码中只和日志打印的功能相关,和逻辑功能无关,在后文的源码中可以看到。

3、digester.setFakeAttributes(fakeAttributes);设置需要过滤的属性,如果设置后在进行类初始化时,配置的属性不会赋值给实例化后的对象,具体功能和实现在下文中会有详细说明。

4、digester.setClassLoader(StandardServer.class.getClassLoader());由于启动时首先启动的是StandardServer,设置StandardServer所在的的classloader。

5、之后的操作可以发现实际上调用的方法分为如下几个:

digester.addObjectCreate(String pattern, String className, String
attributeName)

digester.addSetProperties(String pattern)

digester.addSetNext(String pattern, String methodName, String paramType)

digester.addRule(String pattern, Rule rule)

digester.addRuleSet(RuleSet ruleSet)

到这里createStartDigester()的主体功能就算分析完了;

关于上面第5点这些方法,我们继续查看Digester类的实现源码,可以总结如下规律:

1、digester.addObjectCreate(String pattern, String className, String
attributeName)实际上调用的是addRule(String pattern, Rule
rule)方法;且第二个参数新创建了一个对象ObjectCreateRule(className,
attributeName);

2、同理可以发现digester.addSetProperties(String
pattern)调用的也是addRule(String pattern, Rule
rule)方法,创建了SetPropertiesRule()对象

3、digester.addSetNext(String pattern, String methodName, String
paramType)调用的也是addRule(String pattern, Rule
rule)方法,创建了SetNextRule(methodName, paramType)对象

4、只有digester.addRuleSet(RuleSet ruleSet)不同,这个需要专门分析。

所以可以理解为createStartDigester()中第5部分只执行了

digester.addRule(String pattern, Rule rule)

digester.addRuleSet(RuleSet ruleSet)

两种操作。

所关注的Rule也集中在:ObjectCreateRule、SetPropertiesRule、SetNextRule这三个类上。

细跟digester.addRule(String pattern, Rule rule)源码,如下:

    /**
     * <p>Register a new Rule matching the specified pattern.
     * This method sets the <code>Digester</code> property on the rule.</p>
     *
     * @param pattern Element matching pattern
     * @param rule Rule to be registered
     */
    public void addRule(String pattern, Rule rule) {

        rule.setDigester(this);
        getRules().add(pattern, rule);

    }

相应的 getRules()源码:

    /**
     * Return the <code>Rules</code> implementation object containing our
     * rules collection and associated matching policy.  If none has been
     * established, a default implementation will be created and returned.
     */
    public Rules getRules() {

        if (this.rules == null) {
            this.rules = new RulesBase();
            this.rules.setDigester(this);
        }
        return (this.rules);

    }

可以发现pattern和rule的映射关系是通过RulesBase()进行管理的;

继续查看RulesBase的add(String pattern, Rule rule)源码:

    /**
     * Register a new Rule instance matching the specified pattern.
     *
     * @param pattern Nesting pattern to be matched for this Rule
     * @param rule Rule instance to be registered
     */
    public void add(String pattern, Rule rule) {
        // to help users who accidently add '/' to the end of their patterns
        int patternLength = pattern.length();
        if (patternLength>1 && pattern.endsWith("/")) {
            pattern = pattern.substring(0, patternLength-1);
        }


        List list = (List) cache.get(pattern);
        if (list == null) {
            list = new ArrayList();
            cache.put(pattern, list);
        }
        list.add(rule);
        rules.add(rule);
        if (this.digester != null) {
            rule.setDigester(this.digester);
        }
        if (this.namespaceURI != null) {
            rule.setNamespaceURI(this.namespaceURI);
        }

    }

从中可以看到pattern和rule的映射关系是一对多的,且通过map方式进行管理的(cache实际上是map类型的),例如pattern为”Server”时,会对应三个Rule;而且RulesBase中会单独维护rules(额外的List对象)去记录所有添加的rule。

到这里,可以发现pattern的存在至关重要,全部都是Server开头,例如“Server/GlobalNamingResources”,“Server/Service/Connector”等,这些都会对应到server.xml的各个元素节点的层级关系。

现在可以结合上一篇文章的内容,分析出tomcat利用SAX解析server.xml时,在触发例如startElement的回调函数时,通过传入localName和qname参数生成match(例如:参数“Server”)取出对应的所有rules,并分别执行begin方法。

具体以下以解析pattern为”Server”的执行过程为例;

首先在Catalina类的createStartDigester()中执行了

        // Configure the actions we will be using
        digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        digester.addSetProperties("Server");
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");

实际是在pattern为”Server”中设置了3个Rule,他们分别是ObjectCreateRule(className,
attributeName),SetPropertiesRule(),SetNextRule(methodName, paramType);

当Catalina的load()函数执行到digester.parse(inputSource);时,通过SAX解析server.xml后,遇到开始节点<Server>,并在开始解析时触发Digester类的startElement方法。

在startElement方法中生成match=“Server”;即xml中的元素节点就是pattern,它们之间的对应关系生成规则可参考Digester类的startElement源码:

        // Compute the current matching rule
        StringBuffer sb = new StringBuffer(match);
        if (match.length() > 0) {
            sb.append('/');
        }
        sb.append(name);
        match = sb.toString();

match是全局变量,每次修改都是基于上一次的match;现在得到match及传入的参数namespaceURI就可以从RulesBase类中找到对应的Rules

留下评论

网站地图xml地图