在前面的文章中,如果我們要啟動(dòng)tomcat容器,我們需要使用Bootstrap類來實(shí)例化連接器、servlet容器、Wrapper實(shí)例和其他組件,然后調(diào)用各個(gè)對(duì)象的set方法將它們關(guān)聯(lián)起來;這種配置應(yīng)用程序的方法有一個(gè)明顯的缺陷,即所有的配置都必須硬編碼。調(diào)整組件配置和屬性值都必須要重新編譯Bootstrap類。幸運(yùn)的是,Tomcat的設(shè)計(jì)者使用了一種更加優(yōu)雅的配置方式,即使用一個(gè)名為server.xml的XML文件來對(duì)應(yīng)用程序進(jìn)行配置。server.xml文件中的每個(gè)元素都會(huì)轉(zhuǎn)換為一個(gè)java對(duì)象,元素的屬性會(huì)用于設(shè)置java對(duì)象的屬性,這樣,就可以通過簡(jiǎn)單的編輯server.xml文件來修改tomcat的配置。
Tomcat使用了開源庫Digester來將xml文件中的元素轉(zhuǎn)換成java對(duì)象。
由于一個(gè)Context實(shí)例表示一個(gè)Web應(yīng)用程序,因此配置Web應(yīng)用程序是通過對(duì)已經(jīng)實(shí)例化的Context實(shí)例進(jìn)行配置完成的。用來配置Web應(yīng)用程序的XML文件的名稱是web.xml,該文件位于Web應(yīng)用程序的WEB-INF目錄下。
下面來介紹Digester庫,Digester庫是Apache軟件基金會(huì)的Jatarta項(xiàng)目下的子Commons項(xiàng)目下的一個(gè)開源項(xiàng)目,它的主頁地址是http://commons.apache.org/proper/commons-digester/
org.apache.commons.digester3.Digester類是Digester庫中的主類,該類可用于解析XML文件,對(duì)于XML文件中的每個(gè)元素,Digester對(duì)象都會(huì)檢查它是否要做事先預(yù)定義的事件,在調(diào)用Digester對(duì)象的parse()方法之前,程序員要先定義好Digester對(duì)象執(zhí)行哪些動(dòng)作。
因此,程序員要先定義好模式,然后將每個(gè)模式與一條或多條規(guī)則相關(guān)聯(lián)。
模式通常是xml文件里面元素的路徑,類似于xpath的語法路徑
規(guī)則指明了當(dāng)Digester對(duì)象遇到了某個(gè)特殊的模式時(shí)要執(zhí)行的一個(gè)或多個(gè)動(dòng)作,規(guī)則是org.apache.commons.digester3.Rule類的實(shí)例,Digester類開源包含0個(gè)或多個(gè)Rule對(duì)象,在Digester實(shí)例中,這些規(guī)則和其相關(guān)聯(lián)的模式都存儲(chǔ)在由org.apache.commons.digester3.Rules接口表示的一類存儲(chǔ)器中,每當(dāng)把一條規(guī)則添加到Digester實(shí)例中時(shí),Rule對(duì)象都會(huì)被添加到Rules對(duì)象中。
另外,Rule類有begin()方法和end()方法,在解析xml文件時(shí),當(dāng)Digester實(shí)例遇到匹配某個(gè)模式的元素的開始標(biāo)簽時(shí),它會(huì)調(diào)用相應(yīng)的Rule對(duì)象的begin()方法,而當(dāng)Digester實(shí)例遇到相應(yīng)元素的結(jié)束標(biāo)簽時(shí),它會(huì)調(diào)用Rule對(duì)象的end()方法。
在使用Digester庫時(shí),我們需要先導(dǎo)入相關(guān)依賴jar
< dependency > < groupId > org.apache.commons </ groupId > < artifactId > commons-digester3 </ artifactId > < version > 3.2 </ version > < classifier > with-deps </ classifier > </ dependency >
第一個(gè)示例應(yīng)用程序演示如何使用Digester庫動(dòng)態(tài)的創(chuàng)建對(duì)象,并設(shè)置相應(yīng)的屬性值。
employee1.xml文件內(nèi)容如下
<? xml version="1.0" encoding="ISO-8859-1" ?> < employee firstName ="Brian" lastName ="May" > </ employee >
我們需要根據(jù)上面的xml文件創(chuàng)建Employee對(duì)象,并設(shè)置相應(yīng)屬性,Employee類代碼如下:
public class Employee { private String firstName; private String lastName; private ArrayList offices = new ArrayList(); public Employee() { System.out.println( "Creating Employee" ); } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { System.out.println( "Setting firstName : " + firstName); this .firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { System.out.println( "Setting lastName : " + lastName); this .lastName = lastName; } public void addOffice(Office office) { System.out.println( "Adding Office to this employee" ); offices.add(office); } public ArrayList getOffices() { return offices; } public void printName() { System.out.println( "My name is " + firstName + " " + lastName); } }
現(xiàn)在寫一個(gè)測(cè)試類Test01,它使用Digester類,并為其添加創(chuàng)建Employee對(duì)象和設(shè)置其屬性的規(guī)則。
public class Test01 { public static void main(String[] args) { InputStream inputStream = null ; Digester digester = new Digester(); // add rules digester.addObjectCreate("employee","ex15.pyrmont.digestertest.Employee" ); digester.addSetProperties( "employee" ); digester.addCallMethod( "employee", "printName" ); try { inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("employee1.xml"); Employee employee = (Employee) digester.parse(inputStream); System.out.println( "First name : " + employee.getFirstName()); System.out.println( "Last name : " + employee.getLastName()); } catch (Exception e) { e.printStackTrace(); } finally { if (inputStream != null ) { try { inputStream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
第二個(gè)示例演示如何利用Digester庫創(chuàng)建兩個(gè)對(duì)象,并建立他們之間的關(guān)系
employee2.xml 文件內(nèi)容如下
<? xml version="1.0" encoding="ISO-8859-1" ?> < employee firstName ="Freddie" lastName ="Mercury" > < office description ="Headquarters" > < address streetName ="Wellington Avenue" streetNumber ="223" /> </ office > < office description ="Client site" > < address streetName ="Downing Street" streetNumber ="10" /> </ office > </ employee >
然后我們還需要?jiǎng)?chuàng)建Office類和Address類
Office類代碼如下:
public class Office { private Address address; private String description; public Office() { System.out.println( "..Creating Office" ); } public String getDescription() { return description; } public void setDescription(String description) { System.out.println( "..Setting office description : " + description); this .description = description; } public Address getAddress() { return address; } public void setAddress(Address address) { System.out.println( "..Setting office address : " + address); this .address = address; } }
Address類代碼如下:
public class Address { private String streetName; private String streetNumber; public Address() { System.out.println( "....Creating Address" ); } public String getStreetName() { return streetName; } public void setStreetName(String streetName) { System.out.println( "....Setting streetName : " + streetName); this .streetName = streetName; } public String getStreetNumber() { return streetNumber; } public void setStreetNumber(String streetNumber) { System.out.println( "....Setting streetNumber : " + streetNumber); this .streetNumber = streetNumber; } public String toString() { return "...." + streetNumber + " " + streetName; } }
下面是Test02類的定義,該類使用一個(gè)Digester對(duì)象,并為其添加規(guī)則
public class Test02 { public static void main(String[] args) { InputStream inputStream = null ; Digester digester = new Digester(); // add rules digester.addObjectCreate("employee" , "ex15.pyrmont.digestertest.Employee" ); digester.addSetProperties( "employee" ); digester.addObjectCreate( "employee/office" , "ex15.pyrmont.digestertest.Office" ); digester.addSetProperties( "employee/office" ); digester.addSetNext( "employee/office", "addOffice" ); digester.addObjectCreate( "employee/office/address" , "ex15.pyrmont.digestertest.Address" ); digester.addSetProperties( "employee/office/address" ); digester.addSetNext( "employee/office/address", "setAddress" ); try { inputStream = Thread.currentThread().getContextClassLoader() .getResourceAsStream( "employee2.xml"); Employee employee = (Employee) digester.parse(inputStream); ArrayList offices = employee.getOffices(); Iterator iterator = offices.iterator(); System.out .println( "-------------------------------------------------" ); while (iterator.hasNext()) { Office office = (Office) iterator.next(); Address address = office.getAddress(); System.out.println(office.getDescription()); System.out.println( "Address : " + address.getStreetNumber() + " " + address.getStreetName()); System.out.println( "--------------------------------" ); } } catch (Exception e) { e.printStackTrace(); } finally { if (inputStream != null ) { try { inputStream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
Rule類包含了一些方法,其中最重要的兩個(gè)方法是begin()方法和end()方法,當(dāng)Digester實(shí)例遇到某個(gè)XML元素的開始標(biāo)簽時(shí),它會(huì)調(diào)用它所包含的匹配Rule對(duì)象的begin()方法,方法簽名如下:
public void begin( String namespace, String name, Attributes attributes ) throws Exception
當(dāng)Digester實(shí)例遇到某個(gè)XML元素的結(jié)束標(biāo)簽時(shí),它會(huì)調(diào)用它所包含的匹配Rule對(duì)象的end()方法,方法簽名如下:
public void end( String namespace, String name ) throws Exception
Digester對(duì)象是如何完成這些工作的呢?當(dāng)調(diào)用Digester對(duì)象的addObjectCreate()方法、addCallMethod()方法、addSetNext()方法或其他方法時(shí),都會(huì)間接地調(diào)用Digester類的addRule()方法;該方法將一個(gè)Rule對(duì)象和它所匹配的模式添加到Digester對(duì)象的Rules集合中。
addRule()方法實(shí)現(xiàn)如下:
public void addRule( String pattern, Rule rule ) { rule.setDigester( this ); getRules().add( pattern, rule ); }
查看Digester類的addObjectCreate()方法的重載實(shí)現(xiàn)如下:
public void addObjectCreate( String pattern, String className ) { addRule( pattern, new ObjectCreateRule( className ) ); } public void addObjectCreate( String pattern, Class<?> clazz ) { addRule( pattern, new ObjectCreateRule( clazz ) ); } public void addObjectCreate( String pattern, String className, String attributeName ) { addRule( pattern, new ObjectCreateRule( className, attributeName ) ); } public void addObjectCreate( String pattern, String attributeName, Class<?> clazz ) { addRule( pattern, new ObjectCreateRule( attributeName, clazz ) ); }
這四個(gè)重載方法都調(diào)用了addRule()方法,ObjectCreateRule類是Rule類的子類,該類的實(shí)例可作為addRule()方法的第二個(gè)參數(shù)使用。
下面是ObjectCreateRule類的begin()方法和end()方法的實(shí)現(xiàn)
@Override public void begin( String namespace, String name, Attributes attributes ) throws Exception { Class <?> clazz = this .clazz; if ( clazz == null ) { // 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 ( getDigester().getLogger().isDebugEnabled() ) { getDigester().getLogger().debug( format( "[ObjectCreateRule]{%s} New '%s'" , getDigester().getMatch(), realClassName ) ); } // Instantiate the new object and push it on the context stack clazz = getDigester().getClassLoader().loadClass( realClassName ); } Object instance; if ( constructorArgumentTypes == null || constructorArgumentTypes.length == 0 ) { if ( getDigester().getLogger().isDebugEnabled() ) { getDigester() .getLogger() .debug( format( "[ObjectCreateRule]{%s} New '%s' using default empty constructor" , getDigester().getMatch(), clazz.getName() ) ); } instance = clazz.newInstance(); } else { if ( proxyManager == null ) { Constructor <?> constructor = getAccessibleConstructor( clazz, constructorArgumentTypes ); if ( constructor == null ) { throw new SAXException( format( "[ObjectCreateRule]{%s} Class '%s' does not have a construcor with types %s" , getDigester().getMatch(), clazz.getName(), Arrays.toString( constructorArgumentTypes ) ) ); } proxyManager = new ProxyManager( clazz, constructor, defaultConstructorArguments, getDigester() ); } instance = proxyManager.createProxy(); } getDigester().push( instance ); } /** * { @inheritDoc } */ @Override public void end( String namespace, String name ) throws Exception { Object top = getDigester().pop(); if ( proxyManager != null ) { proxyManager.finalize( top ); } if ( getDigester().getLogger().isDebugEnabled() ) { getDigester().getLogger().debug( format( "[ObjectCreateRule]{%s} Pop '%s'" , getDigester().getMatch(), top.getClass().getName() ) ); } }
begin()方法用于創(chuàng)建一個(gè)對(duì)象實(shí)例,并將其壓入到Digester對(duì)象的內(nèi)部棧中;end()方法會(huì)將內(nèi)部棧的棧頂元素彈出棧
要向Digester實(shí)例中添加Rule對(duì)象,還可以調(diào)用其addRuleSet()方法,方法實(shí)現(xiàn)如下:
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 ); }
org.apache.commons.digester3.RuleSet接口表示Rule對(duì)象的集合,該接口定義了兩個(gè)方法,分別為addRuleInstance()和getNamespaceURI(),addRuleInstance()方法簽名如下:
public void addRuleInstance(Digester digester)
addRuleInstance()方法用于添加定義在當(dāng)前RuleSet對(duì)象中的Rule對(duì)象集合到作為該方法參數(shù)傳輸?shù)腄igester實(shí)例中
getNamespaceUR()方法返回將要應(yīng)用在所有Rule對(duì)象(在當(dāng)前Ruleset中創(chuàng)建的)的命名空間的URI,該方法簽名如下
public java.lang.String getNamespaceURI()
因此,在創(chuàng)建了Digester對(duì)象之后,可以創(chuàng)建一個(gè)RuleSet對(duì)象,并將其傳輸給Digester對(duì)象的addRuleSet()方法
為了便于使用,實(shí)現(xiàn)RuleSet接口有一個(gè)基類RuleSetBase,RuleSetBase類為抽象類,提供了getNamespaceURI()方法的實(shí)現(xiàn),我們只需要提供addRuleInstances()方法的實(shí)現(xiàn)就可以了
下面是我們創(chuàng)建的EmployeeRuleSet類的源碼(繼承自RuleSetBase類)
public class EmployeeRuleSet extends RuleSetBase { public void addRuleInstances(Digester digester) { // add rules digester.addObjectCreate("employee", "ex15.pyrmont.digestertest.Employee" ); digester.addSetProperties( "employee" ); digester.addObjectCreate( "employee/office", "ex15.pyrmont.digestertest.Office" ); digester.addSetProperties( "employee/office" ); digester.addSetNext( "employee/office", "addOffice" ); digester.addObjectCreate( "employee/office/address" , "ex15.pyrmont.digestertest.Address" ); digester.addSetProperties( "employee/office/address" ); digester.addSetNext( "employee/office/address", "setAddress" ); } }
我們注意到,EmployeeRuleSet類中的addRuleInstances()方法的實(shí)現(xiàn)的功能類似Test02類,將相同的Rule對(duì)象添加到Digester對(duì)象中
下面是Test03的代碼,里面會(huì)創(chuàng)建EmployeeRuleSet類的實(shí)例,然后將其添加到之前創(chuàng)建的Digester對(duì)象中
public class Test03 { public static void main(String[] args) { InputStream inputStream = null ; Digester digester = new Digester(); digester.addRuleSet( new EmployeeRuleSet()); try { inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("employee2.xml" ); Employee employee = (Employee) digester.parse(inputStream); ArrayList offices = employee.getOffices(); Iterator iterator = offices.iterator(); System.out.println( "-------------------------------------------------" ); while (iterator.hasNext()) { Office office = (Office) iterator.next(); Address address = office.getAddress(); System.out.println(office.getDescription()); System.out.println( "Address : " + address.getStreetNumber() + " " + address.getStreetName()); System.out.println( "--------------------------------" ); } } catch (Exception e) { e.printStackTrace(); } finally { if (inputStream != null ) { try { inputStream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
與其他類型的容器不同,StandardContext實(shí)例必須有一個(gè)監(jiān)聽器,該監(jiān)聽器會(huì)負(fù)責(zé)配置StandardContext實(shí)例,設(shè)置成功后會(huì)將StandardContext實(shí)例的變量configued值設(shè)置為tue。
StandardContext類的標(biāo)準(zhǔn)監(jiān)聽器是org.apache.catalina.startup.ContextConfig類的實(shí)例,它會(huì)執(zhí)行很對(duì)StandardContext實(shí)例來說必不可少的任務(wù),例如安裝驗(yàn)證器閥到StandardContext實(shí)例的管道對(duì)象中,此外還會(huì)添加許可器閥(類型為org.apache.catalina.valves.CertificateValve)到管道對(duì)象中。
但更重要的是,ContextConfig類的實(shí)例還會(huì)讀取和解析默認(rèn)的web.xml文件和應(yīng)用程序自定義的web.xml文件,并將xml元素轉(zhuǎn)換為java對(duì)象。
默認(rèn)的web.xml文件位于CATALINE_HOME目錄下的conf目錄中,其中定義并映射了很多默認(rèn)的servlet,配置了很多MIME類型文件的映射,定義了默認(rèn)的session超時(shí)時(shí)間,以及定義了歡迎文件的列表。
應(yīng)用程序的web.xml文件是應(yīng)用程序自定義的配置文件,位于應(yīng)用程序目錄下的WEB-INF目錄中。
ContextConfig實(shí)例會(huì)為每一個(gè)servlet元素創(chuàng)建StandardWrapper實(shí)例,因此,正如你在本章應(yīng)用程序中看到的,配置變簡(jiǎn)單了,你不在需要實(shí)例化Wrapper實(shí)例了
因此,我們需要在Bootstrap類中實(shí)例化一個(gè)ContextConfig類,并調(diào)用org.apache.catalina.Lifecycle接口的addLifecycleListener()方法將其添加到StandardContext對(duì)象中
LifecycleListener listener = new ContextConfig(); ((Lifecycle) context).addLifecycleListener(listener);
在啟動(dòng)和停止StandardContext實(shí)例時(shí),會(huì)觸發(fā)相應(yīng)事件,ContextConfig類會(huì)對(duì)兩種事件做出響應(yīng),分別為START_EVENT 和STOP_EVENT
每當(dāng)StandardContext實(shí)例觸發(fā)事件時(shí),會(huì)調(diào)用ContextConfig實(shí)例的lifecycleEvent()方法
public void lifecycleEvent(LifecycleEvent event) { // Identify the context we are associated with try { context = (Context) event.getLifecycle(); if (context instanceof StandardContext) { int contextDebug = ((StandardContext) context).getDebug(); if (contextDebug > this .debug) this .debug = contextDebug; } } catch (ClassCastException e) { log(sm.getString( "contextConfig.cce" , event.getLifecycle()), e); return ; } // Process the event that has occurred if (event.getType().equals(Lifecycle.START_EVENT)) start(); else if (event.getType().equals(Lifecycle.STOP_EVENT)) stop(); }
在上面方法中,會(huì)繼續(xù)調(diào)用start()方法和stop()方法
private synchronized void start() { if (debug > 0 ) log(sm.getString( "contextConfig.start" )); context.setConfigured( false ); ok = true ; // Set properties based on DefaultContext Container container = context.getParent(); if ( ! context.getOverride() ) { if ( container instanceof Host ) { ((Host)container).importDefaultContext(context); container = container.getParent(); } if ( container instanceof Engine ) { ((Engine)container).importDefaultContext(context); } } // Process the default and application web.xml files defaultConfig(); applicationConfig(); if (ok) { validateSecurityRoles(); } // Scan tag library descriptor files for additional listener classes if (ok) { try { tldScan(); } catch (Exception e) { log(e.getMessage(), e); ok = false ; } } // Configure a certificates exposer valve, if required if (ok) certificatesConfig(); // Configure an authenticator if we need one if (ok) authenticatorConfig(); // Dump the contents of this pipeline if requested if ((debug >= 1) && (context instanceof ContainerBase)) { log( "Pipline Configuration:" ); Pipeline pipeline = ((ContainerBase) context).getPipeline(); Valve valves[] = null ; if (pipeline != null ) valves = pipeline.getValves(); if (valves != null ) { for ( int i = 0; i < valves.length; i++ ) { log( " " + valves[i].getInfo()); } } log( "======================" ); } // Make our application available if no problems were encountered if (ok) context.setConfigured( true ); else { log(sm.getString( "contextConfig.unavailable" )); context.setConfigured( false ); } }
start()方法會(huì)進(jìn)一步調(diào)用defaultConfig()方法和applicationConfig()方法
defaultConfig()方法負(fù)責(zé)讀取并解析位于%CATALINA_HOME%/conf目錄下的默認(rèn)的web.xml文件
private void defaultConfig() { // Open the default web.xml file, if it exists File file = new File(Constants.DefaultWebXml); if (! file.isAbsolute()) file = new File(System.getProperty("catalina.base" ), Constants.DefaultWebXml); FileInputStream stream = null ; try { stream = new FileInputStream(file.getCanonicalPath()); stream.close(); stream = null ; } catch (FileNotFoundException e) { log(sm.getString( "contextConfig.defaultMissing" )); return ; } catch (IOException e) { log(sm.getString( "contextConfig.defaultMissing" ), e); return ; } // Process the default web.xml file synchronized (webDigester) { try { InputSource is = new InputSource("file://" + file.getAbsolutePath()); stream = new FileInputStream(file); is.setByteStream(stream); webDigester.setDebug(getDebug()); if (context instanceof StandardContext) ((StandardContext) context).setReplaceWelcomeFiles( true ); webDigester.clear(); webDigester.push(context); webDigester.parse(is); } catch (SAXParseException e) { log(sm.getString( "contextConfig.defaultParse" ), e); log(sm.getString( "contextConfig.defaultPosition" , "" + e.getLineNumber(), "" + e.getColumnNumber())); ok = false ; } catch (Exception e) { log(sm.getString( "contextConfig.defaultParse" ), e); ok = false ; } finally { try { if (stream != null ) { stream.close(); } } catch (IOException e) { log(sm.getString( "contextConfig.defaultClose" ), e); } } } }
applicationConfig()方法與defaultConfig()方法類似,只不過它處理的是應(yīng)用程序自定義的部署描述符,該部署描述符位于應(yīng)用目錄下的WEB-INF目錄中
private void applicationConfig() { // Open the application web.xml file, if it exists InputStream stream = null ; ServletContext servletContext = context.getServletContext(); if (servletContext != null ) stream = servletContext.getResourceAsStream (Constants.ApplicationWebXml); if (stream == null ) { log(sm.getString( "contextConfig.applicationMissing" )); return ; } // Process the application web.xml file synchronized (webDigester) { try { URL url = servletContext.getResource(Constants.ApplicationWebXml); InputSource is = new InputSource(url.toExternalForm()); is.setByteStream(stream); webDigester.setDebug(getDebug()); if (context instanceof StandardContext) { ((StandardContext) context).setReplaceWelcomeFiles( true ); } webDigester.clear(); webDigester.push(context); webDigester.parse(is); } catch (SAXParseException e) { log(sm.getString( "contextConfig.applicationParse" ), e); log(sm.getString( "contextConfig.applicationPosition" , "" + e.getLineNumber(), "" + e.getColumnNumber())); ok = false ; } catch (Exception e) { log(sm.getString( "contextConfig.applicationParse" ), e); ok = false ; } finally { try { if (stream != null ) { stream.close(); } } catch (IOException e) { log(sm.getString( "contextConfig.applicationClose" ), e); } } } }
在ContextConfig類中,使用變量webDigester來引用一個(gè)Digester類型的對(duì)象
private static Digester webDigester = createWebDigester();
該Digester對(duì)象用于解析默認(rèn)的web.xml文件和應(yīng)用程序自定義的web.xml文件,在調(diào)用createWebDigester()方法時(shí)會(huì)添加用來處理web.xml文件的規(guī)則
/** * Create (if necessary) and return a Digester configured to process the * web application deployment descriptor (web.xml). */ private static Digester createWebDigester() { URL url = null ; Digester webDigester = new Digester(); webDigester.setValidating( true ); url = ContextConfig. class .getResource(Constants.WebDtdResourcePath_22); webDigester.register(Constants.WebDtdPublicId_22, url.toString()); url = ContextConfig. class .getResource(Constants.WebDtdResourcePath_23); webDigester.register(Constants.WebDtdPublicId_23, url.toString()); webDigester.addRuleSet( new WebRuleSet()); return (webDigester); }
我們注意到,上面方法中調(diào)用了變量webDigester的addRuleSet()方法,傳入一個(gè)org.apache.catalina.startup.WebRuleSet類型的對(duì)象作為參數(shù);WebRuleSet類是org.apache.commons.digester.RuleSetBase的子類。
下面是WebRuleSet類的addRuleInstances()方法實(shí)現(xiàn):
public void addRuleInstances(Digester digester) { digester.addRule(prefix + "web-app" , new SetPublicIdRule(digester, "setPublicId" )); digester.addCallMethod(prefix + "web-app/context-param" , "addParameter", 2 ); digester.addCallParam(prefix + "web-app/context-param/param-name", 0 ); digester.addCallParam(prefix + "web-app/context-param/param-value", 1 ); digester.addCallMethod(prefix + "web-app/display-name" , "setDisplayName", 0 ); digester.addRule(prefix + "web-app/distributable" , new SetDistributableRule(digester)); digester.addObjectCreate(prefix + "web-app/ejb-local-ref" , "org.apache.catalina.deploy.ContextLocalEjb" ); digester.addSetNext(prefix + "web-app/ejb-local-ref" , "addLocalEjb" , "org.apache.catalina.deploy.ContextLocalEjb" ); // 代碼太長,后面部分略 }
---------------------------------------------------------------------------?
本系列How Tomcat Works系本人原創(chuàng)?
轉(zhuǎn)載請(qǐng)注明出處 博客園 刺猬的溫馴?
本人郵箱: ? chenying998179 # 163.com ( #改為@ )
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
