tomcat启动整体时序图
fireLifecycleEvent时序图
从“tomcat启动整体时序图”可以看出,web.xml解析发生在StandardContext startInternal()的fireLifecycleEvent环节,具体时序图如下:
其中ContextConfig监听器是tomcat启动解析conf/server.xml时加到StandardContext的。
相关源码
- /** StandardContext.java */
- // StandardContext启动
- 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();
- }
- // Add missing components as necessary
- if (webappResources == null) { // (1) Required by Loader
- if (log.isDebugEnabled())
- log.debug("Configuring default Resources");
- try {
- String docBase = getDocBase();
- if (docBase == null) {
- setResources(new EmptyDirContext());
- } else if (docBase.endsWith(".war")
- && !(new File(getBasePath())).isDirectory()) {
- setResources(new WARDirContext());
- } else {
- setResources(new FileDirContext());
- }
- } catch (IllegalArgumentException e) {
- log.error(sm.getString("standardContext.resourcesInit"), e);
- ok = false;
- }
- }
- if (ok) {
- if (!resourcesStart()) {
- throw new LifecycleException("Error in resourceStart()");
- }
- }
- if (getLoader() == null) {
- WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
- webappLoader.setDelegate(getDelegate());
- setLoader(webappLoader);
- }
- // Initialize character set mapper
- getCharsetMapper();
- // Post work directory
- postWorkDirectory();
- // 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 depency 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
- if ((loader != null) && (loader instanceof Lifecycle))
- ((Lifecycle) loader).start();
- // since the loader just started, the webapp classloader is now
- // created.
- // 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();
- if ((cluster != null) && (cluster instanceof Lifecycle))
- ((Lifecycle) cluster).start();
- Realm realm = getRealmInternal();
- if ((realm != null) && (realm instanceof Lifecycle))
- ((Lifecycle) realm).start();
- if ((resources != null) && (resources instanceof Lifecycle))
- ((Lifecycle) resources).start();
- // 触发CONFIGURE_START_EVENT事件,加载web.xml
- 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;
- 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("standardContext.clusterFail", 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);
- }
- }
- } finally {
- // Unbinding thread
- unbindThread(oldCCL);
- }
- 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());
- // Initialize associated mapper
- mapper.setContext(getPath(), welcomeFiles, resources);
- // Binding thread
- oldCCL = bindThread();
- 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 NamingResources(): getNamingResources());
- setInstanceManager(new DefaultInstanceManager(context,
- injectionMap, this, this.getClass().getClassLoader()));
- getServletContext().setAttribute(
- InstanceManager.class.getName(), getInstanceManager());
- }
- }
- try {
- // 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;
- }
- }
- // 实例化listeners及其初始化
- if (ok) {
- if (!listenerStart()) {
- log.error(sm.getString("standardContext.listenerFail"));
- ok = false;
- }
- }
- try {
- // Start manager
- if ((manager != null) && (manager instanceof Lifecycle)) {
- ((Lifecycle) getManager()).start();
- }
- } catch(Exception e) {
- log.error(sm.getString("standardContext.managerFail"), e);
- ok = false;
- }
- // 配置和初始化filters
- if (ok) {
- if (!filterStart()) {
- log.error(sm.getString("standardContext.filterFail"));
- ok = false;
- }
- }
- // 实例化及初始化所有带<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);
- }
- // Close all JARs right away to avoid always opening a peak number
- // of files on startup
- if (getLoader() instanceof WebappLoader) {
- ((WebappLoader) getLoader()).closeJARs(true);
- }
- // Reinitializing if something went wrong
- if (!ok) {
- setState(LifecycleState.FAILED);
- } else {
- setState(LifecycleState.STARTING);
- }
- }
- /** LifecycleBase.java */
- protected void fireLifecycleEvent(String type, Object data) {
- lifecycle.fireLifecycleEvent(type, data);
- }
- /** LifecycleSupport.java */
- public void fireLifecycleEvent(String type, Object data) {
- LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
- LifecycleListener interested[] = listeners;
- for (int i = 0; i < interested.length; i++)
- interested[i].lifecycleEvent(event);
- }
- /** ContextConfig.java */
- 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(); // 响应CONFIGURE_START_EVENT事件
- } 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();
- }
- }
- /**
- * Process a "contextConfig" event for this Context.
- */
- 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())));
- }
- // 解析tomcat/conf/web.xml、tomcat\conf\Catalina\web.xml.default、tomcat\webapps\Context名称\WEB-INF\web.xml
- // 及tomcat/lib下的所有jar中的web-fragment.xml,将所有这些web.xml配置整合到WebXml中,进而配置给StandardContext
- webConfig();
- 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()) && (context instanceof ContainerBase)) {
- log.debug("Pipeline 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.debug(" " + valves[i].getInfo());
- }
- }
- 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);
- }
- }
- protected void webConfig() {
- /*
- * Anything and everything can override the global and host defaults.
- * This is implemented in two parts
- * - Handle as a web fragment that gets added after everything else so
- * everything else takes priority
- * - Mark Servlets as overridable so SCI configuration can replace
- * configuration from the defaults
- */
- /*
- * The rules for annotation scanning are not as clear-cut as one might
- * think. Tomcat implements the following process:
- * - As per SRV.1.6.2, Tomcat will scan for annotations regardless of
- * which Servlet spec version is declared in web.xml. The EG has
- * confirmed this is the expected behaviour.
- * - As per http://java.net/jira/browse/SERVLET_SPEC-36, if the main
- * web.xml is marked as metadata-complete, JARs are still processed
- * for SCIs.
- * - If metadata-complete=true and an absolute ordering is specified,
- * JARs excluded from the ordering are also excluded from the SCI
- * processing.
- * - If an SCI has a @HandlesType annotation then all classes (except
- * those in JARs excluded from an absolute ordering) need to be
- * scanned to check if they match.
- */
- Set<WebXml> defaults = new HashSet<WebXml>();
- // 解析tomcat/conf/web.xml、tomcat\conf\Catalina\web.xml.default
- defaults.add(getDefaultWebXmlFragment());
- WebXml webXml = createWebXml();
- // 解析tomcat\webapps\Context名称\WEB-INF\web.xml
- InputSource contextWebXml = getContextWebXmlSource();
- parseWebXml(contextWebXml, webXml, false);
- ServletContext sContext = context.getServletContext();
- // Ordering is important here
- // Step 1. Identify all the JARs packaged with the application
- // If the JARs have a web-fragment.xml it will be parsed at this
- // point.
- // 解析tomcat/lib下的所有jar中的web-fragment.xml
- Map<String,WebXml> fragments = processJarsForWebFragments(webXml);
- // 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) {
- // Step 4. Process /WEB-INF/classes for annotations
- if (ok) {
- // Hack required by Eclipse's "serve modules without
- // publishing" feature since this backs WEB-INF/classes by
- // multiple locations rather than one.
- NamingEnumeration<Binding> listBindings = null;
- try {
- try {
- listBindings = context.getResources().listBindings(
- "/WEB-INF/classes");
- } catch (NameNotFoundException ignore) {
- // Safe to ignore
- }
- while (listBindings != null &&
- listBindings.hasMoreElements()) {
- Binding binding = listBindings.nextElement();
- if (binding.getObject() instanceof FileDirContext) {
- File webInfClassDir = new File(
- ((FileDirContext) binding.getObject()).getDocBase());
- processAnnotationsFile(webInfClassDir, webXml,
- webXml.isMetadataComplete());
- } else {
- String resource =
- "/WEB-INF/classes/" + binding.getName();
- try {
- URL url = sContext.getResource(resource);
- processAnnotationsUrl(url, webXml,
- webXml.isMetadataComplete());
- } catch (MalformedURLException e) {
- log.error(sm.getString(
- "contextConfig.webinfClassesUrl",
- resource), e);
- }
- }
- }
- } catch (NamingException e) {
- log.error(sm.getString(
- "contextConfig.webinfClassesUrl",
- "/WEB-INF/classes"), e);
- }
- }
- // Step 5. Process JARs for annotations - only need to process
- // those fragments we are going to use
- if (ok) {
- processAnnotations(
- orderedFragments, webXml.isMetadataComplete());
- }
- // Cache, if used, is no longer required so clear it
- javaClassCache.clear();
- }
- if (!webXml.isMetadataComplete()) {
- // Step 6. Merge web-fragment.xml files into the main web.xml
- // file.
- if (ok) {
- // 整合web-fragment.xml配置
- ok = webXml.merge(orderedFragments);
- }
- // Step 7. Apply global defaults
- // Have to merge defaults before JSP conversion since defaults
- // provide JSP servlet definition.
- // 整合tomcat/conf/web.xml、tomcat\conf\Catalina\web.xml.default
- webXml.merge(defaults);
- // Step 8. Convert explicitly mentioned jsps to servlets
- if (ok) {
- convertJsps(webXml);
- }
- // Step 9. Apply merged web.xml to Context
- // WebXml配置给StandardContext
- if (ok) {
- webXml.configureContext(context);
- }
- } else {
- webXml.merge(defaults);
- convertJsps(webXml);
- webXml.configureContext(context);
- }
- // Step 9a. Make the merged web.xml available to other
- // components, specifically Jasper, to save those components
- // from having to re-generate it.
- // TODO Use a ServletContainerInitializer for Jasper
- String mergedWebXml = webXml.toXml();
- sContext.setAttribute(
- org.apache.tomcat.util.scan.Constants.MERGED_WEB_XML,
- mergedWebXml);
- if (context.getLogEffectiveWebXml()) {
- log.info("web.xml:\n" + mergedWebXml);
- }
- // Always need to look for static resources
- // Step 10. Look for static resources packaged in JARs
- if (ok) {
- // Spec does not define an order.
- // Use ordered JARs followed by remaining JARs
- Set<WebXml> resourceJars = new LinkedHashSet<WebXml>();
- for (WebXml fragment : orderedFragments) {
- resourceJars.add(fragment);
- }
- for (WebXml fragment : fragments.values()) {
- if (!resourceJars.contains(fragment)) {
- resourceJars.add(fragment);
- }
- }
- processResourceJARs(resourceJars);
- // See also StandardContext.resourcesStart() for
- // WEB-INF/classes/META-INF/resources configuration
- }
- // Step 11. Apply the ServletContainerInitializer config to the
- // context
- if (ok) {
- for (Map.Entry<ServletContainerInitializer,
- Set<Class<?>>> entry :
- initializerClassMap.entrySet()) {
- if (entry.getValue().isEmpty()) {
- context.addServletContainerInitializer(
- entry.getKey(), null);
- } else {
- context.addServletContainerInitializer(
- entry.getKey(), entry.getValue());
- }
- }
- }
- }