How Tomcat Works 12: StandardContext
1. StandardContext的配置 (1)啟動:start()方法呼叫後,如果context例項失敗,需要將available設為false (2)Tomcat部署context時:會先讀取解析%CATALINA_HOME%/conf內的web.xml為所有standardContext,而後會讀取解析webapp-level級別的web.xml。此外,authenticator和certicate valve的安裝。 (3)StandardContext使用event Listener作為configurator,當所有listener event呼叫完成,則需要把configured property設定為true。如果配置失敗,則StandardContext例項會拒絕啟動 (4)使用了StandardContextConfig【實現了Lifecycle Listener介面】類 2. StandardContext解析: (1)構造器: pipeline加入standardContextValve,處理所有connector過來的http request
public StandardContext() {
super();
pipeline.setBasic(new StandardContextValve()); // StandardContextValve 處理所有的從connector過來的http request
namingResources.setContainer(this);
}
(2)啟動StandardContext 啟動方法初始化context例項,讓所有lifecycle listener有機會完成配置。配置成功會設定available=true,為所有http request提供服務 Tomcat4中的start方法實現
@Override public void start() throws LifecycleException { if(started) { throw new LifecycleException("containerBase.alreadyStarted"); } lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); setAvailable(false); setConfigured(false); boolean ok = true; // (1)設定Loader需要的資源路徑 if ((docBase != null) && (docBase.endsWith(".war"))) { setResources(new WARDirContext()); }else { setResources(new FileDirContext()); } // (2)set Loader和Manager(session管理) if (getPrivileged()) { setLoader(new WebappLoader(this.getClass().getClassLoader())); }esle{ setLoader(new WebappLoader(getParentClassLoader())); } setManager(new StandardManager()); // Initialize character set mapper getCharsetMapper(); // Post work directory postWorkDirectory(); // (3)如果都配置完成,使用namingContextListener設定配置項 if (ok && isUseNaming()) { if (namingContextListener == null) { namingContextListener = new NamingContextListener(); namingContextListener.setName(getNamingContextName()); addLifecycleListener(namingContextListener); } // Binding thread ClassLoader oldCCL = bindThread(); // (4)啟動loader,logger,解綁執行緒-》繫結執行緒,啟動mapper,啟動子容器,啟動pipeline,觸發startevent,啟動manager 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); } } // (5) 如果完成,載入啟動引數給servlets if (!getConfigured()) ok = false; // Load and initialize all "load on startup" servlets if (ok) loadOnStartup(findChildren()); // Unbinding thread unbindThread(oldCCL); lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); }
(3)invoke方法: 【若有】被Host.invoke呼叫或者connector中呼叫invoke.呼叫前會檢視是否重啟中
public void invoke(Request request, Response response) throws IOException, ServletException {
// Wait if we are reloading
while (getPaused()) {
Thread.sleep(1000);
// Normal request processing
if (swallowOutput) {
SystemLogHandler.startCapture();
super.invoke(request, response);
String log = SystemLogHandler.stopCapture();
if (log != null && log.length() > 0) {
log(log);
}
}
}
else {
super.invoke(request, response);
}
}
(4)支援reload 在WebAppLoad.setContainer中會加入setReloadable(container.getReloadable()), setReloadable會根據true/false選擇threadStart()或threadStop()間隔掃描WEB-INF中的class和jar包 如果有變化,會在context中解除安裝原有的classLoader,然後所有wrapper.stop() 然後再重啟wrapper.start()->這是wrapper的getClassLoader會變成新的classloader,同時所有servlet class都會從新allocate