tomcat源码解读一 Digester的解析方式
来源:互联网 发布:djay pro 破解版 mac 编辑:程序博客网 时间:2024/06/05 09:48
1 Digester
Digester在tomcat中的作用是对conf下的server.xml文件进行实例化,其是从Catalian这个组件开始,创建Digester实例,再添加对应的规则,然后将其实例化,通过setServer方法,将其实例话的对象作为当前Catalian实例的句柄。这样就实现了对象句柄之间的关联引用,从而实现整个平台的递进启动。
1.1 UML类图
1.2 UML时序图
1.3 规则添加解析
1.3.1 添加对应解析规则
规则的添加实在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);}
1.3.2 注入栈顶对象
将当前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);}
1.3.3 解析标签创建实例
在解析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); } }
根据上文的解析规则与过程,下面介绍一些对哪些对象做了解析
1.3.4 Rule实现类begin方法
对应规则:
1.3.4.1 ObjectCreateRule的begin方法
===============================================================
创建对象是根据realClassName,根据类加载器创建其对应的实例,然后将这个实例给压入digester的栈中,在这里有必要解释一下attributes这个属性的集合来自于配置文件, getValue这个方法是根据attributeName==》className来获取对应的类名,这些值来自于server.xml中的解析,所以可以看出如果xml中存在,则优先使用xml中的值。只是默认server.xml中为空
@Overridepublic 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);}
1.3.4.2 SetPropertiesRule
===============================================================
当前方法主要是对属性进行规则验证,如果需要进行规则验证,且其是一个不合法的属性,则输出警告日志。
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."); } }}
1.3.4.3 SetNextRule
===============================================================
这个方法的begin什么事情都没有做
1.3.5 Rule实现类的body方法
这部分方法没有进行任何处理
1.3.6 Rule实现类的end方法
=============================================================
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());}
1.3.6.1 SetNextRule
===============================================================
这个方法的end什么事情都没有做
1.3.6.2 ObjectCreateRule
==============================================================
这个方法是将当前实例元素给移除栈顶
public void end(String namespace, String name)throws Exception {
Object top = digester.pop();
}
1.3.7 标签解析值
1.3.7.1 Server标签
===============================================================
[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
1.3.7.2 Server/Listener标签
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
1.3.7.3 Server/Service标签
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]
1.3.7.4 Server/Service/Connector标签
1.3.7.5 Server/Service/Engine标签
1.3.7.6 Server/Service/Realm标签
1.3.7.7 Server/Service/Host标签
- tomcat源码解读一 Digester的解析方式
- tomcat源码中Digester解析
- tomcat解析(五)Digester(一)
- Tomcat源码的catalina中利用Digester解析conf/server.xml
- Tomcat源码的catalina中利用Digester解析conf/server.xml
- 理解Tomcat(一) 利用Digester解析xml文件
- tomcat源码阅读之Digester
- tomcat源码解析(三)——Digester类源码解析及Rule分析
- Tomcat中xml的解析器Digester 第二篇(共三篇)
- tomcat源码解读二 tomcat的生命周期
- Tomcat源码阅读四:Digester工具
- tomcat源码阅读(二) Digester方法详解
- Tomcat源码解析一(概论)
- tomcat源码解析(一)
- tomcat解析(六)Digester(二)startElement
- Tomcat中Digester解析server.xml文件
- 【Tomcat】Tomcat源码解读系列(一)——server.xml文件的配置
- Tomcat 8(八)Tomcat解析server.xml的工具—Digester
- HBase之HRegionServer处理put请求
- 技术分享连载(四十八)
- Opencv cvCircle()函数
- 服务器常见错误代码500、501、502、503、504、505
- 《高性能iOS 应用开发》之降低你 APP 的电量消耗
- tomcat源码解读一 Digester的解析方式
- window.onload()函数解析
- 如何理解 4G LTE 网络不能承载语音通话,只能提供数据服务?
- mysql explain type
- expect安装以及进程查杀重启
- 容易被大多数人忽视的STM32串口DMA问题
- solr5以上版本配置到tomcat运行
- Centos6.9 安装zabbix-server
- Effective C++ 44. Factor parameter-independent code out of templates