Digester在tomcat中的作用是对conf下的server.xml文件进行实例化,其是从Catalian这个组件开始,创建Digester实例,再添加对应的规则,然后将其实例化,通过setServer方法,将其实例话的对象作为当前Catalian实例的句柄。这样就实现了对象句柄之间的关联引用,从而实现整个平台的递进启动。
规则的添加实在Catalia.java的load()方法之中。规则主要是根据各个标签创建对应对象的规则,以及解析对象的通过何种方法设为相应句柄属性。其主要实现过程是创建Digester实例,设置规则
protected Digester createStartDigester() {
long t1=System.currentTimeMillis();
//创建一个digester实例
Digesterdigester = new Digester();
//是否需要验证xml文档的合法性,false表示不需要进行DTD规则校验
digester.setValidating(false);
//是否需要进行节点设置规则校验
digester.setRulesValidation(true);
//将xml节点中的className作为假属性,不用调用默认的setter方法
//在解析时,调用相应对象的setter方法来设置属性值,setter的参数就是节点属性,
//而有className的话,则直接使用className来直接实例化对象
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);
//遇到xml中Server节点,就创建一个StandardServer对象注意在这里只是添加了这个规则
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
//根据Server节点中的属性信息,调用属性的setter方法,比如说server节点中会有port=“8080”属性,则会调用setPort方法
digester.addSetProperties("Server");
//在上面的load方法中有个digester.push(this),this对象就是栈顶了
//这里将Server节点对应的对象作为参数,调用this对象,也就是Catalina对象的setServer方法
//意思即将addObjectCreate 在解析后的对象通过this在digester.push(this)中通过setServer方法注入当前server对象
//注意这里只是添加规则
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
//Server节点下的GlobalNamingResources节点,创建一个NamingResource对象
digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
//Server下的Listener节点
digester.addObjectCreate("Server/Listener",
null, // MUST bespecified in the element
"className");
digester.addSetProperties("Server/Listener");
digester.addSetNext("Server/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
//Server下的Service节点
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");
//Service节点下的Listener节点
digester.addObjectCreate("Server/Service/Listener",
null, // MUST bespecified 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");
//给Connector添加规则,就是当遇到Connector的时候,会调用ConnectorCreateRule里面定义的规则
//跟上面的作用是一样的,只不过该节点的规则比较多,就创建一个规则类
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 bespecified in the element
"className");
digester.addSetProperties("Server/Service/Connector/Listener");
digester.addSetNext("Server/Service/Connector/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
// AddRuleSets 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/"));
// Whenthe '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);
}
将当前catalina压入栈顶,stack 是一个ArrayStack实例
digester.push(this);
具体代码如下:
如果stack的大小为0,则将当前对象赋值给root,这样做的目的是在解析之后,能够直接根据root句柄,返回当前对象
public void push(Object object) {
if (stack.size() == 0) {
root = object;
}
stack.push(object);
}
public Object parse(InputSource input) throws IOException,SAXException {
configure();
getXMLReader().parse(input);
return (root);
}
在解析xml直接首先要获取的xml阅读器,在这里获取的是,其过程是通过getParser方法获取对应的SAXParserImpl工厂,然后调用SAXParserImpl实例的newSAXParser方法,创建SAXParserImpl实例,然后设置相关属性
public XMLReader getXMLReader() throws SAXException{
if (reader == null) {
reader =getParser().getXMLReader();
}
reader.setDTDHandler(this);
reader.setContentHandler(this);
if (entityResolver== null) {
reader.setEntityResolver(this);
} else {
reader.setEntityResolver(entityResolver);
}
reader.setProperty("http://xml.org/sax/properties/lexical-handler", this);
reader.setErrorHandler(this);
return reader;
}
根据上述代码,可以知道getXMLReader().parse(input);实际上调用的是SAXParserImpl中的 parse方法对input资源进行解析。方法如下:
public void parse(InputSource inputSource)
throws SAXException,IOException {
if (fSAXParser != null && fSAXParser.fSchemaValidator!= null) {
if (fSAXParser.fSchemaValidationManager!= null) {
fSAXParser.fSchemaValidationManager.reset();
fSAXParser.fUnparsedEntityHandler.reset();
}
resetSchemaValidator();
}
super.parse(inputSource);
}
由上看出其继续调用父类AbstractSAXParser的parse方法,在这个父类方法,其主要将资源文件转化为了XMLInputSource,设置其相关属性,而后调用其重载方法,对XMLInputSource进行解析最终经过一系列转化调用Digester的startDocument方法。这个方法主要是设置了一下编码。在startDocument之后继续开始扫描文档,主要方法是scanDocument,开始对整个文档开始进行解析,方法如下:
public boolean scanDocument(boolean complete)
throws IOException, XNIException {
fEntityManager.setEntityHandler(this);
int event =next();
do {
switch (event) {
case XMLStreamConstants.START_DOCUMENT:
break;
case XMLStreamConstants.START_ELEMENT:
break;
case XMLStreamConstants.CHARACTERS :
fDocumentHandler.characters(getCharacterData(),null);
break;
case XMLStreamConstants.SPACE:
break;
case XMLStreamConstants.ENTITY_REFERENCE:
break;
case XMLStreamConstants.PROCESSING_INSTRUCTION:
fDocumentHandler.processingInstruction(getPITarget(),getPIData(),null);
break;
case XMLStreamConstants.COMMENT :
fDocumentHandler.comment(getCharacterData(),null);
break;
case XMLStreamConstants.DTD :
break;
case XMLStreamConstants.CDATA:
fDocumentHandler.startCDATA(null);
//xxx: checkif CDATA values comes from getCharacterData() function
fDocumentHandler.characters(getCharacterData(),null);
fDocumentHandler.endCDATA(null);
//System.out.println("in CDATA of the XMLNSDocumentScannerImpl");
break;
case XMLStreamConstants.NOTATION_DECLARATION:
break;
case XMLStreamConstants.ENTITY_DECLARATION:
break;
case XMLStreamConstants.NAMESPACE :
break;
case XMLStreamConstants.ATTRIBUTE :
break;
case XMLStreamConstants.END_ELEMENT:
break;
default :
throw new InternalError("processingevent: " + event);
}
event = next();
} while (event!=XMLStreamConstants.END_DOCUMENT&& complete);
if(event ==XMLStreamConstants.END_DOCUMENT) {
fDocumentHandler.endDocument(null);
return false;
}
return true;
}
开始调用startElement对元素开始解析,先拼接模式然后获取其对应的规则,遍历所有规则,调用其对应规则实例的begin方法,这要求所有规则实现抽象类Rule,规则的添加在上文解析过程中。
public void startElement(String namespaceURI, String localName, String qName, Attributes list)
throws SAXException {
list = updateAttributes(list);
bodyTexts.push(bodyText);
bodyText = new StringBuilder();
String name = localName;
if ((name == null) || (name.length() < 1)) {
name = qName;
}
StringBuilder sb = new StringBuilder(match);
if (match.length() > 0) {
sb.append('/');
}
sb.append(name);
match = sb.toString();
if (debug) {
log.debug(" New match='" + match + "'");
}
List<Rule> rules = getRules().match(namespaceURI, match);
matches.push(rules);
if ((rules != null) && (rules.size() > 0)) {
for (int i = 0; i < rules.size(); i++) {
Rule rule = rules.get(i);
rule.begin(namespaceURI, name, list);
}
}
根据上文的解析规则与过程,下面介绍一些对哪些对象做了解析
对应规则:
===============================================================
创建对象是根据realClassName,根据类加载器创建其对应的实例,然后将这个实例给压入digester的栈中,在这里有必要解释一下attributes这个属性的集合来自于配置文件, getValue这个方法是根据attributeName==》className来获取对应的类名,这些值来自于server.xml中的解析,所以可以看出如果xml中存在,则优先使用xml中的值。只是默认server.xml中为空
@Override
public void begin(Stringnamespace, String name, Attributes attributes)
throws Exception {
String realClassName = className;
if (attributeName!= null) {
String value =attributes.getValue(attributeName);
if (value != null) {
realClassName = value;
}
}
……………………
Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
Object instance =clazz.newInstance();
digester.push(instance);
}
===============================================================
当前方法主要是对属性进行规则验证,如果需要进行规则验证,且其是一个不合法的属性,则输出警告日志。
public void begin(String namespace, String theName,Attributes attributes)
throws Exception {
// Populate thecorresponding properties of the top object
Object top = digester.peek();
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.isFakeAttribute(top,name)
&& !IntrospectionUtils.setProperty(top,name, value)
&& digester.getRulesValidation()){
digester.log.warn("[SetPropertiesRule]{"+ digester.match +
"}Setting property '" + name + "' to '" +
value + "' didnot find a matching property.");
}
}
}
===============================================================
这个方法的begin什么事情都没有做
这部分方法没有进行任何处理
=============================================================
SetNextRule:
SetNextRule[methodName=,paramType=org.apache.catalina.LifecycleListener]
该方法是在标签元素结束的时候调用,获取当前对象以及其父级对象,然后根据方法名和参数类型调用调用父类方法,将当前实例注入作为其句柄属性。
public void end(String namespace, String name) throws Exception {
Object child = digester.peek(0);
Object parent = digester.peek(1);
IntrospectionUtils.callMethod1(parent,methodName,
child, paramType, digester.getClassLoader());
}
===============================================================
这个方法的end什么事情都没有做
==============================================================
这个方法是将当前实例元素给移除栈顶
public void end(String namespace, String name) throws Exception { Object top = digester.pop(); }
===============================================================
[className=org.apache.catalina.core.StandardServer,attributeName=className]
其创建了一个StandardServer对象
此时stack栈中的集合:
Catalina@1590
StandardServer@1788
===============================================================
SetNextRule[methodName=setServer,paramType=org.apache.catalina.Server]
默认实现方法中begin方法什么也没有做
===============================================================
SetPropertiesRule[]
验证属性是否符合规范并注入相应的值,在这里给StandardServer注入了port=8005shutdown=SHUTDOWN
begin
===============================================================
ObjectCreateRule[className=null, attributeName=className]
attributeName=>org.apache.catalina.startup.VersionLoggerListener
从而创建对应实例然后压入到stack栈:
Catalina@1590
StandardServer@1788
VersionLoggerListener@1974
begin
===============================================================
SetPropertiesRule[]
这里并没有什么属性设置到当前实例
begin
===============================================================
SetNextRule[methodName=addLifecycleListener,paramType=org.apache.catalina.LifecycleListener]
默认实现方法中begin方法什么也没有做
end
===============================================================
SetNextRule[methodName=addLifecycleListener,paramType=org.apache.catalina.LifecycleListener]
调用StandardServer@1788这个实例中的addLifecycleListener
(LifecycleListener lifecycleListener)方法,将VersionLoggerListener添加到其句柄lifecycle这个LifecycleSupport实例中去
end
===============================================================
SetPropertiesRule[]
这里什么也没有做
end
===============================================================
ObjectCreateRule[className=null, attributeName=className]
将栈顶元素从stack中移除,这里移除的是VersionLoggerListener@1974
实例,所以此时栈顶元素
Catalina@1590
StandardServer@1788
相同原理依次加入
AprLifecycleListener
JreMemoryLeakPreventionListener
GlobalResourcesLifecycleListener
ThreadLocalLeakPreventionListener
begin
=============================================================
ObjectCreateRule[className=org.apache.catalina.core.StandardService,attributeName=className]
创建StandardService实例压入到栈中,此时栈中的元素:
Catalina@1590
StandardServer@1788
StandardService
begin
============================================================
SetPropertiesRule[]
设置属性值,这里将其name设置为catalina
begin
===============================================================
SetNextRule[methodName=addService,paramType=org.apache.catalina.Service]