现在的位置: 首页 > 综合 > 正文

springMVC之ContextLoaderListener分析

2014年07月04日 ⁄ 综合 ⁄ 共 6237字 ⁄ 字号 评论关闭

我记得最开始接触javaWeb方面的东西大概是本科二年级吧,好几年以前了。。。那个时候课程作于需要用java写一个web,当时好像特别流行SSH框架吧,然后自己也当然要用上了。。。

好像最开始卡壳就在web.xml,不知道该怎么配置,而且也不知道这些配置都是干啥用的。。

最后好像是直接从网上copy了别人的配置文件也算是跑起来了吧。。。。

其实这篇文章主要是来看看配置文件中的一项:

	<listener> 
        <listener-class> org.springframework.web.context.ContextLoaderListener</listener-class> 
    </listener> 

listener配置项,listener是干啥的呢,其实看了jetty的代码之后就知道了,servlet容器,例如tomcat,jetty啥的,会为其部署的web程序创建其专属的context,这里还有一个特别的东西是servletContext。。。在启动部署的web程序之前,先会为其创建上述的资源,然后在进行上下文的启动,其中包括载入默认的服务器的xml配置,里面有default的servlet什么的,还会载入用户定义的web.xml里面的定义的东西。。包括listener,servlet,以及初始化参数什么的。。

那么在真正启动程序之前,还会先调用listener的contextInitialized方法。。。

这里我们也就知道这个listener是干嘛用的了吧:我们申明的listener会在程序真正启动之前调用listener的contextInitialized方法,因此我们可以将一些初始化的内容放到listener里面来。。。。。

。。。好了,接下来回到springMVC,一般情况下我们在web应用中如果采用了springMVC,那么我们一般情况下会定义一个listener,也就是上面贴出来的。。。它用于为整个web程序创建根WebApplicationContext,它会默认读取WEB-INF目录下的applicationContext.xml文件,载入里面配置的bean什么的。。。而且对于以后DispatcherServlet创建的ApplicationContext,它会将刚刚创建的context设置为parent。。。。。


好了,好像前面的叙述比较多了。。。那么现在开始来看看ContextLoaderListener的代码吧,还是先来看看它的继承体系吧:



这个继承体系够简单的了吧,实现了ServletContextListener接口,然后继承了ContextLoaderListener,。。那么这里我们来看看它的contextInitialized方法吧。。。。

	//初始化方法,会在web程序context启动之前调用这个方法,其实主要就是创建整个程序的root ApplicationContext
	public void contextInitialized(ServletContextEvent event) {
		this.contextLoader = createContextLoader();
		if (this.contextLoader == null) {
			this.contextLoader = this;  //因为继承了ContextLoader,所以直接就是自己就好了
		}
		this.contextLoader.initWebApplicationContext(event.getServletContext());
	}

这个代码好像也没做什么什么事情,将当前的contextLoader指向了自己(因为当前还继承了ContextLoader),然后说白了就是调用父类的initWebApplicationContext方法,用于创建跟ApplicationContext,那么来看看这个方法是怎么实现的吧。。。

	//这里的servletcontext是容器创建的
	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		//"org.springframework.web.context.WebApplicationContext.ROOT"
		//首先判断当前的servletContext是否已经占用了这个属性,因为待会创建applicationContext会将其用这个名字保存
		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 {
			//父applicationcontext。如果有的话,那么待会创建的applicationContext会继承他
			ApplicationContext parent = loadParentContext(servletContext);

			this.context = createWebApplicationContext(servletContext, parent);  //创建属于这里的webApplicationContext,如果有的话,需要继承父context
			//将刚刚创建的webapplicationContext保存到servletcontext里面,key用的是"org.springframework.web.context.WebApplicationContext.ROOT"
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			//获取当前的线程上线文classLoader,在外部的servlet容器中,会为每一个web程序创建专属的classLoader,用于加载属于当前web程序的class。这里获取的就是那个
			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			//判断加载本类的加载器是否就是这个加载器,这个一般都是相等的,因为web程序下面,包括classes文件夹以及lib文件夹下面的都是这个classLoader加载的
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;  //将这个类型变量指向刚刚创建的webapplicationcontext
			} 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;  //返回刚刚刚创建的WebApplicationContext
		}
		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;
		}
	}

这部分的代码其实还算是蛮简单的了,首先判断当前的servletcontext的"org.springframework.web.context.WebApplicationContext.ROOT"属性是否已经被占用了,因为待会创建了WebApplicationContext会将其保存在servlet的这个属性。。。。然后接着就是调用createWebApplicationContext创建context了,并将其保存到servletcontext上述的属性下面,最后再这个创建的context返回,当然这里还有一些细节的东西要处理。。上面的注释就已经说的很清楚了。。。

那么接下来我们来看看createWebApplicationContext这个方法是怎么搞的吧:

	//用于创建爱webapplicationContext,这里可以理解为创建整个web程序的根context
	protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) {
		Class<?> contextClass = determineContextClass(sc);  //首先确定应该使用哪一种具体的WebApplicationContext类型,默认情况是默认情况下是org.springframework.web.context.support.XmlWebApplicationContext
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
		}
		//创建要用到的context具体类型的实例
		ConfigurableWebApplicationContext wac =
				(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

		
		
		// Assign the best possible id value.
		if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {
			// Servlet <= 2.4: resort to name specified in web.xml, if any.
			String servletContextName = sc.getServletContextName();
			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
					ObjectUtils.getDisplayString(servletContextName));
		}
		else {
			// Servlet 2.5's getContextPath available!
			try {
				String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc);
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(contextPath));
			}
			catch (Exception ex) {
				throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex);
			}
		}
		wac.setParent(parent);
		wac.setServletContext(sc);
		//设置配置文件的地方,不过这里其实一般都不会设置的,也就是null,而是直接使用web-inf目录下的applicationContext.xml文件
		wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));  //contextConfigLocation
		customizeContext(sc, wac);
		wac.refresh();    //调用refresh方法,这里会载入具体的bean的定义xml文件,读取bean的定义什么的
		return wac;
	}

这部分的代码也算是很简单吧,首先先处理一下,设置当前应该用到的具体的context类型,这里默认情况下都是XmlWebApplicationContext类型,然后再用反射创建这个类型的对象,然后再设置一些属性,最后调用refresh方法,这里默认是载入WEB-INF目录下的applicationContext.xml文件。。。

好了。。到这里为止,ContextLoaderListener是干嘛用的,以及在web.xml中配置的listener是干嘛用的就基本比较清楚了。。也算是解答了以前自己本科阶段的疑问。。。

抱歉!评论已关闭.