tomcat8.5.57源码阅读笔记4.1 - StandardContext的init和start
接着上一篇, 该到StandardContext的 init 和 startInternal 方法了.
StandardContext#initInternal()
此方法由 org.apache.catalina.util.LifecycleBase#init() 调用而来.
//org.apache.catalina.core.StandardContext#initInternal 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); } }
super.initInternal() 调用的是 org.apache.catalina.core.ContainerBase#initInternal(), 里面创建了一个阻塞队列线程池
StandardContext#startInternal
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 // 生成工作目录, work/Catalina/localhost 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(); 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("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 // 这里会激发监听器 ContextConfig fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); // Start our child containers, if not already started 如果Wrapper没启动, 这里会再次尝试启动 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(); } ...... }
2. 这个方法里面会调用 loadOnStartup(findChildren()) 方法, 当 loadOnStartup >=0 时, 会对wrapper进行load操作, 最终执行 servlet#init() 方法.
1. fireLifecycleEvent 会激发 ContextConfig 的监听方法: org.apache.catalina.startup.ContextConfig#lifecycleEvent
套路和 HostConfig 是一样的.
public void lifecycleEvent(LifecycleEvent event) { // Identify the context we are associated with try { context = (Context) event.getLifecycle(); } catch (ClassCastException e) { log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e); return; } // Process the event that has occurred if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) { configureStart(); } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) { beforeStart(); } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) { // Restore docBase for management tools if (originalDocBase != null) { context.setDocBase(originalDocBase); } } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) { configureStop(); } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) { init(); } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) { destroy(); } }
根据传入事件, 会进入 configureStart() 方法.
protected synchronized void configureStart() { // Called from StandardContext.start() if (log.isDebugEnabled()) { log.debug(sm.getString("contextConfig.start")); } if (log.isDebugEnabled()) { log.debug(sm.getString("contextConfig.xmlSettings", context.getName(), Boolean.valueOf(context.getXmlValidation()), Boolean.valueOf(context.getXmlNamespaceAware()))); } //解析 web.xml webConfig(); //后加代码, 为了解决报错问题 context.addServletContainerInitializer(new JasperInitializer(), null); if (!context.getIgnoreAnnotations()) { applicationAnnotationsConfig(); } if (ok) { validateSecurityRoles(); } // Configure an authenticator if we need one if (ok) { authenticatorConfig(); } // Dump the contents of this pipeline if requested if (log.isDebugEnabled()) { log.debug("Pipeline Configuration:"); Pipeline pipeline = context.getPipeline(); Valve valves[] = null; if (pipeline != null) { valves = pipeline.getValves(); } if (valves != null) { for (Valve valve : valves) { log.debug(" " + valve.getClass().getName()); } } log.debug("======================"); } // Make our application available if no problems were encountered if (ok) { context.setConfigured(true); } else { log.error(sm.getString("contextConfig.unavailable")); context.setConfigured(false); } }
webConfig()方法, 会去解析 web.xml 文件. servlet3.0之前, 都是将 servlet 配置在web.xml文件中, servlet3.0之后, 出现了注解的方式.
protected void webConfig() { //web.xml解析器 WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(), context.getXmlValidation(), context.getXmlBlockExternal()); Set<WebXml> defaults = new HashSet<>(); defaults.add(getDefaultWebXmlFragment(webXmlParser)); //web.xml解析结果, 放到 WebXml 对象中 WebXml webXml = createWebXml(); // Parse context level web.xml InputSource contextWebXml = getContextWebXmlSource(); if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) { ok = false; } ServletContext sContext = context.getServletContext(); // Ordering is important here // Step 1. Identify all the JARs packaged with the application and those // provided by the container. If any of the application JARs have a // web-fragment.xml it will be parsed at this point. web-fragment.xml // files are ignored for container provided JARs. Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser); // Step 2. Order the fragments. Set<WebXml> orderedFragments = null; orderedFragments = WebXml.orderWebFragments(webXml, fragments, sContext); // Step 3. Look for ServletContainerInitializer implementations if (ok) { processServletContainerInitializers(); } if (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) { // Steps 4 & 5. processClasses(webXml, orderedFragments); } if (!webXml.isMetadataComplete()) { ...... } else { webXml.merge(defaults); convertJsps(webXml); //配置方法, 会调用 context.addChild()方法 configureContext(webXml); } ...... }
进 configureContext() 方法看
private void configureContext(WebXml webxml) { // As far as possible, process in alphabetical order so it is easy to // check everything is present // Some validation depends on correct public ID context.setPublicId(webxml.getPublicId()); // Everything else in order context.setEffectiveMajorVersion(webxml.getMajorVersion()); context.setEffectiveMinorVersion(webxml.getMinorVersion()); for (Entry<String, String> entry : webxml.getContextParams().entrySet()) { context.addParameter(entry.getKey(), entry.getValue()); } context.setDenyUncoveredHttpMethods( webxml.getDenyUncoveredHttpMethods()); context.setDisplayName(webxml.getDisplayName()); context.setDistributable(webxml.isDistributable()); for (ContextLocalEjb ejbLocalRef : webxml.getEjbLocalRefs().values()) { context.getNamingResources().addLocalEjb(ejbLocalRef); } for (ContextEjb ejbRef : webxml.getEjbRefs().values()) { context.getNamingResources().addEjb(ejbRef); } for (ContextEnvironment environment : webxml.getEnvEntries().values()) { context.getNamingResources().addEnvironment(environment); } for (ErrorPage errorPage : webxml.getErrorPages().values()) { context.addErrorPage(errorPage); } for (FilterDef filter : webxml.getFilters().values()) { if (filter.getAsyncSupported() == null) { filter.setAsyncSupported("false"); } context.addFilterDef(filter); } for (FilterMap filterMap : webxml.getFilterMappings()) { context.addFilterMap(filterMap); } context.setJspConfigDescriptor(webxml.getJspConfigDescriptor()); for (String listener : webxml.getListeners()) { context.addApplicationListener(listener); } for (Entry<String, String> entry : webxml.getLocaleEncodingMappings().entrySet()) { context.addLocaleEncodingMappingParameter(entry.getKey(), entry.getValue()); } // Prevents IAE if (webxml.getLoginConfig() != null) { context.setLoginConfig(webxml.getLoginConfig()); } for (MessageDestinationRef mdr : webxml.getMessageDestinationRefs().values()) { context.getNamingResources().addMessageDestinationRef(mdr); } // messageDestinations were ignored in Tomcat 6, so ignore here context.setIgnoreAnnotations(webxml.isMetadataComplete()); for (Entry<String, String> entry : webxml.getMimeMappings().entrySet()) { context.addMimeMapping(entry.getKey(), entry.getValue()); } // Name is just used for ordering for (ContextResourceEnvRef resource : webxml.getResourceEnvRefs().values()) { context.getNamingResources().addResourceEnvRef(resource); } for (ContextResource resource : webxml.getResourceRefs().values()) { context.getNamingResources().addResource(resource); } boolean allAuthenticatedUsersIsAppRole = webxml.getSecurityRoles().contains( SecurityConstraint.ROLE_ALL_AUTHENTICATED_USERS); for (SecurityConstraint constraint : webxml.getSecurityConstraints()) { if (allAuthenticatedUsersIsAppRole) { constraint.treatAllAuthenticatedUsersAsApplicationRole(); } context.addConstraint(constraint); } for (String role : webxml.getSecurityRoles()) { context.addSecurityRole(role); } for (ContextService service : webxml.getServiceRefs().values()) { context.getNamingResources().addService(service); } for (ServletDef servlet : webxml.getServlets().values()) { Wrapper wrapper = context.createWrapper(); // Description is ignored // Display name is ignored // Icons are ignored // jsp-file gets passed to the JSP Servlet as an init-param if (servlet.getLoadOnStartup() != null) { wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue()); } if (servlet.getEnabled() != null) { wrapper.setEnabled(servlet.getEnabled().booleanValue()); } wrapper.setName(servlet.getServletName()); Map<String,String> params = servlet.getParameterMap(); for (Entry<String, String> entry : params.entrySet()) { wrapper.addInitParameter(entry.getKey(), entry.getValue()); } wrapper.setRunAs(servlet.getRunAs()); Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs(); for (SecurityRoleRef roleRef : roleRefs) { wrapper.addSecurityReference( roleRef.getName(), roleRef.getLink()); } wrapper.setServletClass(servlet.getServletClass()); MultipartDef multipartdef = servlet.getMultipartDef(); if (multipartdef != null) { long maxFileSize = -1; long maxRequestSize = -1; int fileSizeThreshold = 0; if(null != multipartdef.getMaxFileSize()) { maxFileSize = Long.parseLong(multipartdef.getMaxFileSize()); } if(null != multipartdef.getMaxRequestSize()) { maxRequestSize = Long.parseLong(multipartdef.getMaxRequestSize()); } if(null != multipartdef.getFileSizeThreshold()) { fileSizeThreshold = Integer.parseInt(multipartdef.getFileSizeThreshold()); } wrapper.setMultipartConfigElement(new MultipartConfigElement( multipartdef.getLocation(), maxFileSize, maxRequestSize, fileSizeThreshold)); } if (servlet.getAsyncSupported() != null) { wrapper.setAsyncSupported( servlet.getAsyncSupported().booleanValue()); } wrapper.setOverridable(servlet.isOverridable()); //加入节点Servlet StandardContext#addChild(StandardWrapper) context.addChild(wrapper); } for (Entry<String, String> entry : webxml.getServletMappings().entrySet()) { context.addServletMappingDecoded(entry.getKey(), entry.getValue()); } SessionConfig sessionConfig = webxml.getSessionConfig(); if (sessionConfig != null) { if (sessionConfig.getSessionTimeout() != null) { context.setSessionTimeout( sessionConfig.getSessionTimeout().intValue()); } SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig(); scc.setName(sessionConfig.getCookieName()); scc.setDomain(sessionConfig.getCookieDomain()); scc.setPath(sessionConfig.getCookiePath()); scc.setComment(sessionConfig.getCookieComment()); if (sessionConfig.getCookieHttpOnly() != null) { scc.setHttpOnly(sessionConfig.getCookieHttpOnly().booleanValue()); } if (sessionConfig.getCookieSecure() != null) { scc.setSecure(sessionConfig.getCookieSecure().booleanValue()); } if (sessionConfig.getCookieMaxAge() != null) { scc.setMaxAge(sessionConfig.getCookieMaxAge().intValue()); } if (sessionConfig.getSessionTrackingModes().size() > 0) { context.getServletContext().setSessionTrackingModes( sessionConfig.getSessionTrackingModes()); } } // Context doesn't use version directly for (String welcomeFile : webxml.getWelcomeFiles()) { /* * The following will result in a welcome file of "" so don't add * that to the context * <welcome-file-list> * <welcome-file/> * </welcome-file-list> */ if (welcomeFile != null && welcomeFile.length() > 0) { context.addWelcomeFile(welcomeFile); } } // Do this last as it depends on servlets for (JspPropertyGroup jspPropertyGroup : webxml.getJspPropertyGroups()) { String jspServletName = context.findServletMapping("*.jsp"); if (jspServletName == null) { jspServletName = "jsp"; } if (context.findChild(jspServletName) != null) { for (String urlPattern : jspPropertyGroup.getUrlPatterns()) { context.addServletMappingDecoded(urlPattern, jspServletName, true); } } else { if(log.isDebugEnabled()) { for (String urlPattern : jspPropertyGroup.getUrlPatterns()) { log.debug("Skipping " + urlPattern + " , no servlet " + jspServletName); } } } } for (Entry<String, String> entry : webxml.getPostConstructMethods().entrySet()) { context.addPostConstructMethod(entry.getKey(), entry.getValue()); } for (Entry<String, String> entry : webxml.getPreDestroyMethods().entrySet()) { context.addPreDestroyMethod(entry.getKey(), entry.getValue()); } }
1. 对 Context 设置了一些属性, 如过滤器, 错误页等
2. 创建 Wrapper , 并设置一些属性, 如 LoadOnStartup
3. 通过 context.addChild(wrapper), 将 servlet 加入到 context 的子节点上.
StandardContext.addChild()
public void addChild(Container child) { // Global JspServlet Wrapper oldJspServlet = null; if (!(child instanceof Wrapper)) { throw new IllegalArgumentException (sm.getString("standardContext.notWrapper")); } boolean isJspServlet = "jsp".equals(child.getName()); // Allow webapp to override JspServlet inherited from global web.xml. if (isJspServlet) { oldJspServlet = (Wrapper) findChild("jsp"); if (oldJspServlet != null) { removeChild(oldJspServlet); } } super.addChild(child); if (isJspServlet && oldJspServlet != null) { /* * The webapp-specific JspServlet inherits all the mappings * specified in the global web.xml, and may add additional ones. */ String[] jspMappings = oldJspServlet.findMappings(); for (int i=0; jspMappings!=null && i<jspMappings.length; i++) { addServletMappingDecoded(jspMappings[i], child.getName()); } } }
这里的 super.addChild(child) 和 StandardHost里面, 调用的 super.addChild() 是一样的.
@Override public void addChild(Container child) { if (Globals.IS_SECURITY_ENABLED) { PrivilegedAction<Void> dp = new PrivilegedAddChild(child); AccessController.doPrivileged(dp); } else { addChildInternal(child); } } //在解析 Server.xml 文件的时候, 通过 StandardEngine.addChild() 一路调用过来 //<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true" startStopThreads="1"> //此时, 不会调用 start() 方法 private void addChildInternal(Container child) { if( log.isDebugEnabled() ) log.debug("Add child " + child + " " + this); synchronized(children) { if (children.get(child.getName()) != null) throw new IllegalArgumentException("addChild: Child name '" + child.getName() + "' is not unique"); child.setParent(this); // May throw IAE children.put(child.getName(), child); } // Start child // Don't do this inside sync block - start can be a slow process and // locking the children object can cause problems elsewhere try { if ((getState().isAvailable() || LifecycleState.STARTING_PREP.equals(getState())) && startChildren) { //addChild的时候, 调用启动方法, 解析Server.xml时, 不会进此方法 //如果子类没有重写start方法, 则会调用 LifecycleBase#start() //StandardHost的时候, 会进此方法, 调用的是 StandardContext.start() -> StandardContext.startInternal() //StandardContext的时候, 会进此方法, 调用的是 StandardWrapper.start() -> StandardWrapper.startInternal() child.start(); } } catch (LifecycleException e) { log.error("ContainerBase.addChild: start: ", e); throw new IllegalStateException("ContainerBase.addChild: start: " + e); } finally { fireContainerEvent(ADD_CHILD_EVENT, child); } }
由于 StandardWrapper 没有重写 start() 方法, 所以会调用 LifecycleBase#start()
public final synchronized void start() throws LifecycleException { if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) || LifecycleState.STARTED.equals(state)) { if (log.isDebugEnabled()) { Exception e = new LifecycleException(); log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e); } else if (log.isInfoEnabled()) { log.info(sm.getString("lifecycleBase.alreadyStarted", toString())); } return; } if (state.equals(LifecycleState.NEW)) { //results.add(startStopExecutor.submit(new StartChild(child))); //异步执行 StandardHost.start() 方法时, 会走这里 init(); } else if (state.equals(LifecycleState.FAILED)) { stop(); } else if (!state.equals(LifecycleState.INITIALIZED) && !state.equals(LifecycleState.STOPPED)) { invalidTransition(Lifecycle.BEFORE_START_EVENT); } try { setStateInternal(LifecycleState.STARTING_PREP, null, false); startInternal(); if (state.equals(LifecycleState.FAILED)) { // This is a 'controlled' failure. The component put itself into the // FAILED state so call stop() to complete the clean-up. stop(); } else if (!state.equals(LifecycleState.STARTING)) { // Shouldn't be necessary but acts as a check that sub-classes are // doing what they are supposed to. invalidTransition(Lifecycle.AFTER_START_EVENT); } else { setStateInternal(LifecycleState.STARTED, null, false); } } catch (Throwable t) { // This is an 'uncontrolled' failure so put the component into the // FAILED state and throw an exception. handleSubClassException(t, "lifecycleBase.startFail", toString()); } }
StandardWrapper中, 没有重写 initInternal(), 所以会调用 org.apache.catalina.core.ContainerBase#initInternal(), 创建一个阻塞队列线程池
所以, 这里只需要看 startInternal() 方法即可
StandardWrapper#startInternal()
protected synchronized void startInternal() throws LifecycleException { // Send j2ee.state.starting notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.starting", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } // Start up this component super.startInternal(); setAvailable(0L); // Send j2ee.state.running notification if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.running", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); } }
StandardWrapper#startInternal() 到这里就完结了, 因为他是Servlet, 是最后一级, 所以也没有子节点了.