1.Spring 源码学习—web.xml 之 ClassLoaderListener

web.xml 的 ClassLoaderListener 初始化 Spring 容器

1.web.xml

  <!-- spring 的 ContextLoaderListener 是一个servlet 监听器,是最常用的上下文载入器之一 -->
  <listener>
  	<listener-class>org.Springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <context-param>
 	<!-- ContextLoaderListener 里的一个参数,告知配置文件的位置 -->
  	<param-name>contextConfigLocation</param-name>
  	<param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  
  
  <!-- DispatcherServlet 也是上下文载入器之一  -->
  <servlet>
  	<servlet-name>Springmvc</servlet-name>
  	<servlet-class>org.Springframework.web.servlet.DispatcherServlet</servlet-class>
  	<!-- DispatcherServlet 初始化时会默认从与 web.xml 同目录下读取 Spring-servlet.xml 文件 -->
  	<load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
  	<servlet-name>Springmvc</servlet-name>
  	<url-pattern>*.html</url-pattern>
  </servlet-mapping>

 

2.ContextLoaderListener

ContextLoaderListener 源码

  • ContextLoaderListener 实现了 ServletContextListener 接口,可以在 web 容器启动时自动装配 ApplicationContext 的配置信息。容器的上下文即是 ServletContext。
  • 启动 web 容器时系统会调用 ServletContextListener 接口的实现类的 contextInitialized 方法。在 ContextLoaderListener 里此方法里便是 initWebApplicationContext(event.getServletContext())。
  • initWebApplicationContext 是继承于 ContextLoader 的方法。event.getServletContext() 即是上下文 ServletContext。
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {

	public ContextLoaderListener() {
	}

	public ContextLoaderListener(WebApplicationContext context) {
		super(context);
	}

	/**
	 * Initialize the root web application context.
	 */
	@Override
	public void contextInitialized(ServletContextEvent event) {
     // initWebApplicationContext 是 ContextLoader 的方法
		initWebApplicationContext(event.getServletContext());
	}

	@Override
	public void contextDestroyed(ServletContextEvent event) {
		closeWebApplicationContext(event.getServletContext());
		ContextCleanupListener.cleanupAttributes(event.getServletContext());
	}
}

 

ContextLoader 部分源码

public class ContextLoader {
    ...
    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());
		}
	}
    ...
 }
  • java 加载此 ContextLoader 类时会读取与此类同目录的一个配置文件文件 ContextLoader.properties,即 ContextLoader 的成员变量 defaultStrategies 得到了值是 “org.springframework.web.context.support.XmlWebApplicationContext”。此值在下面讲解中有用处。
// ContextLoader.properties 内容
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

 

3.ContextLoder 类的多个方法

initWebApplicationContext 方法源码

  • 查询上下文 ServletContext 中是否存在 key=WebApplicationContext.ROOT 的 value 值,只允许一个。第一次调用此方法肯定没有 value 值。
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
			throw new IllegalStateException(
					"Cannot initialize context because there is already a root application context present - " +
					"check whether you have multiple ContextLoader* definitions in your web.xml!");
		}

		Log logger = LogFactory.getLog(ContextLoader.class);
		servletContext.log("Initializing Spring root WebApplicationContext");
		if (logger.isInfoEnabled()) {
			logger.info("Root WebApplicationContext: initialization started");
		}
		long startTime = System.currentTimeMillis();

		try {
			// 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, etc
					if (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);

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}

			if (logger.isDebugEnabled()) {
				logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
						WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
			}
			if (logger.isInfoEnabled()) {
				long elapsedTime = System.currentTimeMillis() - startTime;
				logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
			}

			return this.context;
		}
		catch (RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
			throw ex;
		}
		catch (Error err) {
			logger.error("Context initialization failed", err);
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
			throw err;
		}
	}

 

createWebApplicationContext 方法源码

  • 调用 createWebApplicationContext 方法创建 WebApplicationContext 实例(根据下面源码来说,其实就是 WebApplicationContext contextClass =(ConfigurableWebApplicationContext) (new XmlWebApplicationContext()),但实际是通过反射生成 )。
	protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
		Class<?> 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);
	}

 

determineContextClass 方法源码

  • determineContextClass 方法里默认策略获取类的名字便是上面说的 ContextLoader.properties 里的 org.springframework.web.context.support.XmlWebApplicationContext,返回类类型。
  • 先查找 web.xml 中<context-param> 定义的 contextClass 的值作为 Spring 上下文的初始化类,但没有找到。便会默认使用 XmlWebApplicationContext。
protected Class<?> determineContextClass(ServletContext servletContext) {
         // 成员变量 CONTEXT_CLASS_PARAM="contextClass",web.xml 中<context-param> 没有这个定义,故不会加载
		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
		if (contextClassName != null) {
			try {
				return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load custom context class [" + contextClassName + "]", ex);
			}
		}
		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);
			}
		}
	}

 

  • 接下来就是 Spring 容器的初始化工作了,涉及到 configureAndRefreshWebApplicationContext 方法
  • 成员变量 CONFIG_LOCATION_PARAM 便是 web.xml 中的定义在 <context-param> 中的 contextConfigLocation 变量,从而得到 Spring 初始化实例的配置文件的路径。
  • servletContext.getInitParameter 是用来获取 web.xml 中配置的 <context-param> 的值。
	// 成员变量 CONFIG_LOCATION_PARAM = "contextConfigLocation"
     protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		...
		wac.setServletContext(sc);
		String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (configLocationParam != null) {
			wac.setConfigLocation(configLocationParam);
		}
            ...
	}

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值