Spring 源码解析(一)加载配置文件1

       Spring初始化入口是org.springframework.web.context.ContextLoaderListener类中的contextInitialized方法,如果不清楚contextInitialized是如何被调用的请参考本人另一篇随笔:http://www.cnblogs.com/jintian315/p/8628873.html。

//org.springframework.web.context.ContextLoaderListener 1     /**
2      * Initialize the root web application context.
3      */
4     @Override
5     public void contextInitialized(ServletContextEvent event) {
6         initWebApplicationContext(event.getServletContext());
7     }

      进入initWebApplicationContext方法,为了方便阅读,省略了部分不影响流程的代码,并将关键代码行加了下划线,同时会在代码开头标注此是在哪个类中。注意:阅读代码中的注释是个好习惯,哪怕你的英文水平

 //org.springframework.web.context.ContextLoaderListener
1
/** 2 * Initialize Spring's web application context for the given servlet context, 3 * using the application context provided at construction time, or creating a new one 4 * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and 5 * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params. 6 * @param servletContext current servlet context 7 * @return the new WebApplicationContext 8 * @see #ContextLoader(WebApplicationContext) 9 * @see #CONTEXT_CLASS_PARAM 10 * @see #CONFIG_LOCATION_PARAM 11 */ 12 public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { 13 if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { 14 throw new IllegalStateException( 15 "Cannot initialize context because there is already a root application context present - " + 16 "check whether you have multiple ContextLoader* definitions in your web.xml!"); 17 }18
19      Log logger = LogFactory.getLog(ContextLoader.class);
20      servletContext.log("Initializing Spring root WebApplicationContext");
21      if (logger.isInfoEnabled()) {
22        logger.info("Root WebApplicationContext: initialization started");
23      }
24      long startTime = System.currentTimeMillis();
25 26 try { 27 // Store context in local instance variable, to guarantee that 28 // it is available on ServletContext shutdown. 29 if (this.context == null) { 30 this.context = createWebApplicationContext(servletContext); 31 } 32 if (this.context instanceof ConfigurableWebApplicationContext) { 33 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; 34 if (!cwac.isActive()) { 35 // The context has not yet been refreshed -> provide services such as 36 // setting the parent context, setting the application context id, etc 37 if (cwac.getParent() == null) { 38 // The context instance was injected without an explicit parent -> 39 // determine parent for root web application context, if any. 40 ApplicationContext parent = loadParentContext(servletContext); 41 cwac.setParent(parent); 42 } 43 configureAndRefreshWebApplicationContext(cwac, servletContext); 44 } 45 } 46 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); 47 48 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 49 if (ccl == ContextLoader.class.getClassLoader()) { 50 currentContext = this.context; 51 }77 }

第13行首先判断servletContex中是否有值为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的属性,有则抛出异常。

//org.springframework.web.context.WebApplicationContext
1
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

第30行创建了WebApplicationContext并赋给this.context,创建的实例对象为org.springframework.web.context.support.XmlWebApplicationContext。那么是如何创建这个实例对象的呢?下面我们就进入到createWebApplicationContext方法中一探究竟

//org.springframework.web.context.ContextLoaderListener
1
protected WebApplicationContext createWebApplicationContext(ServletContext sc) { 2 Class<?> contextClass = determineContextClass(sc); 3 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { 4 throw new ApplicationContextException("Custom context class [" + contextClass.getName() + 5 "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); 6 } 7 return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); 8 }

第2行查找Class类,第7行生成类的实例。主要关注下第2行的determineContextClass方法

 //org.springframework.web.context.ContextLoaderListener
1
/** 2 * Return the WebApplicationContext implementation class to use, either the 3 * default XmlWebApplicationContext or a custom context class if specified. 4 * @param servletContext current servlet context 5 * @return the WebApplicationContext implementation class to use 6 * @see #CONTEXT_CLASS_PARAM 7 * @see org.springframework.web.context.support.XmlWebApplicationContext 8 */ 9 protected Class<?> determineContextClass(ServletContext servletContext) { 10 String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); 11 if (contextClassName != null) { 12 try { 13 return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); 14 } 15 catch (ClassNotFoundException ex) { 16 throw new ApplicationContextException( 17 "Failed to load custom context class [" + contextClassName + "]", ex); 18 } 19 } 20 else { 21 contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); 22 try { 23 return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); 24 } 25 catch (ClassNotFoundException ex) { 26 throw new ApplicationContextException( 27 "Failed to load default context class [" + contextClassName + "]", ex); 28 } 29 } 30 }

第10行是从web.xml中查找<context-param>标签定义的context值作为类名,此参数一般不设置,除非有特殊需求。
第21是从类型为Properties的defaultStrategies对象中,将以WebApplicationContext.class.getName()(得到的值为org.springframework.web.context.WebApplicationContext)为key获得值作为类名。defaultStrategies的在ContextLoaderListener类的静态代码块中初始化的。

//org.springframework.web.context.ContextLoaderListener 
1
/** 2 * Name of the class path resource (relative to the ContextLoader class) 3 * that defines ContextLoader's default strategy names. 4 */ 5 private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties"; 6 7 8 private static final Properties defaultStrategies; 9 10 static { 11 // Load default strategy implementations from properties file. 12 // This is currently strictly internal and not meant to be customized 13 // by application developers. 14 try { 15 ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class); 16 defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); 17 } 18 catch (IOException ex) { 19 throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage()); 20 } 21 }

不难发现其实是加载ContextLoader.properties文件了,让我们看下这个文件的具体内容

# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

从这个文件中可验证了我们前边所说的:创建WebApplicationContext是org.springframework.web.context.support.XmlWebApplicationContext实例。createWebApplicationContext方法生成的对象,经过了类型转换,为了让大家不至于生产混淆,下面列出WebApplicationContext、ConfigurableWebApplicationContext、XmlWebApplicationContext之间的关系

让我们回到ContextLoaderListener的initWebApplicationContext方法中,看看这个方法接下来会做什么

 //org.springframework.web.context.ContextLoaderListener
1 /** 2 * Initialize Spring's web application context for the given servlet context, 3 * using the application context provided at construction time, or creating a new one 4 * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and 5 * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params. 6 * @param servletContext current servlet context 7 * @return the new WebApplicationContext 8 * @see #ContextLoader(WebApplicationContext) 9 * @see #CONTEXT_CLASS_PARAM 10 * @see #CONFIG_LOCATION_PARAM 11 */ 12 public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { 13 if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { 14 throw new IllegalStateException( 15 "Cannot initialize context because there is already a root application context present - " + 16 "check whether you have multiple ContextLoader* definitions in your web.xml!"); 17 } 18 19      Log logger = LogFactory.getLog(ContextLoader.class); 20      servletContext.log("Initializing Spring root WebApplicationContext"); 21      if (logger.isInfoEnabled()) { 22        logger.info("Root WebApplicationContext: initialization started"); 23      } 24      long startTime = System.currentTimeMillis(); 25 26 try { 27 // Store context in local instance variable, to guarantee that 28 // it is available on ServletContext shutdown. 29 if (this.context == null) { 30 this.context = createWebApplicationContext(servletContext); 31 } 32 if (this.context instanceof ConfigurableWebApplicationContext) { 33 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; 34 if (!cwac.isActive()) { 35 // The context has not yet been refreshed -> provide services such as 36 // setting the parent context, setting the application context id, etc 37 if (cwac.getParent() == null) { 38 // The context instance was injected without an explicit parent -> 39 // determine parent for root web application context, if any. 40 ApplicationContext parent = loadParentContext(servletContext); 41 cwac.setParent(parent); 42 } 43 configureAndRefreshWebApplicationContext(cwac, servletContext); 44 } 45 } 46 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); 47 48 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 49 if (ccl == ContextLoader.class.getClassLoader()) { 50 currentContext = this.context; 51 } 77 }

 通过上面的分析得知会执行43行代码,也就是configureAndRefreshWebApplicationContext方法(核心方法,完成了配置文件解析、bean加载等很多工作,下章讨论),之后执行46行(将webapplicationcontext设置到servletContext中)。

总结:ContextLoaderListener的initWebApplicationContext方法主要干了三件事情

1:30行,生成XmlWebApplicationContext实例,并赋给this.context

2:43行,初始化this.context(暂且用初始化,其实做了大量的工作,如配置文件解析、bean加载等)

3:46行,将this.context设置到servleContext中

 

posted @ 2018-03-29 18:28  jintian315  阅读(392)  评论(0编辑  收藏  举报