WEB-INF/web.xml
一个比较常见的 web.xml 配置,可以看到配置了一个 listener 实现, ContextLoaderListener。
spring-action contextConfigLocation classpath*:/spring/my-application-context.xml org.springframework.web.context.ContextLoaderListener
javax.servlet.ServletContextListener
ServletContextListener 来自于 javax.servlet 包,是 tomcat 与 spring 的衔接接口,可以监听 tomcat servletContext 相关事件,拥有操作 servlet 上下文的能力。
package javax.servlet;import java.util.EventListener;/*** 是 tomcat 与 spring 的衔接接口,可以监听 tomcat servletContext 相关事件,拥有操作 servlet 上下文的能力。*/
public interface ServletContextListener extends EventListener {/*** 初始化上下文*/void contextInitialized(ServletContextEvent var1);/*** 销毁上下文*/void contextDestroyed(ServletContextEvent var1);
}
org.springframework.web.context.ContextLoaderListener
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {public ContextLoaderListener() {}public ContextLoaderListener(WebApplicationContext context) {super(context);}/*** Initialize the root web application context.** 初始化 web 应用上下文*/@Overridepublic void contextInitialized(ServletContextEvent event) {initWebApplicationContext(event.getServletContext());}/*** Close the root web application context.** 销毁 web 应用上下文*/@Overridepublic void contextDestroyed(ServletContextEvent event) {closeWebApplicationContext(event.getServletContext());ContextCleanupListener.cleanupAttributes(event.getServletContext());}
}
org.springframework.web.context.ContextLoader#initWebApplicationContext
初始化 web 应用上下文
/*** Initialize Spring's web application context for the given servlet context,* using the application context provided at construction time, or creating a new one* according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and* "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.** 初始化 web 应用上下文** @param servletContext current servlet context* @return the new WebApplicationContext* @see #ContextLoader(WebApplicationContext)* @see #CONTEXT_CLASS_PARAM* @see #CONFIG_LOCATION_PARAM*/public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {// ...// Store context in local instance variable, to guarantee that// it is available on ServletContext shutdown.// 创建当前应用上下文if (this.context == null) {this.context = createWebApplicationContext(servletContext);}if (this.context instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;if (!cwac.isActive()) {// The context has not yet been refreshed -> provide services such as// setting the parent context, setting the application context id, etcif (cwac.getParent() == null) {// The context instance was injected without an explicit parent ->// determine parent for root web application context, if any.ApplicationContext parent = loadParentContext(servletContext);cwac.setParent(parent);}// 配置和刷新当前容器configureAndRefreshWebApplicationContext(cwac, servletContext);}}servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);// ...}
org.springframework.web.context.ContextLoader#configureAndRefreshWebApplicationContext
配置和刷新当前容器。
/*** 配置和刷新当前容器*/protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {if (ObjectUtils.identityToString(wac).equals(wac.getId())) {// The application context id is still set to its original default value// -> assign a more useful id based on available informationString idParam = sc.getInitParameter(CONTEXT_ID_PARAM);if (idParam != null) {wac.setId(idParam);}else {// Generate default id...wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +ObjectUtils.getDisplayString(sc.getContextPath()));}}wac.setServletContext(sc);String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);if (configLocationParam != null) {wac.setConfigLocation(configLocationParam);}// The wac environment's #initPropertySources will be called in any case when the context// is refreshed; do it eagerly here to ensure servlet property sources are in place for// use in any post-processing or initialization that occurs below prior to #refreshConfigurableEnvironment env = wac.getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment) env).initPropertySources(sc, null);}customizeContext(sc, wac);/** 调用 ConfigurableWebApplicationContext#refresh 方法,具体实现是 AbstractApplicationContext#refresh* 其实 AbstractApplicationContext#refresh 方法也是被具体的应用上下文实现通过继承的方式调用的,* 具体的应用上下文是什么可以看 ContextLoader#initWebApplicationContext 方法中调用的 ContextLoader#createWebApplicationContext 方法。* 默认使用的应用上下文是 XmlWebApplicationContext*/wac.refresh();}
org.springframework.web.context.ContextLoader#createWebApplicationContext
创建指定的应用上下文
/*** Instantiate the root WebApplicationContext for this loader, either the* default context class or a custom context class if specified.* This implementation expects custom contexts to implement the* {@link ConfigurableWebApplicationContext} interface.* Can be overridden in subclasses.*
In addition, {@link #customizeContext} gets called prior to refreshing the* context, allowing subclasses to perform custom modifications to the context.** 创建指定的应用上下文** @param sc current servlet context* @return the root WebApplicationContext* @see ConfigurableWebApplicationContext*/protected WebApplicationContext createWebApplicationContext(ServletContext sc) {// 获取应用上下文的 clazzClass> contextClass = determineContextClass(sc);if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Custom context class [" + contextClass.getName() +"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");}// 创建应用上下文实例return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);}
org.springframework.web.context.ContextLoader#determineContextClass
获取应用上下文的 clazz
/*** Return the WebApplicationContext implementation class to use, either the* default XmlWebApplicationContext or a custom context class if specified.** 获取应用上下文的 clazz** @param servletContext current servlet context* @return the WebApplicationContext implementation class to use* @see #CONTEXT_CLASS_PARAM* @see org.springframework.web.context.support.XmlWebApplicationContext*/protected Class> determineContextClass(ServletContext servletContext) {// 尝试获取 servletContext 中指定的应用上下文名称String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);/** 如果 servletContext 中指定了,则获取指定的应用上下文 clazz。指定方式和 web.xml 中设置的两个参数有关 contextClass, contextConfigLocation* 指定的应用上下文来源,是在 web.xml 中设置的。contextClass 和 contextConfigLocation*/if (contextClassName != null) {try {return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());}catch (ClassNotFoundException ex) {throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", ex);}}/** 如果 servletContext 中未指定,则获取默认的应用上下文 clazz* 默认的应用上下文来源,在 defaultStrategies 中获得的,要看 defaultStrategies 初始化的位置,在 ContextLoader 的 static 代码块中*/else {contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());try {return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());}catch (ClassNotFoundException ex) {throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", ex);}}}
默认的应用上下文来源,在 defaultStrategies 中获得的,要看 defaultStrategies 初始化的位置,在 ContextLoader 的 static 代码块中。
/*** Name of the class path resource (relative to the ContextLoader class)* that defines ContextLoader's default strategy names.*/private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";private static final Properties defaultStrategies;static {// Load default strategy implementations from properties file.// This is currently strictly internal and not meant to be customized// by application developers.try {ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);}catch (IOException ex) {throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());}}
org/springframework/web/context/ContextLoader.properties
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
指定使用的应用上下文,指定方式和 web.xml 中设置的两个参数有关 contextClass, contextConfigLocation
指定应用上下文的 class,不同的 contextClass,使用的配置文件类型不同(例如 xml 或 java 配置类):
org.springframework.web.context.support.XmlWebApplicationContext,使用的配置文件类型是 xml,这是传统的 spring 应用使用的应用上下文。
org.springframework.web.context.support.AnnotationConfigWebApplicationContext,使用的配置文件类型是 java 配置类,这是 spring boot 应用使用的应用上下文。
用来指定配置文件所在的位置: