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

来源:互联网 发布:七层网络和四层模型 编辑:程序博客网 时间:2024/05/19 02:31

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 lifecycleif (namingResources != null) {namingResources.start();}// Add missing components as necessaryif (webappResources == null) {   // (1) Required by Loaderif (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 mappergetCharsetMapper();// Post work directorypostWorkDirectory();// Validate required extensionsboolean 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 failsok = false;}// Reading the "catalina.useNaming" environment variableString 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 startupif (log.isDebugEnabled())log.debug("Processing standard container startup");// Binding threadClassLoader oldCCL = bindThread();try {if (ok) {// Start our subordinate components, if anyif ((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 classloaderunbindThread(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.xmlfireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);// Start our child containers, if not already startedfor (Container child : findChildren()) {if (!child.getState().isAvailable()) {child.start();}}// Start the Valves in our pipeline (including the basic),// if anyif (pipeline instanceof Lifecycle) {((Lifecycle) pipeline).start();}// Acquire clustered managerManager 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 specifiedif (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 managergetCluster().registerManager(manager);}}} finally {// Unbinding threadunbindThread(oldCCL);}if (!getConfigured()) {log.error(sm.getString("standardContext.configurationFail"));ok = false;}// We put the resources into the servlet contextif (ok)getServletContext().setAttribute(Globals.RESOURCES_ATTR, getResources());// Initialize associated mappermapper.setContext(getPath(), welcomeFiles, resources);// Binding threadoldCCL = 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 requiredif (ok) {getServletContext().setAttribute(JarScanner.class.getName(), getJarScanner());}// Set up the context init paramsmergeParameters();// Call ServletContainerInitializersfor (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 managerif ((manager != null) && (manager instanceof Lifecycle)) {((Lifecycle) getManager()).start();}} catch(Exception e) {log.error(sm.getString("standardContext.managerFail"), e);ok = false;}// 配置和初始化filtersif (ok) {if (!filterStart()) {log.error(sm.getString("standardContext.filterFail"));ok = false;}}// 实例化及初始化所有带<load-on-startup>配置的servletsif (ok) {if (!loadOnStartup(findChildren())){log.error(sm.getString("standardContext.servletFail"));ok = false;}}// Start ContainerBackgroundProcessor threadsuper.threadStart();} finally {// Unbinding threadunbindThread(oldCCL);}// Set available status depending upon startup successif (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 startupif (getLoader() instanceof WebappLoader) {((WebappLoader) getLoader()).closeJARs(true);}// Reinitializing if something went wrongif (!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 withtry {context = (Context) event.getLifecycle();} catch (ClassCastException e) {log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);return;}// Process the event that has occurredif (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 toolsif (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中,进而配置给StandardContextwebConfig();if (!context.getIgnoreAnnotations()) {applicationAnnotationsConfig();}if (ok) {validateSecurityRoles();}// Configure an authenticator if we need oneif (ok)authenticatorConfig();// Dump the contents of this pipeline if requestedif ((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 encounteredif (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.defaultdefaults.add(getDefaultWebXmlFragment());WebXml webXml = createWebXml();// 解析tomcat\webapps\Context名称\WEB-INF\web.xmlInputSource 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.xmlMap<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 implementationsif (ok) {processServletContainerInitializers();}if  (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {// Step 4. Process /WEB-INF/classes for annotationsif (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 useif (ok) {processAnnotations(orderedFragments, webXml.isMetadataComplete());}// Cache, if used, is no longer required so clear itjavaClassCache.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.defaultwebXml.merge(defaults);// Step 8. Convert explicitly mentioned jsps to servletsif (ok) {convertJsps(webXml);}// Step 9. Apply merged web.xml to Context// WebXml配置给StandardContextif (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 JasperString 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 JARsif (ok) {// Spec does not define an order.// Use ordered JARs followed by remaining JARsSet<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// contextif (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 0
原创粉丝点击