Tomcat 解析servlet.xml 源码学习

作者: jwfy | 来源:发表于2018-01-04 23:08 被阅读29次

    digester 学习

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

    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(instance);
    }
    

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

    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(); 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 + "'");
           }
           
           // 到这就获取到了具体属性的名称和其内容,调用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方法。

    addCallMethod

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

    调用栈顶元素的方法

    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(0);
       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 + ")");
           }
       }
    
       // 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以及组件初始化操作

    StandardServer

    // 解析xml的规则
    digester.push(catalina);  // 先往栈中插入catalina本身
    
    digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer","className");
    // 创建一个新的对象,StandardServer
    digester.addSetProperties("Server");
    // 初始化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对象的初始化以及赋值

    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组件中的目的

    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(listener);
    
    // c 是指的StandardContext,创建一个ContextConfig(监听器)实例,然后添加至c中
    

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

    总结

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

    相关文章

      网友评论

        本文标题:Tomcat 解析servlet.xml 源码学习

        本文链接:https://www.haomeiwen.com/subject/kzndnxtx.html