Spring和Web容器整合的内部实现
Spring和Web应用的整合配置如下
?
<context-param>
?????? <param-name>contextConfigLocation</param-name>
?????? <param-value>classpath:applicationContext.xml</param-value>
<listener>
??? <listener-class>
? ? ? org.springframework.web.context.ContextLoaderListener
? ?</listener-class>
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());}}
?
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。
?
?
?
?