在研究Catalina之前,首先轉一下createStartDegester的解析說明,這是對Tomcat配置文件server.xml的解析並初始化到Tomcat中。本來想自己研究一番,再寫閱讀心得上來。但發現這個解析過程也是比較複雜,涉及東西挺多的,然後搜了一下其他研究Tomcat源碼大神們的心得,發現這篇文章的確不錯。詳盡介紹瞭解析xml的過程,值得轉載一番。感謝holly2k這位大神的傑作。
tomcat解析(八)Catalina.createStartDigester
在tomcat解析(四)中我們講到了Catalina的load及start方法啓動及準備整個tomcat服務器,而這兩個方法最終又將該任務交由server的initialize及start方法處理,該變更將引用Server類的實例,但初始化時爲空,因此我們需要該對象實例化過程,而該過程盡在Catalina.load方法的第三步驟裏(可看tomcat解析四).首先我們需要了解一下其中的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、setClassLoader等方法設置其某些屬性,這裏不是本文的重點,因此不準備細講。
2.調用了其addObjectCreate、addSetProperties、addSetNext、addRule及addRuleSet等方法
還記得在之前我們講到Digester類的startElement等方法時,有一點講到該類根據標籤路徑名獲取到一年Rule類的List後,分別調用其begin、body及end方法,那個Rule List從何而來呢,答案即將揭曉,我們這裏將通過詳細講解Digester的addObjectCreate等方法來向你揭示這些Ruler的意義。因爲下面這些方法均調用多次,這裏將只舉例說明其作用。
如:
- digester.addObjectCreate("Server",
- "org.apache.catalina.core.StandardServer",
- "className");
方法內容如下:
- /**
- * Add an "object create" rule for the specified parameters.
- *
- * @param pattern Element matching pattern
- * @param className Default Java class name to be created
- * @param attr<mce:script type="text/javascript" src="http://hi.images.csdn.net/js/blog/tiny_mce/themes/advanced/langs/zh.js" mce_src="http://hi.images.csdn.net/js/blog/tiny_mce/themes/advanced/langs/zh.js"></mce:script><mce:script type="text/javascript" src="http://hi.images.csdn.net/js/blog/tiny_mce/plugins/syntaxhl/langs/zh.js" mce_src="http://hi.images.csdn.net/js/blog/tiny_mce/plugins/syntaxhl/langs/zh.js"></mce:script>ibuteName Attribute name that optionally overrides
- * the default Java class name to be created
- * @see ObjectCreateRule
- */
- public void addObjectCreate(String pattern, String className,
- String attributeName) {
- addRule(pattern,
- new ObjectCreateRule(className, attributeName));
- }
在這裏將調用addRule方法,該方法如下:
- /**
- * <p>Register a new Rule matching the specified pattern.
- * This method sets the <code>Digester</code> property on the rule.</p>
- *
- * @param pattern Element matching pattern
- * @param rule Rule to be registered
- */
- public void addRule(String pattern, Rule rule) {
- rule.setDigester(this);
- getRules().add(pattern, rule);
- }
先看getRules(),該方法如下:
- /**
- * Return the <code>Rules</code> implementation object containing our
- * rules collection and associated matching policy. If none has been
- * established, a default implementation will be created and returned.
- */
- public Rules getRules() {
- if (this.rules == null) {
- this.rules = new RulesBase();
- this.rules.setDigester(this);
- }
- return (this.rules);
- }
我們可以看到該方法用於實例化一個RuleBase類對象,並且可以瞭解該方法以後每一次調用都只返回第一次生成的對象,因此是一個單例對象,在getRule方法返回後又調用了add方法,此時調用的將是RuleBase.add方法,該方法如下
- /**
- * Register a new Rule instance matching the specified pattern.
- *
- * @param pattern Nesting pattern to be matched for this Rule
- * @param rule Rule instance to be registered
- */
- public void add(String pattern, Rule rule) {
- // to help users who accidently add '/' to the end of their patterns
- int patternLength = pattern.length();
- if (patternLength>1 && pattern.endsWith("/")) {
- pattern = pattern.substring(0, patternLength-1);
- }
- List list = (List) cache.get(pattern);
- if (list == null) {
- list = new ArrayList();
- cache.put(pattern, list);
- }
- list.add(rule);
- rules.add(rule);
- if (this.digester != null) {
- rule.setDigester(this.digester);
- }
- if (this.namespaceURI != null) {
- rule.setNamespaceURI(this.namespaceURI);
- }
- }
RuleBase類裏掛有一個變量名爲cache的HashMap,該Map將以pattern爲key,各種繼承Rule類對象組成的List爲value.我們再看一下在Digester.startElement裏有如下語句:
List rules = getRules().match(namespaceURI, match);
如上邊的解釋,getRule方法將返回單例的RuleBase對象,然後以標籤路徑名調用其match方法,該方法內容如下:
- /**
- * Return a List of all registered Rule instances that match the specified
- * nesting pattern, or a zero-length List if there are no matches. If more
- * than one Rule instance matches, they <strong>must</strong> be returned
- * in the order originally registered through the <code>add()</code>
- * method.
- *
- * @param namespaceURI Namespace URI for which to select matching rules,
- * or <code>null</code> to match regardless of namespace URI
- * @param pattern Nesting pattern to be matched
- */
- public List match(String namespaceURI, String pattern) {
- // List rulesList = (List) this.cache.get(pattern);
- List rulesList = lookup(namespaceURI, pattern);
- if ((rulesList == null) || (rulesList.size() < 1)) {
- // Find the longest key, ie more discriminant
- String longKey = "";
- Iterator keys = this.cache.keySet().iterator();
- while (keys.hasNext()) {
- String key = (String) keys.next();
- if (key.startsWith("*/")) {
- if (pattern.equals(key.substring(2)) ||
- pattern.endsWith(key.substring(1))) {
- if (key.length() > longKey.length()) {
- // rulesList = (List) this.cache.get(key);
- rulesList = lookup(namespaceURI, key);
- longKey = key;
- }
- }
- }
- }
- }
- if (rulesList == null) {
- rulesList = new ArrayList();
- }
- return (rulesList);
- }
該方法將主要以調用lookup方法來完成獲取對應Rule List的工作,lookup方法如下:
- /**
- * Return a List of Rule instances for the specified pattern that also
- * match the specified namespace URI (if any). If there are no such
- * rules, return <code>null</code>.
- *
- * @param namespaceURI Namespace URI to match, or <code>null</code> to
- * select matching rules regardless of namespace URI
- * @param pattern Pattern to be matched
- */
- protected List lookup(String namespaceURI, String pattern) {
- // Optimize when no namespace URI is specified
- List list = (List) this.cache.get(pattern);
- if (list == null) {
- return (null);
- }
- if ((namespaceURI == null) || (namespaceURI.length() == 0)) {
- return (list);
- }
- // Select only Rules that match on the specified namespace URI
- ArrayList results = new ArrayList();
- Iterator items = list.iterator();
- while (items.hasNext()) {
- Rule item = (Rule) items.next();
- if ((namespaceURI.equals(item.getNamespaceURI())) ||
- (item.getNamespaceURI() == null)) {
- results.add(item);
- }
- }
- return (results);
- }
我們可以看到,該方法將以標籤路徑名pattern爲key值,從cache中取出對應的Rule List,如果我們看一下Catalina.createStartDigester各方法調用時的傳入的pattern值,你會發現是到server.xml一一匹配的,據此我們可大概地理解到這個框架的實現方式:
1.實例化Digester
2.以要解析的xml的結構爲準,調用Digester類各add方法加入對應的Rule.
3.調用Digester.parse方法解析xml文件,則解析每一個標籤時對每一個解析動作將觸發對應Rule的begin、body及end方法。
儘管理解了該框架,但我們仍然需要看一下在Catalina.createStartDigester方法加入了哪些Rule來確定在解析該文件時服務器做了多少事及這些事對整個服務器的啓動有何意義。下面我們先看一下其中幾個比較重要的調用,如
1.addObjectCreate,有如下的調用
- digester.addObjectCreate("Server",
- "org.apache.catalina.core.StandardServer",
- "className");
所加入的Rule實現類爲ObjectCreateRule,該類有begin方法如下
- /**
- * Process the beginning of this element.
- *
- * @param attributes The attribute list of this element
- */
- public void begin(Attributes attributes) throws Exception {
- // Identify the name of the class to instantiate
- String realClassName = className;
- if (attributeName != null) {
- String value = attributes.getValue(attributeName);
- if (value != null) {
- realClassName = value;
- }
- }
- if (digester.log.isDebugEnabled()) {
- digester.log.debug("[ObjectCreateRule]{" + digester.match +
- "}New " + realClassName);
- }
- // Instantiate the new object and push it on the context stack
- Class clazz = digester.getClassLoader().loadClass(realClassName);
- Object instance = clazz.newInstance();
- digester.push(instance);
- }
可以看到該方法內容爲實例化一個類,類名可爲實例化該類時初始化的className對象或在調用其begin方法時傳入的Attribute對象(該對象用以表示標籤的屬性)裏className對應的類名,實例化類後又調用digester.push()方法,我們在tomcat解析四中已看到過該方法,該方法會在digester.parse之前調用,參數爲已實例化的Catalina對象。
該類又有end方法如下:
- /**
- * Process the end of this element.
- */
- public void end() throws Exception {
- Object top = digester.pop();
- if (digester.log.isDebugEnabled()) {
- digester.log.debug("[ObjectCreateRule]{" + digester.match +
- "} Pop " + top.getClass().getName());
- }
- }
可以看到在解析Server的開始標籤時將產生一個org.apache.catalina.core.StandardServer實例,並將之放到棧中,在結束標籤時又將它從棧中取,之所以放於棧中是因爲後續還要對該對象進行很多處理,如下面將講到的
2.addSetProperties
這個方法將加入的Rule實現類爲SetPropertiesRule,這裏先不細講該類啦
3.addSetNext,如
該方法將添加的實現類爲SetNextRule類,該類沒有begin方法,因此在解析開始標籤的時候沒有對應的動作,但該類有end()方法,該方法是在解析結束標籤的事件方法endElement()裏調用的,SetNextRule類的end()方法如下:
- /**
- * Process the end of this element.
- */
- public void end() throws Exception {
- // Identify the objects to be used
- 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());
- }
該方法內容爲:取出stack頂部的對象,再取出其前一個對象,然後調用前一個對象的方法,方法名爲methodName,比如有如下的調用(事實是有的)
- digester.addSetNext("Server",
- "setServer",
- "org.apache.catalina.Server");
這段代碼將在解析Server標籤的時候觸發,此時在棧中的對象有兩個,一個是最先放入的Catalina對象,而另一個是在addSetNext調用前加入的ObjectCreateRule類所實例化的org.apache.catalina.core.StandardServer實例,因此此時調用Catalina.setServer方法,以StandardServer實例爲參數。這裏是一個非常巧妙的設計,通過相鄰的標籤來生成對應的對象,並且讓上一個對象作爲父對象持有另一對象,因此在加入每一個RULE的時候都必須精妙地設計加入的順序.
4.addRule,比如:
這個直接對某一個pattern加入對應的Rule,無也講
5.addRuleSet,如
- digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
addRuleSet代碼如下:
- /**
- * Register a set of Rule instances defined in a RuleSet.
- *
- * @param ruleSet The RuleSet instance to configure from
- */
- public void addRuleSet(RuleSet ruleSet) {
- String oldNamespaceURI = getRuleNamespaceURI();
- String newNamespaceURI = ruleSet.getNamespaceURI();
- if (log.isDebugEnabled()) {
- if (newNamespaceURI == null) {
- log.debug("addRuleSet() with no namespace URI");
- } else {
- log.debug("addRuleSet() with namespace URI " + newNamespaceURI);
- }
- }
- setRuleNamespaceURI(newNamespaceURI);
- ruleSet.addRuleInstances(this);
- setRuleNamespaceURI(oldNamespaceURI);
- }
可以看到又調用了RuleSet實現類的addRuleInstances方法,以EngineRuleSet爲例,其addRuleInstances方法如下:
- /**
- * <p>Add the set of Rule instances defined in this RuleSet to the
- * specified <code>Digester</code> instance, associating them with
- * our namespace URI (if any). This method should only be called
- * by a Digester instance.</p>
- *
- * @param digester Digester instance to which the new Rule instances
- * should be added.
- */
- public void addRuleInstances(Digester digester) {
- digester.addObjectCreate(prefix + "Ejb",
- "org.apache.catalina.deploy.ContextEjb");
- digester.addRule(prefix + "Ejb", new SetAllPropertiesRule());
- digester.addRule(prefix + "Ejb",
- new SetNextNamingRule("addEjb",
- "org.apache.catalina.deploy.ContextEjb"));
- digester.addObjectCreate(prefix + "Environment",
- "org.apache.catalina.deploy.ContextEnvironment");
- digester.addSetProperties(prefix + "Environment");
- digester.addRule(prefix + "Environment",
- new SetNextNamingRule("addEnvironment",
- "org.apache.catalina.deploy.ContextEnvironment"));
- digester.addObjectCreate(prefix + "LocalEjb",
- "org.apache.catalina.deploy.ContextLocalEjb");
- digester.addRule(prefix + "LocalEjb", new SetAllPropertiesRule());
- digester.addRule(prefix + "LocalEjb",
- new SetNextNamingRule("addLocalEjb",
- "org.apache.catalina.deploy.ContextLocalEjb"));
- digester.addObjectCreate(prefix + "Resource",
- "org.apache.catalina.deploy.ContextResource");
- digester.addRule(prefix + "Resource", new SetAllPropertiesRule());
- digester.addRule(prefix + "Resource",
- new SetNextNamingRule("addResource",
- "org.apache.catalina.deploy.ContextResource"));
- digester.addObjectCreate(prefix + "ResourceEnvRef",
- "org.apache.catalina.deploy.ContextResourceEnvRef");
- digester.addRule(prefix + "ResourceEnvRef", new SetAllPropertiesRule());
- digester.addRule(prefix + "ResourceEnvRef",
- new SetNextNamingRule("addResourceEnvRef",
- "org.apache.catalina.deploy.ContextResourceEnvRef"));
- digester.addObjectCreate(prefix + "ServiceRef",
- "org.apache.catalina.deploy.ContextService");
- digester.addRule(prefix + "ServiceRef", new SetAllPropertiesRule());
- digester.addRule(prefix + "ServiceRef",
- new SetNextNamingRule("addService",
- "org.apache.catalina.deploy.ContextService"));
- digester.addObjectCreate(prefix + "Transaction",
- "org.apache.catalina.deploy.ContextTransaction");
- digester.addRule(prefix + "Transaction", new SetAllPropertiesRule());
- digester.addRule(prefix + "Transaction",
- new SetNextNamingRule("setTransaction",
- "org.apache.catalina.deploy.ContextTransaction"));
- }
可以得知addRuleSet方法主要是用於爲某一想同前輟的標籤加入一批的Rule實現類
我們沒有篇幅來說明tomcat做的每一件事,因此這裏我將影響到後續服務器啓動的一些Rule拿出來講一下。
1.對<Server>標籤
- // 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");
實例化了一個org.apache.catalina.core.StandardServer,並以該對象爲參數調用Catalina類的setServer方法,在這裏我們可以回想一下在tomcat解析四裏我們說的server變量所引用的對象是如何得到的呢?該對象有一個設置方法,而該方法即是setServer,因此後續整個服務器的啓動重任將落於剛剛新生的org.apache.catalina.core.StandardServer對象
2.對Service標籤
- digester.addObjectCreate("Server/Service",
- "org.apache.catalina.core.StandardService",
- "className");
- digester.addSetProperties("Server/Service");
- digester.addSetNext("Server/Service",
- "addService",
- "org.apache.catalina.Service");
實例化了一個org.apache.catalina.core.StandardService對象,放入棧裏,並以自身爲參數調用棧裏上一個對象的addService方法,通過server.xml我們可以瞭解到<service>標籤是位於<server>標籤之內的,因此此時StandardService的上一個對象爲StandardServer對象(該對象要到結束標籤時纔會被取出來,這又是一個很巧妙的設計,讓xml文件裏有上下級關係的兩個對象實例化後又存在上下級關係,或者是互持有的關係)
3.對Engine標籤有:
- digester.addRuleSet(new EngineRuleSet("Server/Service/"));
將會加下以下動作:
- /**
- * <p>Add the set of Rule instances defined in this RuleSet to the
- * specified <code>Digester</code> instance, associating them with
- * our namespace URI (if any). This method should only be called
- * by a Digester instance.</p>
- *
- * @param digester Digester instance to which the new Rule instances
- * should be added.
- */
- public void addRuleInstances(Digester digester) {
- digester.addObjectCreate(prefix + "Engine",
- "org.apache.catalina.core.StandardEngine",
- "className");
- digester.addSetProperties(prefix + "Engine");
- digester.addRule(prefix + "Engine",
- new LifecycleListenerRule
- ("org.apache.catalina.startup.EngineConfig",
- "engineConfigClass"));
- digester.addSetNext(prefix + "Engine",
- "setContainer",
- "org.apache.catalina.Container");
- //Cluster configuration start
- digester.addObjectCreate(prefix + "Engine/Cluster",
- null, // MUST be specified in the element
- "className");
- digester.addSetProperties(prefix + "Engine/Cluster");
- digester.addSetNext(prefix + "Engine/Cluster",
- "setCluster",
- "org.apache.catalina.Cluster");
- //Cluster configuration end
- digester.addObjectCreate(prefix + "Engine/Listener",
- null, // MUST be specified in the element
- "className");
- digester.addSetProperties(prefix + "Engine/Listener");
- digester.addSetNext(prefix + "Engine/Listener",
- "addLifecycleListener",
- "org.apache.catalina.LifecycleListener");
- digester.addRuleSet(new RealmRuleSet(prefix + "Engine/"));
- digester.addObjectCreate(prefix + "Engine/Valve",
- null, // MUST be specified in the element
- "className");
- digester.addSetProperties(prefix + "Engine/Valve");
- digester.addSetNext(prefix + "Engine/Valve",
- "addValve",
- "org.apache.catalina.Valve");
- }
內容有:實例化org.apache.catalina.core.StandardEngine對象,實例化org.apache.catalina.startup.EngineConfig對象,以該對象爲參數,調用StandardEngine的addLifecycleListener方法,以StandardEngine對象爲參數,調用StandardService.setContainer方法
4.對Host標籤
內容有:內容有:實例化org.apache.catalina.core.StandardHost對象,實例化org.apache.catalina.startup.HostConfig對象,以該對象爲參數,調用StandardHost的addLifecycleListener方法,以StandardHost對象爲參數,調用StandardEngine.addChild方法
5.對Context標籤
內容有:內容有:實例化org.apache.catalina.core.StandardContext對象,實例化org.apache.catalina.startup.ContextConfig對象,以該對象爲參數,調用StandardContext的addLifecycleListener方法,以StandardContext對象爲參數,調用StandardHost.addChild方法
方法總結:自上而下地實例了一些tomcat啓動及處理客戶請求的處理類,其中有StandardHost(表示着一個http訪問的主機)、HostConfig、StandardContext(java web中的虛擬目錄)及ContextConfig等,我們後續將會介紹各個主要的啓動類
轉載:http://blog.csdn.net/holly2k/article/details/5258849