ContextLoaderListener初始化了webapp的root context(应用根上下文),过程如下
ContextLoaderListener继承了ContextLoader,主要方法如下
private WebApplicationContext context; 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!"); } else { //开始创建根上下文 try { if(this.context == null) { //创建根上下文 this.context = this.createWebApplicationContext(servletContext); } if(this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext err = (ConfigurableWebApplicationContext)this.context; if(!err.isActive()) { if(err.getParent() == null) { ApplicationContext elapsedTime = this.loadParentContext(servletContext); err.setParent(elapsedTime); } // 给上下文设置各种属性 // 给上下文设置id // 给上下文设置配置文件的路径(applicationcontext.xml类似) // 通过refresh方法设置beanfactory?这里不是太清楚 this.configureAndRefreshWebApplicationContext(err, servletContext); } } //创建好的根上下文放到servletContext里,整个webapp只有一个servletContext //servletContext是ServletConfig的一个成员变量 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader err1 = Thread.currentThread().getContextClassLoader(); if(err1 == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if(err1 != null) { currentContextPerThread.put(err1, this.context); } return this.context; } catch (RuntimeException var8) { } } }
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { //获取上下文类型,只能为ConfigurableWebApplicationContext类型的 Class contextClass = this.determineContextClass(sc); if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } else { //如果类型正确,实例化并返回 return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); } }
protected Class<?> determineContextClass(ServletContext servletContext) { //首先,看在web.xml中有没有配置我们自己的上下文类型,很明显,我们没有,此处为null String contextClassName = servletContext.getInitParameter("contextClass"); if(contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException var4) { throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4); } } else { //由于我们自己没有配置,spring加载自己的默认类型(XmlWebApplicaionContext.java) contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException var5) { throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5); } } }
//这个是默认类型 static { try { ClassPathResource ex = new ClassPathResource("ContextLoader.properties", ContextLoader.class); defaultStrategies = PropertiesLoaderUtils.loadProperties(ex); } catch (IOException var1) { throw new IllegalStateException("Could not load \'ContextLoader.properties\': " + var1.getMessage()); } currentContextPerThread = new ConcurrentHashMap(1); }
ContextLoader.properties文件内容如下
org.springframework.web.context.WebApplicationContext= org.springframework.web.context.support.XmlWebApplicationContext
XmlWebApplicationContext是web应用中定制的getBean的方法
ServletContext servletContext = request.getSession().getServletContext(); WebApplicationContext was = WebApplicationContextUtils.getWebApplicationContext(servletContext); User user = (User)was.getBean("user");
其余两种方法为:
ApplicationContext ctx =
new FileSystemXmlApplicationContext( "G:/Test/applicationcontext.xml ");
或
ApplicationContext ctx =
new ClassPathXmlApplicationContext( "/applicationcontext.xml ");
最后一句话:一个请求一个request,一个用户一个session,一个应用一个ServletContext
ServletContext中可以存储共享的数据,因为它的生命周期很长,从应用启动直到消亡。所以里面不要存大的数据,会很占内存
ServletContext对象是由容器创建的,也就是tomcat
和request一样,ServletContext也有setAttribute以及getAttribute和removeAttribute方法
通过ServletContext,我们也可以在servlet之间传递信息,也可以读取web.xml中的初始化参数context-param,如web.xml中context-param如下
<context-param> <param-name>log4jConfigLocation</param-name> <param-value>classpath:${env}-conf/log4j.properties</param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring.xml</param-value> <!-- <param-value>classpath:spring-mybatis.xml</param-value> --> </context-param> <context-param> <param-name>webAppRootKey</param-name> <param-value>ott-vms-admin.root</param-value> </context-param>
ServletContext servletContext = request.getSession().getServletContext(); String value = (String)servletContext.getInitParameter("contextConfigLocation");