本文接下來(lái)分析Context容器,Context容器實(shí)例表示一個(gè)具體的Web應(yīng)用程序,其中包括一個(gè)或多個(gè)Wrapper實(shí)例;不過Context容器還需要其他的組件支持,典型的如載入器和Session管理器等。
在創(chuàng)建StandardContext實(shí)例后,必須調(diào)用其start()方法來(lái)為引入的每個(gè)HTTP請(qǐng)求服務(wù);其中包括讀取和解析默認(rèn)的web.xml文件(該文件位于%CATALINA_HOME%/conf目錄),該文件的內(nèi)容會(huì)應(yīng)用到所有部署到tomcat中的應(yīng)用程序中;此外,還會(huì)配置驗(yàn)證器閥和許可閥。
StandardContext類使用一個(gè)事件監(jiān)聽器來(lái)作為其配置器(前面我們已經(jīng)學(xué)過在SimpleContextConfig事件監(jiān)聽器中配置驗(yàn)證器閥)
public synchronized void start() throws LifecycleException { if (started) throw new LifecycleException (sm.getString( "containerBase.alreadyStarted" , logName())); if (debug >= 1 ) log( "Starting" ); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null ); if (debug >= 1 ) log( "Processing start(), current available=" + getAvailable()); setAvailable( false ); setConfigured( false ); boolean ok = true ; // Add missing components as necessary if (getResources() == null ) { // (1) Required by Loader if (debug >= 1 ) log( "Configuring default Resources" ); try { if ((docBase != null ) && (docBase.endsWith(".war" ))) setResources( new WARDirContext()); else setResources( new FileDirContext()); } catch (IllegalArgumentException e) { log( "Error initializing resources: " + e.getMessage()); ok = false ; } } if (ok && (resources instanceof ProxyDirContext)) { DirContext dirContext = ((ProxyDirContext) resources).getDirContext(); if ((dirContext != null ) && (dirContext instanceof BaseDirContext)) { ((BaseDirContext) dirContext).setDocBase(getBasePath()); ((BaseDirContext) dirContext).allocate(); } } if (getLoader() == null ) { // (2) Required by Manager if (getPrivileged()) { if (debug >= 1 ) log( "Configuring privileged default Loader" ); setLoader( new WebappLoader( this .getClass().getClassLoader())); } else { if (debug >= 1 ) log( "Configuring non-privileged default Loader" ); setLoader( new WebappLoader(getParentClassLoader())); } } if (getManager() == null ) { // (3) After prerequisites if (debug >= 1 ) log( "Configuring default Manager" ); setManager( new StandardManager()); } // Initialize character set mapper getCharsetMapper(); // Post work directory postWorkDirectory(); // Reading the "catalina.useNaming" environment variable String useNamingProperty = System.getProperty("catalina.useNaming" ); if ((useNamingProperty != null ) && (useNamingProperty.equals("false" ))) { useNaming = false ; } if (ok && isUseNaming()) { if (namingContextListener == null ) { namingContextListener = new NamingContextListener(); namingContextListener.setDebug(getDebug()); namingContextListener.setName(getNamingContextName()); addLifecycleListener(namingContextListener); } } // Binding thread ClassLoader oldCCL = bindThread(); // Standard container startup if (debug >= 1 ) log( "Processing standard container startup" ); if (ok) { try { addDefaultMapper( this .mapperClass); started = true ; // Start our subordinate components, if any if ((loader != null ) && (loader instanceof Lifecycle)) ((Lifecycle) loader).start(); if ((logger != null ) && (logger instanceof Lifecycle)) ((Lifecycle) logger).start(); // Unbinding thread unbindThread(oldCCL); // Binding thread oldCCL = bindThread(); if ((cluster != null ) && (cluster instanceof Lifecycle)) ((Lifecycle) cluster).start(); if ((realm != null ) && (realm instanceof Lifecycle)) ((Lifecycle) realm).start(); if ((resources != null ) && (resources instanceof Lifecycle)) ((Lifecycle) resources).start(); // Start our Mappers, if any Mapper mappers[] = findMappers(); for ( int i = 0; i < mappers.length; i++ ) { if (mappers[i] instanceof Lifecycle) ((Lifecycle) mappers[i]).start(); } // Start our child containers, if any Container children[] = findChildren(); for ( int i = 0; i < children.length; i++ ) { if (children[i] instanceof Lifecycle) ((Lifecycle) children[i]).start(); } // Start the Valves in our pipeline (including the basic), // if any if (pipeline instanceof Lifecycle) ((Lifecycle) pipeline).start(); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(START_EVENT, null ); if ((manager != null ) && (manager instanceof Lifecycle)) ((Lifecycle) manager).start(); } finally { // Unbinding thread unbindThread(oldCCL); } } if (! getConfigured()) ok = false ; // We put the resources into the servlet context if (ok) getServletContext().setAttribute (Globals.RESOURCES_ATTR, getResources()); // Binding thread oldCCL = bindThread(); // Create context attributes that will be required if (ok) { if (debug >= 1 ) log( "Posting standard context attributes" ); postWelcomeFiles(); } // Configure and call application event listeners and filters if (ok) { if (! listenerStart()) ok = false ; } if (ok) { if (! filterStart()) ok = false ; } // Load and initialize all "load on startup" servlets if (ok) loadOnStartup(findChildren()); // Unbinding thread unbindThread(oldCCL); // Set available status depending upon startup success if (ok) { if (debug >= 1 ) log( "Starting completed" ); setAvailable( true ); } else { log(sm.getString( "standardContext.startFailed" )); try { stop(); } catch (Throwable t) { log(sm.getString( "standardContext.startCleanup" ), t); } setAvailable( false ); } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null ); }
在它的start()方法里面,包括初始化相關(guān)容器組件、觸發(fā)相關(guān)事件等(ContextConfig監(jiān)聽器會(huì)執(zhí)行一些配置操作)
StandardContext類的invoke()方法由與其相關(guān)聯(lián)的連接器調(diào)用,或者當(dāng)StandardContext實(shí)例是Host容器的子容器時(shí),由Host實(shí)例的invoke()方法調(diào)用
public void invoke(Request request, Response response) throws IOException, ServletException { // Wait if we are reloading while (getPaused()) { try { Thread.sleep( 1000 ); } catch (InterruptedException e) { ; } } // Normal request processing if (swallowOutput) { try { SystemLogHandler.startCapture(); super .invoke(request, response); } finally { String log = SystemLogHandler.stopCapture(); if (log != null && log.length() > 0 ) { log(log); } } } else { super .invoke(request, response); } }
對(duì)于每個(gè)引入的HTTP請(qǐng)求,都會(huì)調(diào)用StandardContext實(shí)例的管道對(duì)象的基礎(chǔ)閥的invoke()方法來(lái)處理,這里是org.apache.catalina.core.StandardContextValve類的實(shí)例;StandardContextValve類的invoke()方法要做的第一件事是獲取一個(gè)要處理當(dāng)前HTTP請(qǐng)求的Wrapper實(shí)例;StandardContextValve實(shí)例使用StandardContext實(shí)例的映射器找到一個(gè)合適的Wrapper實(shí)例,找到Wrapper實(shí)例后,它就會(huì)調(diào)用Wrapper實(shí)例的invoke()方法
下面是standardContextMapper類的map()方法
public Container map(Request request, boolean update) { int debug = context.getDebug(); // Has this request already been mapped? if (update && (request.getWrapper() != null )) return (request.getWrapper()); // Identify the context-relative URI to be mapped String contextPath = ((HttpServletRequest) request.getRequest()).getContextPath(); String requestURI = ((HttpRequest) request).getDecodedRequestURI(); String relativeURI = requestURI.substring(contextPath.length()); if (debug >= 1 ) context.log( "Mapping contextPath='" + contextPath + "' with requestURI='" + requestURI + "' and relativeURI='" + relativeURI + "'" ); // Apply the standard request URI mapping rules from the specification Wrapper wrapper = null ; String servletPath = relativeURI; String pathInfo = null ; String name = null ; // Rule 1 -- Exact Match if (wrapper == null ) { if (debug >= 2 ) context.log( " Trying exact match" ); if (!(relativeURI.equals("/" ))) name = context.findServletMapping(relativeURI); if (name != null ) wrapper = (Wrapper) context.findChild(name); if (wrapper != null ) { servletPath = relativeURI; pathInfo = null ; } } // Rule 2 -- Prefix Match if (wrapper == null ) { if (debug >= 2 ) context.log( " Trying prefix match" ); servletPath = relativeURI; while ( true ) { name = context.findServletMapping(servletPath + "/*" ); if (name != null ) wrapper = (Wrapper) context.findChild(name); if (wrapper != null ) { pathInfo = relativeURI.substring(servletPath.length()); if (pathInfo.length() == 0 ) pathInfo = null ; break ; } int slash = servletPath.lastIndexOf('/' ); if (slash < 0 ) break ; servletPath = servletPath.substring(0 , slash); } } // Rule 3 -- Extension Match if (wrapper == null ) { if (debug >= 2 ) context.log( " Trying extension match" ); int slash = relativeURI.lastIndexOf('/' ); if (slash >= 0 ) { String last = relativeURI.substring(slash); int period = last.lastIndexOf('.' ); if (period >= 0 ) { String pattern = "*" + last.substring(period); name = context.findServletMapping(pattern); if (name != null ) wrapper = (Wrapper) context.findChild(name); if (wrapper != null ) { servletPath = relativeURI; pathInfo = null ; } } } } // Rule 4 -- Default Match if (wrapper == null ) { if (debug >= 2 ) context.log( " Trying default match" ); name = context.findServletMapping("/" ); if (name != null ) wrapper = (Wrapper) context.findChild(name); if (wrapper != null ) { servletPath = relativeURI; pathInfo = null ; } } // Update the Request (if requested) and return this Wrapper if ((debug >= 1) && (wrapper != null )) context.log( " Mapped to servlet '" + wrapper.getName() + "' with servlet path '" + servletPath + "' and path info '" + pathInfo + "' and update=" + update); if (update) { request.setWrapper(wrapper); ((HttpRequest) request).setServletPath(servletPath); ((HttpRequest) request).setPathInfo(pathInfo); } return (wrapper); }
standardContextMapper實(shí)例必須與一個(gè)Context級(jí)的容器相關(guān)聯(lián)(在它的map()方法中調(diào)用了Context容器實(shí)例的相關(guān)方法)
standardContext類定義了reloadable屬性來(lái)指明該應(yīng)用程序是否啟用了重載功能,當(dāng)啟用重載功能后,當(dāng)web.xml文件發(fā)生變化或WEB-INF/classes目錄下的文件被重新編譯后,應(yīng)用程序會(huì)重載。
standardContext類是通過其載入器實(shí)現(xiàn)應(yīng)用程序重載的,在tomcat4中,standardContext對(duì)象中的WebappLoader類實(shí)現(xiàn)了Loader接口,并使用另一個(gè)線程檢查WEB-INF目錄中的所有類和JAR文件的時(shí)間戳。只需要調(diào)用其setContainer()方法將WebappLoader對(duì)象與standardContext對(duì)象相關(guān)聯(lián)就可以啟動(dòng)該檢查線程
下面是tomcat4中WebappLoader類的setContainer()方法的實(shí)現(xiàn)代碼
public void setContainer(Container container) { // Deregister from the old Container (if any) if (( this .container != null ) && ( this .container instanceof Context)) ((Context) this .container).removePropertyChangeListener( this ); // Process this property change Container oldContainer = this .container; this .container = container; support.firePropertyChange( "container", oldContainer, this .container); // Register with the new Container (if any) if (( this .container != null ) && ( this .container instanceof Context)) { setReloadable( ((Context) this .container).getReloadable() ); ((Context) this .container).addPropertyChangeListener( this ); } }
WebappLoader實(shí)例的reloadable屬性值與standardContext實(shí)例的reloadable屬性值是一致的
下面是WebappLoader類的setReloadable()方法的實(shí)現(xiàn)代碼:
public void setReloadable( boolean reloadable) { // Process this property change boolean oldReloadable = this .reloadable; this .reloadable = reloadable; support.firePropertyChange( "reloadable" , new Boolean(oldReloadable), new Boolean( this .reloadable)); // Start or stop our background thread if required if (! started) return ; if (!oldReloadable && this .reloadable) threadStart(); else if (oldReloadable && ! this .reloadable) threadStop(); }
里面的threadStart()方法會(huì)啟動(dòng)一個(gè)專用的線程來(lái)不斷地檢查WEB-INF目錄下的類和JAR文件的時(shí)間戳,而threadStop()方法則會(huì)終止該線程。
---------------------------------------------------------------------------?
本系列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)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
