Tomcat 解析servlet.xml 源码学习
2018-01-04 本文已影响29人
jwfy
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的调用方还是?