在这篇文章中主要针对tomcat源码中Rule部分的解析;这部分功能主要涉及server.xml文件加载和tomcat容器中各组件初始化的过程。在我之前的文章中《tomcat源码解析(一)——Bootstrap和Catalina启动部分》和《tomcat源码解析(二)——xml解析过程分析》分别对启动过程和xml解析进行了分析。
之前的文章中遗留了几个问题,将在这篇文章中进行解答;问题如下:
1、第一篇文章启动过程中Catalina类createStartDigester中设置过程及原因。
2、第二篇文章中关于rule,调用的
rule.begin(namespaceURI, name, list);
rule.body(namespaceURI, name, bodyText);
rule.end(namespaceURI, name);
rule.finish();
的具体过程和分析方法和Digester类的stack作用。
如果对tomcat使用SAX解析xml已经了解的可以较好理解本文的思路。
对于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)对象</