前言
上篇文章簡單介紹了 Host,這篇文章裏講到了Context的啓動時機,就是在 Engine 的 start 方法中調用了子容器的 start 方法(在ContainerBase#startInternal),子容器又調用子容器的方法,以此類推,全部子容器的start 方法都會被調用。java
1. StandardContext#initInternalweb
@Override protected void initInternal() throws LifecycleException { super.initInternal(); // Register the naming resources if (namingResources != null) { namingResources.init(); } // Send j2ee.object.created notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.object.created", this.getObjectName(), sequenceNumber.getAndIncrement()); broadcaster.sendNotification(notification); } }
首先調用了父類的 initInternal 方法,也就是 ContainerBase#initInternal。
而後調用了 namingResources(NamingResourcesImpl類型的屬性) 的 init 方法,這個 namingResources 跟 StandardServer 裏的 globalNamingResources 相似,只不過 globalNamingResources 是全局的,而這裏的 namingResources 只是這個 StandardContext 的。
最後構造一個 Notification 對象並調用 broadcaster.sendNotification 方法來廣播這個通知。
broadcaster 是在 StandardContext 對象構造函數裏初始化的。segmentfault
broadcaster = new NotificationBroadcasterSupport();
NotificationBroadcasterSupport 這個類是在javax.management 包下,不是本文重點,先略過。tomcat
2. StandardContext#startInternalcookie
protected synchronized void startInternal() throws LifecycleException { if(log.isDebugEnabled()) log.debug("Starting " + getBaseName()); // Send j2ee.state.starting notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.starting", this.getObjectName(), sequenceNumber.getAndIncrement()); broadcaster.sendNotification(notification); } setConfigured(false); boolean ok = true; // Currently this is effectively a NO-OP but needs to be called to // ensure the NamingResources follows the correct lifecycle if (namingResources != null) { namingResources.start(); } // Post work directory postWorkDirectory(); // Add missing components as necessary if (getResources() == null) { // (1) Required by Loader if (log.isDebugEnabled()) log.debug("Configuring default Resources"); try { setResources(new StandardRoot(this)); } catch (IllegalArgumentException e) { log.error(sm.getString("standardContext.resourcesInit"), e); ok = false; } } if (ok) { resourcesStart(); } if (getLoader() == null) { WebappLoader webappLoader = new WebappLoader(getParentClassLoader()); webappLoader.setDelegate(getDelegate()); setLoader(webappLoader); } // An explicit cookie processor hasn't been specified; use the default if (cookieProcessor == null) { cookieProcessor = new Rfc6265CookieProcessor(); } // Initialize character set mapper getCharsetMapper(); // Validate required extensions boolean dependencyCheck = true; try { dependencyCheck = ExtensionValidator.validateApplication (getResources(), this); } catch (IOException ioe) { log.error(sm.getString("standardContext.extensionValidationError"), ioe); dependencyCheck = false; } if (!dependencyCheck) { // do not make application available if dependency check fails ok = false; } // Reading the "catalina.useNaming" environment variable String useNamingProperty = System.getProperty("catalina.useNaming"); if ((useNamingProperty != null) && (useNamingProperty.equals("false"))) { useNaming = false; } if (ok && isUseNaming()) { if (getNamingContextListener() == null) { NamingContextListener ncl = new NamingContextListener(); ncl.setName(getNamingContextName()); ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite()); addLifecycleListener(ncl); setNamingContextListener(ncl); } } // Standard container startup if (log.isDebugEnabled()) log.debug("Processing standard container startup"); // Binding thread ClassLoader oldCCL = bindThread(); try { if (ok) { // Start our subordinate components, if any Loader loader = getLoader(); if (loader instanceof Lifecycle) { ((Lifecycle) loader).start(); } // since the loader just started, the webapp classloader is now // created. setClassLoaderProperty("clearReferencesRmiTargets", getClearReferencesRmiTargets()); setClassLoaderProperty("clearReferencesStopThreads", getClearReferencesStopThreads()); setClassLoaderProperty("clearReferencesStopTimerThreads", getClearReferencesStopTimerThreads()); setClassLoaderProperty("clearReferencesHttpClientKeepAliveThread", getClearReferencesHttpClientKeepAliveThread()); setClassLoaderProperty("clearReferencesObjectStreamClassCaches", getClearReferencesObjectStreamClassCaches()); setClassLoaderProperty("clearReferencesObjectStreamClassCaches", getClearReferencesObjectStreamClassCaches()); setClassLoaderProperty("clearReferencesThreadLocals", getClearReferencesThreadLocals()); // By calling unbindThread and bindThread in a row, we setup the // current Thread CCL to be the webapp classloader unbindThread(oldCCL); oldCCL = bindThread(); // Initialize logger again. Other components might have used it // too early, so it should be reset. logger = null; getLogger(); Realm realm = getRealmInternal(); if(null != realm) { if (realm instanceof Lifecycle) { ((Lifecycle) realm).start(); } // Place the CredentialHandler into the ServletContext so // applications can have access to it. Wrap it in a "safe" // handler so application's can't modify it. CredentialHandler safeHandler = new CredentialHandler() { @Override public boolean matches(String inputCredentials, String storedCredentials) { return getRealmInternal().getCredentialHandler().matches(inputCredentials, storedCredentials); } @Override public String mutate(String inputCredentials) { return getRealmInternal().getCredentialHandler().mutate(inputCredentials); } }; context.setAttribute(Globals.CREDENTIAL_HANDLER, safeHandler); } // Notify our interested LifecycleListeners fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); // Start our child containers, if not already started for (Container child : findChildren()) { if (!child.getState().isAvailable()) { child.start(); } } // Start the Valves in our pipeline (including the basic), // if any if (pipeline instanceof Lifecycle) { ((Lifecycle) pipeline).start(); } // Acquire clustered manager Manager contextManager = null; Manager manager = getManager(); if (manager == null) { if (log.isDebugEnabled()) { log.debug(sm.getString("standardContext.cluster.noManager", Boolean.valueOf((getCluster() != null)), Boolean.valueOf(distributable))); } if ((getCluster() != null) && distributable) { try { contextManager = getCluster().createManager(getName()); } catch (Exception ex) { log.error(sm.getString("standardContext.cluster.managerError"), ex); ok = false; } } else { contextManager = new StandardManager(); } } // Configure default manager if none was specified if (contextManager != null) { if (log.isDebugEnabled()) { log.debug(sm.getString("standardContext.manager", contextManager.getClass().getName())); } setManager(contextManager); } if (manager!=null && (getCluster() != null) && distributable) { //let the cluster know that there is a context that is distributable //and that it has its own manager getCluster().registerManager(manager); } } if (!getConfigured()) { log.error(sm.getString("standardContext.configurationFail")); ok = false; } // We put the resources into the servlet context if (ok) getServletContext().setAttribute (Globals.RESOURCES_ATTR, getResources()); if (ok ) { if (getInstanceManager() == null) { javax.naming.Context context = null; if (isUseNaming() && getNamingContextListener() != null) { context = getNamingContextListener().getEnvContext(); } Map<String, Map<String, String>> injectionMap = buildInjectionMap( getIgnoreAnnotations() ? new NamingResourcesImpl(): getNamingResources()); setInstanceManager(new DefaultInstanceManager(context, injectionMap, this, this.getClass().getClassLoader())); } getServletContext().setAttribute( InstanceManager.class.getName(), getInstanceManager()); InstanceManagerBindings.bind(getLoader().getClassLoader(), getInstanceManager()); } // Create context attributes that will be required if (ok) { getServletContext().setAttribute( JarScanner.class.getName(), getJarScanner()); } // Set up the context init params mergeParameters(); // Call ServletContainerInitializers for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry : initializers.entrySet()) { try { entry.getKey().onStartup(entry.getValue(), getServletContext()); } catch (ServletException e) { log.error(sm.getString("standardContext.sciFail"), e); ok = false; break; } } // Configure and call application event listeners if (ok) { if (!listenerStart()) { log.error(sm.getString("standardContext.listenerFail")); ok = false; } } // Check constraints for uncovered HTTP methods // Needs to be after SCIs and listeners as they may programmatically // change constraints if (ok) { checkConstraintsForUncoveredMethods(findConstraints()); } try { // Start manager Manager manager = getManager(); if (manager instanceof Lifecycle) { ((Lifecycle) manager).start(); } } catch(Exception e) { log.error(sm.getString("standardContext.managerFail"), e); ok = false; } // Configure and call application filters if (ok) { if (!filterStart()) { log.error(sm.getString("standardContext.filterFail")); ok = false; } } // Load and initialize all "load on startup" servlets if (ok) { if (!loadOnStartup(findChildren())){ log.error(sm.getString("standardContext.servletFail")); ok = false; } } // Start ContainerBackgroundProcessor thread super.threadStart(); } finally { // Unbinding thread unbindThread(oldCCL); } // Set available status depending upon startup success if (ok) { if (log.isDebugEnabled()) log.debug("Starting completed"); } else { log.error(sm.getString("standardContext.startFailed", getName())); } startTime=System.currentTimeMillis(); // Send j2ee.state.running notification if (ok && (this.getObjectName() != null)) { Notification notification = new Notification("j2ee.state.running", this.getObjectName(), sequenceNumber.getAndIncrement()); broadcaster.sendNotification(notification); } // The WebResources implementation caches references to JAR files. On // some platforms these references may lock the JAR files. Since web // application start is likely to have read from lots of JARs, trigger // a clean-up now. getResources().gc(); // Reinitializing if something went wrong if (!ok) { setState(LifecycleState.FAILED); } else { setState(LifecycleState.STARTING); } }
StandardContext 的 startInternal 方法比較長,作了不少事情,是 tomcat 啓動過程當中很是重要的環節。最重要的是作了這幾件事:app
一、生成工做目錄,postWorkDirectory()。 二、設置並啓動解析資源,並加入到Context 的 Attribute 中,setResources(...)、resourcesStart(), 三、設置並啓動 WebappLoader 四、設置 coockieProcessor 和 chasetMapper 五、若是 Context 的 Realm 類型屬性不爲空,啓動 Realm 六、建立 CredentialHandler 對象,並加入到 Context 的 Attribute 中 七、啓動子容器 八、啓動本身的 Pipeline(實際上也是啓動 Pipeline 的 Value) 九、建立並設置一個 Manager 對象,而後啓動,而後加入到 Context 的 Attribute 中 十、建立並設置一個 JarScanner 對象,而後加入到 Context 的 Attribute 中 十一、合併初始化參數,mergeParameters(); 十二、調用 ServletContainerInitializer 的 onStartUp 方法 1三、建立一個或多個 ApplicationFilterConfig 對象,並初始化 Filter(調用Filter#init),並存放在 StandardContext 的一個Map裏 1四、初始化 loadOnStartup 的 Servlet,loadOnStartup(findChildren()),findChildren() 返回的是 Wrapper,Wrapper 包含了 Servlet 類型的屬性,調用 Wrapper#load 方法,在這個方法裏調用 Servlet 對象的 init 方法。 1五、調用父類的 threadStart 方法來執行 backgroudProcess 方法 1六、調用resource的 gc() 方法(也就是 StandardRoot#gc() )來釋放資源,
StandardContext 的 startInternal 方法準備了 Web 應用程序所須要的一切資源,不過並無調用父類ContainerBase 的 startInternal 方法。webapp
3. StandardContext#backgroundProcesside
@Override public void backgroundProcess() { if (!getState().isAvailable()) return; Loader loader = getLoader(); if (loader != null) { try { loader.backgroundProcess(); } catch (Exception e) { log.warn(sm.getString( "standardContext.backgroundProcess.loader", loader), e); } } Manager manager = getManager(); if (manager != null) { try { manager.backgroundProcess(); } catch (Exception e) { log.warn(sm.getString( "standardContext.backgroundProcess.manager", manager), e); } } WebResourceRoot resources = getResources(); if (resources != null) { try { resources.backgroundProcess(); } catch (Exception e) { log.warn(sm.getString( "standardContext.backgroundProcess.resources", resources), e); } } InstanceManager instanceManager = getInstanceManager(); if (instanceManager != null) { try { instanceManager.backgroundProcess(); } catch (Exception e) { log.warn(sm.getString( "standardContext.backgroundProcess.instanceManager", resources), e); } } super.backgroundProcess(); }
StandardContext#backgroundProcess 方法裏分別調用 Loader、Mananger、WebResourceRoot(StandardRoot)、InstanceManager 的 backgroundProcess 方法,最後還調用父類 ContainerBase 的 backgroundProcess()。函數
小結
本文分析了 Context 的 start 和 init 方法,其中 startInternal 方法作了不少事情,其中最重要的是加載了用戶的Web程序代碼並初始化了 Filter 和 Servlet。post