Tomcat源码分析:web.xml解析

一、解析web.xml

StandardContext.startInternal()启动过程中,触发Lifecycle.CONFIGURE_START_EVENT 事件:

         fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

通知监听这个事件的监听器,其中包括ContextConfigContextConfig中通过configureStart()方法解析web.xml

ContextConfig类中的webConfig方法完成具体的解析工作:

1、 全局默认配置文件:tomcat目录下conf/web.xml解析为WebXml对象。

2、 具体应用程序下的web.xml解析:例 /examples/WEB-INF/web.xml

3、 扫描应用程序examples下的所有Jar,查找META-INF/web-fragment.xml文件并解析,保存到变量fragments中,格式<String,WebXml>

4、 已经检索到的WebXml配置进行排序。

5、 基于SPI机制查找ServletContainerInitializer的实现。

6、 扫描/WEB-INF/classes下面的类,处理Annotation@ WebServlet,@ WebFilter,@ WebListener。代码片段:

        String className = clazz.getClassName();
        AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
        if (annotationsEntries != null) {
            for (AnnotationEntry ae : annotationsEntries) {
                String type = ae.getAnnotationType();
                if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) {
                    processAnnotationWebServlet(className, ae, fragment);
                }else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) {
                    processAnnotationWebFilter(className, ae, fragment);
                }else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) {
                    fragment.addListener(className);
                } else {
                    // Unknown annotation - ignore
                }
            }
        }

7、扫描jar包中的Annotationweb-fragment.xml所在的jar包。

8、合并配置:所有web-fragment.xml中的配置,合并到应用程序examples中的配置web.xml

9、合并配置:全局默认配置defaults 合并到应用程序的web.xml

10、将JSP转换Servlet

11、将合并后的最终配置web.xml对象webXml应用到servlet容器(ServletContext),装配servlet上下文;

12、将配置信息保存容器中,供其他组件访问,使得其他组件不需要再次重复上面的步骤去获取配置信息了。

13、查找jar包中的静态资源。

14、ServletContainerInitializer应用到上下文。

二、根据合并后的web.xml配置装配Servlet上下文

在上面介绍的webConfig()方法中,用合并后的web.xml配置装配Servlet上下文,具体代码为:

         webXml.configureContext(context);

进入configureContext方法,按顺序截图代码片段:

1、设置contextParam

        for (Entry entry : contextParams.entrySet()) {
            context.addParameter(entry.getKey(), entry.getValue());
        }

2filterlistener设置。Listner被保存到Context的变量applicationListeners中。

        for (FilterDef filter : filters.values()) {
            if (filter.getAsyncSupported() == null) {
                filter.setAsyncSupported("false");
            }
            context.addFilterDef(filter);
        }
        for (FilterMap filterMap : filterMaps) {
            context.addFilterMap(filterMap);
        }
        for (JspPropertyGroup jspPropertyGroup : jspPropertyGroups) {
            JspPropertyGroupDescriptor descriptor =
                new ApplicationJspPropertyGroupDescriptor(jspPropertyGroup);
            context.getJspConfigDescriptor().getJspPropertyGroups().add(
                    descriptor);
        }
        for (String listener : listeners) {
            context.addApplicationListener(listener);
        }

3servletservlet都被封装为Wrapper对象。

      for (ServletDef servlet : servlets.values()) {
            Wrapper wrapper = context.createWrapper();
            //…
            // 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 params = servlet.getParameterMap();
            for (Entry entry : params.entrySet()) {
                wrapper.addInitParameter(entry.getKey(), entry.getValue());
            }
            wrapper.setRunAs(servlet.getRunAs());
            Set roleRefs = servlet.getSecurityRoleRefs();
            for (SecurityRoleRef roleRef : roleRefs) {
                wrapper.addSecurityReference(
                        roleRef.getName(), roleRef.getLink());
            }
            wrapper.setServletClass(servlet.getServletClass());
            //…
            if (servlet.getAsyncSupported() != null) {
                wrapper.setAsyncSupported(
                        servlet.getAsyncSupported().booleanValue());
            }
            wrapper.setOverridable(servlet.isOverridable());
            context.addChild(wrapper);
        }
        for (Entry entry : servletMappings.entrySet()) {
            context.addServletMapping(entry.getKey(), entry.getValue());
        }

从上面的代码可以总结至少两点:

1、 配置项加载顺序:上下文参数->Filter->Listner->Servlet

2、 Servlet容器支持启动时加载(loadOnStartup变量)、是否异步配置(asyncSupported)以及是否可被SCI覆盖配置(overridable)


三、context启动过程

解析和装配web.xmlcontext的过程中,filter和listner只是将字符类型的变量设置到context中了,并没有真正实例化和启动。

StandardContext.startInternal()方法解析完web.xml后,接下来就是各组件的实例化和启动了。

1、 Set up the context init params

2、 调用ServletContainerInitializer

3、Configure and call application event listeners(配置和调用Listener)

4、Configure and call application filters(配置和调用Filters)

5、Load and initialize all "load on startup" servlets,对设置了load on startupServlet进行初始化。

从以上步骤可以看出,主要组件的初始化顺序为:上下文参数->监听器->过滤器->Servlet

protected synchronized void startInternal() throws LifecycleException {
        //...
        if (ok) {
            //...
            // Notify our interested LifecycleListeners(解析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();
            }
            //...
        }
        //...
        // We put the resources into the servlet context
        if (ok)
            getServletContext().setAttribute
                (Globals.RESOURCES_ATTR, getResources());
 
        // Initialize associated mapper
        mapper.setContext(getPath(), welcomeFiles, resources);
        //...
        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
            // 配置和调用listeners
            if (ok) {
                if (!listenerStart()) {
                    log.error(sm.getString("standardContext.listenerFail"));
                    ok = false;
                }
            }
            //...
            // Configure and call application 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);
        }
        //...
        // Reinitializing if something went wrong
        if (!ok) {
            setState(LifecycleState.FAILED);
        } else {
            setState(LifecycleState.STARTING);
        }
} 


四、监听器初始化和启动过程:

在上面分析的StandardContextstartInternal方法中,调用listenerStart方法实例化和启动Listener,具体代码如下:

public boolean listenerStart() {
//...
// Instantiate the required listeners
ApplicationListener listeners[] = applicationListeners;
Object results[] = new Object[listeners.length];
boolean ok = true;
for (int i = 0; i < results.length; i++) {
//...
try {
ApplicationListener listener = listeners[i];
results[i] = getInstanceManager().newInstance(
listener.getClassName());//创建实例
if (listener.isPluggabilityBlocked()) {
noPluggabilityListeners.add(results[i]);
}
} catch (Throwable t) {
//...
}
}
//...
// Sort listeners in two arrays
ArrayList eventListeners = new ArrayList();
ArrayList lifecycleListeners = new ArrayList();
for (int i = 0; i < results.length; i++) {
if ((results[i] instanceof ServletContextAttributeListener)
|| (results[i] instanceof ServletRequestAttributeListener)
|| (results[i] instanceof ServletRequestListener)
|| (results[i] instanceof HttpSessionAttributeListener)) {
eventListeners.add(results[i]);
}
if ((results[i] instanceof ServletContextListener)
|| (results[i] instanceof HttpSessionListener)) {
lifecycleListeners.add(results[i]);
}
}
// Listener instances may have been added directly to this Context by
// ServletContextInitializers and other code via the pluggability APIs.
// Put them these listeners after the ones defined in web.xml and/or
// annotations then overwrite the list of instances with the new, full
// list.
for (Object eventListener: getApplicationEventListeners()) {
eventListeners.add(eventListener);
}
setApplicationEventListeners(eventListeners.toArray());
for (Object lifecycleListener: getApplicationLifecycleListeners()) {
lifecycleListeners.add(lifecycleListener);
if (lifecycleListener instanceof ServletContextListener) {
noPluggabilityListeners.add(lifecycleListener);
}
}
setApplicationLifecycleListeners(lifecycleListeners.toArray());
//... Send application start events
// Ensure context is not null
getServletContext();
context.setNewServletContextListenerAllowed(false);

Object instances[] = getApplicationLifecycleListeners();
if (instances == null || instances.length == 0) {
return ok;
}
ServletContextEvent event = new ServletContextEvent(getServletContext());
ServletContextEvent tldEvent = null;
if (noPluggabilityListeners.size() > 0) {
noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext());
tldEvent = new ServletContextEvent(noPluggabilityServletContext);
}
for (int i = 0; i < instances.length; i++) {
if (instances[i] == null)
continue;
if (!(instances[i] instanceof ServletContextListener))
continue;
ServletContextListener listener =
(ServletContextListener) instances[i];
try {
fireContainerEvent("beforeContextInitialized", listener);
if (noPluggabilityListeners.contains(listener)) {
listener.contextInitialized(tldEvent);//通知context容器正在初始化
} else {
listener.contextInitialized(event);
}
fireContainerEvent("afterContextInitialized", listener);
} catch (Throwable t) {
//...
}
}
return (ok);
}


ServletContextListener监听容器ServletContext的启动,Spring就是通过自定义这种类型的监听器初始化的。它有两个接口:

contextInitialized:通知context容器正在初始化,在filterservlet实例化之前执行。

contextDestroyed:通知servletcontext将要被关闭,在此之前,servletsfilters已经被销毁。




网易云新用户大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者何欢授权发布。