Tomcat 解析servlet.xml 源码学习

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 源码学习)