Tomcat(五)web.xml解析

一 Context启动

1.1三种方式构建Context

protected void deployApps() { 
    File appBase = host.getAppBaseFile();
    File configBase = host.getConfigBaseFile();
    String[] filteredAppPaths = filterAppPaths(appBase.list());
    // Deploy XML descriptors from configBase
    deployDescriptors(configBase, configBase.list());
    // Deploy WARs
    deployWARs(appBase, filteredAppPaths);
    // Deploy expanded folders
    deployDirectories(appBase, filteredAppPaths); 
}

在上述三种构建方式中,在构建Context时,都会调用如下一段代码:

Class<?> clazz = Class.forName(host.getConfigClass());
LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
context.addLifecycleListener(listener);
/**
 * The Java class name of the default context configuration class
 * for deployed web applications.
 */
private String configClass =    "org.apache.catalina.startup.ContextConfig";

1.2 结论

Context的启动(start方法调用)就是在host的addChild方法中调用的。所以可以得出以下两点结论:

  • Context构建过程中,会为Context添加一个生命周期监听器ContextConfig
  • Context会在构建成功后,添加到Host组件过程中,启动(start方法调用)
    而web.xml的解析就是通过ContextConfig生命周期监听器完成的。

二 ContextConfig

Context的启动过程,最终会调用到standardContext的startInternal方法,在startInternal方法中,会触发生命周期监听器的Lifecycle.CONFIGURE_START_EVENT事件监听,如下:

// Notify our interested LifecycleListeners
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

fireLifecycleEvent会触发ContextConfig的事件监听如下:

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

所以会调用ContextConfig的configureStart()方法。

2.1 configureStart()

/**
 * 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())));
    } 
    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()) {
        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中。

2.2 webConfig()

/**
 * Scan the web.xml files that apply to the web application and merge them
 * using the rules defined in the spec. For the global web.xml files,
 * where there is duplicate configuration, the most specific level wins. ie
 * an application's web.xml takes precedence over the host level or global
 * web.xml file.
 */
protected void webConfig() {
    WebXmlParser webXmlParser = new WebXmlParser(context.getXmlNamespaceAware(),           context.getXmlValidation(), context.getXmlBlockExternal()); 
    Set<WebXml> defaults = new HashSet<>();    defaults.add(getDefaultWebXmlFragment(webXmlParser)); 
    Set<WebXml> tomcatWebXml = new HashSet<>();   tomcatWebXml.add(getTomcatWebXmlFragment(webXmlParser)); 
    WebXml webXml = createWebXml();
    // Parse context level web.xml
    InputSource contextWebXml = getContextWebXmlSource();
    if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
        ok = false;
    }
    ServletContext sContext = context.getServletContext();
    // Step 1. Identify all the JARs packaged with the application and those
    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()) {
        // Step 6. Merge web-fragment.xml files into the main web.xml
        // file.
        if (ok) {
            ok = webXml.merge(orderedFragments);
        }
 
        // Step 7a
        // merge tomcat-web.xml
        webXml.merge(tomcatWebXml);
 
        // Step 7b. Apply global defaults
        // Have to merge defaults before JSP conversion since defaults
        // provide JSP servlet definition.
        webXml.merge(defaults);
 
        // Step 8. Convert explicitly mentioned jsps to servlets
        if (ok) {
            convertJsps(webXml);
        }
    
        // Step 9. Apply merged web.xml to Context
        if (ok) {
            configureContext(webXml);
        }
    } else {
        webXml.merge(tomcatWebXml);
        webXml.merge(defaults);
        convertJsps(webXml);
        configureContext(webXml);
    }
 
    if (context.getLogEffectiveWebXml()) {
        log.info(sm.getString("contextConfig.effectiveWebXml", webXml.toXml()));
    }
 
    // 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<>(orderedFragments);
        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());
            }
        }
    }
}

这个方法的具体逻辑

  • 合并Tomcat全局web.xml 、当前应用中的web.xml 、web-fragment.xml和web应用的注解中的配置信息
  • 将解析出的各种配置信息(如Servlet配置、Filter配置、Listener配置等)关联到Context对象中
    而上面提到的第九点,是通过configureContext方法完成的。

2.3 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());
    }
    context.setRequestCharacterEncoding(webxml.getRequestCharacterEncoding());
    // 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);
    }
    context.setResponseCharacterEncoding(webxml.getResponseCharacterEncoding());
    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());
        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());
    }
}

这里逻辑就比较清晰了,就是调用Context的add方法、set方法配置Context,其中我们比较熟悉的,比如添加Filter:

for (FilterDef filter : webxml.getFilters().values()) {
    if (filter.getAsyncSupported() == null) {
        filter.setAsyncSupported("false");
    }
    context.addFilterDef(filter);
}
 
for (FilterMap filterMap : webxml.getFilterMappings()) {
    context.addFilterMap(filterMap);
}

添加Listener:

for (String listener : webxml.getListeners()) {
    context.addApplicationListener(listener);
}

添加servlet wrapper:

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());
    context.addChild(wrapper);
}

通过ContextConfig,实现了web.xml的解析,及与Context的关联。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_42242792

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值