Spring和Web容器整合的内部实现

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。

 

 

 

猜你喜欢

转载自shiyiyue513.iteye.com/blog/1473886