Spring和Web应用的整合配置如下
?
<context-param>
?????? <param-name>contextConfigLocation</param-name>
?????? <param-value>classpath:applicationContext.xml</param-value>
</context-param>
?
<listener>
??? <listener-class>
? ? ? org.springframework.web.context.ContextLoaderListener
? ?</listener-class>
</listener>
通过上面的配置Spring就和Web应用整合了,之前我一直不明白,它内部的整合方式是怎样的,在整合后Spring和Web应用上下文的关系是怎样的,所以最近深入了源代码去看看内部的机制,首先当让是从ContextLoaderListener这个类入手了,这是一个ServletContextListener监听器类的实现类,在Web应用启动的时候会去加载这个类,调用相关的方法,代码如下:
?
?
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
private ContextLoader contextLoader;
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
protected ContextLoader createContextLoader() {
return null;
}
public ContextLoader getContextLoader() {
return this.contextLoader;
}
public void contextDestroyed(ServletContextEvent event) {
if (this.contextLoader != null) {
this.contextLoader.closeWebApplicationContext(event.getServletContext());
}
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
?
?上面红色标注的是关键代码,它调用了父类ContextLoader的initWebApplicationContext()方法,接下来就去看看在那个方法里面做了些什么,先看ContextLoader这个类,这个类内容比较多,下面只列出一些关键代码:
?
public class ContextLoader { public static final String CONTEXT_CLASS_PARAM = "contextClass"; public static final String CONTEXT_ID_PARAM = "contextId"; public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation"; public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector"; public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey"; private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties"; private static final Properties defaultStrategies; static { 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()); } } //指定了一个类加载器只能实例化一个Spring容器 private static final Map<ClassLoader, WebApplicationContext> currentContextPerThread = new ConcurrentHashMap<ClassLoader, WebApplicationContext>(1); private static volatile WebApplicationContext currentContext; private WebApplicationContext context; //ContextLoaderListener类中就是调用了这个方法 public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { try { ApplicationContext parent = loadParentContext(servletContext); // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown. this.context = createWebApplicationContext(servletContext, parent); servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); return this.context; } protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) { Class<?> contextClass = determineContextClass(sc); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setParent(parent); wac.setServletContext(sc); wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM)); customizeContext(sc, wac); wac.refresh(); return wac; } protected Class<?> determineContextClass(ServletContext servletContext) { 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); } } } }?
?
? ? ? 在这个类的静态代码块里面加载了同路径下的ContextLoader.properties这个属性文件,代开它,里面只有一条
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContex
t,这个是指定了Spring容器的具体实现类,要整合Web,自然是要XmlWebApplicationContext了。
? ? ? 看initWebApplicationContext方法,接着调用createWebApplicationContext(servletContext, parent)方法中,创建出WebApplicationContext,在这个方法里面,determineContext――确定Spring容器的具体实现类,然后使用BeanUtils.instantiateClass()来初始化XmlWebApplicationContext类,注意,这里只是初始化这个类,并没有启动Spring容器,要启动容器,还需要一个非常关键的东西,那就是配置文件。在createWebApplicationContext的最后又几条红色加深的代码,非常关键,
?
? ??wac.setServletContext(sc);//将当前的ServletContext传给即将启动的Spring容器
? ? ?wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM));//设置容器启动时加载的 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?spring配置文件,在这里就是要使用web.xml中配置的contextConfigLocation参数所指定的配 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?置文件位置了
? ? customizeContext(sc, wac);
wac.refresh();//在这里启动Spring容器,这里其实是调用父类AbstractApplicationContext的refresh()方法
?
?
综上所述,Spring和Web应用的整合这是在web应用启动的时候通过一个监听器去启动了XmlWebApplicationContext Spring容器,并将这个容器实例放入到ServletContext的Map里,Map里以WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,对于的字符串也就是WebApplicationContext.class.getName() + ".ROOT";也就是"org.springframework.web.context.WebApplicationContext.ROOT",在Servlet环境下可以通过这个来访问Spring容器,同样Spring中当然也可以访问ServletContext。
?
?
?
?