spring源码学习(一)

    网上很多关键字针对了“spring源码阅读”,但仔细的查看了很多,发现大部分都是一样的,而且讲得很粗,可以借鉴这些思路.但粗粗的去读一下这些文章,对spring真正的实现,还是比较迷茫的,要想真正了解spring,还是得仔细的研读它的每一行代码,对照着别人的思路,静下心来,反复看,才能一点一点的去理解它。有些关键字在百度上讨论得很少的关键字,比如:“EntityResolver”,“PropertySource”,"propertyResolver"之类的,很多人的博文略提到了一点,但也是匆匆而过,这些,都需要自己去看document,自己去看代码,自己去想。

  首先,从哪里开始读,我们的web工程要用到spring,必须在web的配置中引入spring的框架,那么,首先就需要去web.xml里找。contextLoaderListener正是我们要找的答案,同样的道理,log4j等等这些框架,也是在这里进行配置,我们通过listener的方式去引入它

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			classpath*:spring/applicationcontext.xml,
			classpath*:spring/spring-*.xml,
			classpath*:spring/consumer/spring-*.xml,
			classpath*:spring/provider/spring-*.xml	
		</param-value>
	</context-param>
	<context-param>
		<param-name>log4jConfigLocation</param-name>
		<param-value>/WEB-INF/log4j.properties</param-value>
	</context-param>
	<context-param>
		<param-name>log4jRefreshInterval</param-name>
		<param-value>60000</param-value>
	</context-param>
	<listener>
		<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
	</listener>
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
 这里可以看到,我们用到了spring的web包下的contextLoaderListener来作为整个spring容器加载的入口,这里,要稍微提一下,读spring的任何源码的时候,最好刻意的,让自己去看一下他的包路径,对理解它整个框架的划分,慢慢的会有一些好处。  下面,我们来看看contextLoaderListener.  
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
 从ServletContextListener继承而来,ServletContextListener是servlet包下的接口
	/** 
	 * Implementations of this interface receive notifications about
	 * changes to the servlet context of the web application they are
	 * part of.
	 * To receive notification events, the implementation class
	 * must be configured in the deployment descriptor for the web
	 * application.
	 * @see ServletContextEvent
	 * @since	v 2.3
	 */

        public interface ServletContextListener extends EventListener {
	/**
	 ** Notification that the web application initialization
	 ** process is starting.
	 ** All ServletContextListeners are notified of context
	 ** initialization before any filter or servlet in the web
	 ** application is initialized.
	 */

    public void contextInitialized ( ServletContextEvent sce );

	/**
	 ** Notification that the servlet context is about to be shut down.
	 ** All servlets and filters have been destroy()ed before any
	 ** ServletContextListeners are notified of context
	 ** destruction.
	 */
    public void contextDestroyed ( ServletContextEvent sce );
 可以看到,他有一个contextInitialized方法,这个方法在启动初始化时回调,因此,在contextLoaderListener中,找到这个方法的实现,就是我们需要的地方
public void contextInitialized(ServletContextEvent event) {
		this.contextLoader = createContextLoader();
		if (this.contextLoader == null) {
			this.contextLoader = this;
		}
		this.contextLoader.initWebApplicationContext(event.getServletContext());
	}
  好了,前4行代码忽略,createContextLoader()在spring2.5以后,变成了空实现,里面啥也没干,直接进入 contextLoader.initWebApplicationContext.   在进入之前,我们对它的名字思考一下,顾名思义, contextLoaderListner中有一个contextLoader对象,我们用它去初始化WebApplicationContext.用一个ServletContext作为参数,这里要先强调一下,读spring的代码,一定要注意它的一些抽象概念,比如context,ServletContext,Reader,Bean,这种名词,当你全部理解了这些名词的时候,你也就真的懂了   这里我们用到了这两个: 1.contextLoader:   读取器,用来读context的读取器,context是spring中一个非常重要的概念,我们可以简单的 理解为容器,如ApplicationContext. 2.ServletContext:   servlet上下文,我们在很多时候写代码都需要一些贯穿始终的参数,在servlet环境中,web容器就是利用servletContext来作为一个参数进行传递,后续我们还会读到spring中的一些比如holder. propertySources等概念,也是这个用途 3.WebApplicationContext:  web环境的spring容器,是容器的一种   接下来,进入initWebApplicationContext方法
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!");
		}

		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 {
			// Store context in local instance variable, to guarantee that
			// it is available on ServletContext shutdown.
			if (this.context == null) {
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext);
			}
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			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;
		}
		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;
		}
	}
 我们看前3行,可以根据字面意思猜到,容器不能重复定义或重复加载,而且,容器创建好之后会放在ServletContext这个上下文中,并且key是一个常量,等会看到最后我们来验证这个猜测。   紧接着一堆日志,开始时间,这些我们都不关注 真正的加载代码,从try开始
this.context = createWebApplicationContext(servletContext);
这行字面意思是,创建一个web环境的容器,接着
if (this.context instanceof ConfigurableWebApplicationContext) {
				configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext);
			}
表示创建的web环境的容器,必须是configurable的,可配置的,假如是可配置的,就执行配置+刷新操作。 至于为什么可配置,怎么配置,什么叫配置,我们待会应该能看懂。   再往下
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
 验证了我们之前的猜测,容器创建好之后放到servlet的上下文中   再后面的几行代码,我们就不用去关心了,至此,spring容器加载的最重要3步就是: 1.创建一个web applicationcontext, 2.配置并且刷新bean 3.把刷新好的context放到servlet上下文中去。   有了这三步,我们再去逐一的看看,他们分别是怎么实现的。后续还会遇到很多名词,在spring中,这些名词都有很深刻的含义,了解了这些名词才能深刻的了解spring,在(二)中我们去学习创建webApplicationContext.      

猜你喜欢

转载自rkdu2-163-com.iteye.com/blog/2003416