Tomcat启动解析web.xml源码分析

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());
			}
		}
	}
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值