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中