前言:spring主要就是對bean進行管理,因此IOC容器的初始化過程非常重要,搞清楚其原理不管在實際生產或面試過程中都十分的有用。在【spring源碼分析】准備工作中已經搭建好spring的環境,並利用xml配置形式對類進行了實例化。在test代碼中有一個非常關鍵的類ClassPathXmlApplicationContext,在這個類中實現了IOC容器的初始化,因此我們從ClassPathXmlApplicationContext着手開始研究IOC的初始化過程。
ClassPathXmlApplicationContext類繼承關系

ClassPathXmlApplicationContext類的繼承關系非常的龐大,在IOC容器初始化的過程中,經常使用委派的方式進行函數的調用,因此需特別注意類之間的繼承關系。通過閱讀源碼,可粗略的將IOC容器初始化過程分為兩步:
① 解析xml文件,導入bean
② 通過反射生成bean
因此下面將從這兩大步對IOC初始化進行分析。
導入bean階段程序調用鏈
首先給出導入bean的調用鏈:

通過程序調用鏈可知:
#1.導入bean的過程大致分為三個階段:
①導入bean(loadBeanDefinitions)
②解析bean(parseBeanDefinition)
③注冊bean(registerBeanDefinition)
#2.最終bean是存儲在beanDefinitionMap中:鍵為類名(beanName),值為GenericBeanDefinition(BeanDefinition)。
下面對導入bean的三個階段進行分析。
導入bean階段源碼分析
首先來看ClassPathXmlApplicationContext構造函數,具體代碼如下:
1 public ClassPathXmlApplicationContext( 2 String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) 3 throws BeansException { 4 // 初始化父類相關資源 5 super(parent); 6 // 解析配置文件路徑,並設置資源路徑 7 setConfigLocations(configLocations); 8 if (refresh) { 9 // 核心方法 ioc容器初始化在此方法中實現 10 refresh(); 11 } 12 }
看似寥寥的幾行代碼,其實也非常重要,從這里我們可以了解到spring是如何解析配置文件中的占位符信息的,這里關注PropertyResolver接口。
首先我們看PropertyResolver接口源碼:
1 public interface PropertyResolver { 2 3 /** 4 * 是否包含某個屬性<br/> 5 * Return whether the given property key is available for resolution, 6 * i.e. if the value for the given key is not {@code null}. 7 */ 8 boolean containsProperty(String key); 9 10 /** 11 * 獲取屬性值 如果找不到則返回null<br/> 12 * Return the property value associated with the given key, 13 * or {@code null} if the key cannot be resolved. 14 * 15 * @param key the property name to resolve 16 * @see #getProperty(String, String) 17 * @see #getProperty(String, Class) 18 * @see #getRequiredProperty(String) 19 */ 20 @Nullable 21 String getProperty(String key); 22 23 /** 24 * 獲取屬性值,如果找不到則返回默認值<br/> 25 * Return the property value associated with the given key, or 26 * {@code defaultValue} if the key cannot be resolved. 27 * 28 * @param key the property name to resolve 29 * @param defaultValue the default value to return if no value is found 30 * @see #getRequiredProperty(String) 31 * @see #getProperty(String, Class) 32 */ 33 String getProperty(String key, String defaultValue); 34 35 /** 36 * 獲取指定類型的屬性值,找不到則返回null<br/> 37 * Return the property value associated with the given key, 38 * or {@code null} if the key cannot be resolved. 39 * 40 * @param key the property name to resolve 41 * @param targetType the expected type of the property value 42 * @see #getRequiredProperty(String, Class) 43 */ 44 @Nullable 45 <T> T getProperty(String key, Class<T> targetType); 46 47 /** 48 * 獲取指定類型的屬性值,找不到則返回默認值<br/> 49 * Return the property value associated with the given key, 50 * or {@code defaultValue} if the key cannot be resolved. 51 * 52 * @param key the property name to resolve 53 * @param targetType the expected type of the property value 54 * @param defaultValue the default value to return if no value is found 55 * @see #getRequiredProperty(String, Class) 56 */ 57 <T> T getProperty(String key, Class<T> targetType, T defaultValue); 58 59 /** 60 * 獲取屬性值,找不到則拋出異常IllegalStateException<br/> 61 * Return the property value associated with the given key (never {@code null}). 62 * 63 * @throws IllegalStateException if the key cannot be resolved 64 * @see #getRequiredProperty(String, Class) 65 */ 66 String getRequiredProperty(String key) throws IllegalStateException; 67 68 /** 69 * 獲取指定類型的屬性值,找不到則拋出異常IllegalStateException<br/> 70 * Return the property value associated with the given key, converted to the given 71 * targetType (never {@code null}). 72 * 73 * @throws IllegalStateException if the given key cannot be resolved 74 */ 75 <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException; 76 77 /** 78 * 替換文本中的占位符(${key})到屬性值,找不到則不解析 79 * Resolve ${...} placeholders in the given text, replacing them with corresponding 80 * property values as resolved by {@link #getProperty}. Unresolvable placeholders with 81 * no default value are ignored and passed through unchanged. 82 * 83 * @param text the String to resolve 84 * @return the resolved String (never {@code null}) 85 * @throws IllegalArgumentException if given text is {@code null} 86 * @see #resolveRequiredPlaceholders 87 * @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String) 88 */ 89 String resolvePlaceholders(String text); 90 91 /** 92 * 替換文本中占位符(${key})到屬性值,找不到則拋出異常IllegalArgumentException 93 * Resolve ${...} placeholders in the given text, replacing them with corresponding 94 * property values as resolved by {@link #getProperty}. Unresolvable placeholders with 95 * no default value will cause an IllegalArgumentException to be thrown. 96 * 97 * @return the resolved String (never {@code null}) 98 * @throws IllegalArgumentException if given text is {@code null} 99 * or if any placeholders are unresolvable 100 * @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders(String, boolean) 101 */ 102 String resolveRequiredPlaceholders(String text) throws IllegalArgumentException; 103 104 }
該接口定義了一些與屬性(解析占位符/獲取屬性)相關的方法,以ClassPathXmlApplicationContext構造函數的第7行代碼為Debug入口,下面會通過調試的形式進行分析。
PropertyResolver繼承關系如下,注意AbstractPropertyResolver與StandardEnvironment都間接的實現了PropertyResolver接口。

在setConfigLocations(String)打斷點,進行Debug,會走到AbstractRefreshableConfigApplicationContext#resolvePath處:
1 protected String resolvePath(String path) { 2 return getEnvironment().resolveRequiredPlaceholders(path); 3 }
這里getEnvironment()調用的是父類AbstractApplicationContext的方法:
1 public ConfigurableEnvironment getEnvironment() { 2 if (this.environment == null) { 3 // 創建一個ConfigurableEnvironment對象 4 this.environment = createEnvironment(); 5 } 6 return this.environment; 7 } 8 protected ConfigurableEnvironment createEnvironment() { 9 return new StandardEnvironment(); 10 }
注意這里返回的是一個ConfigurableEnvironment 對象,繼續debug,進入resolveRequiredPlaceholders(String)函數:
1 private final ConfigurablePropertyResolver propertyResolver =new PropertySourcesPropertyResolver(this.propertySources); 2 3 public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException { 4 // 委派給AbstractPropertyResolver執行 5 return this.propertyResolver.resolveRequiredPlaceholders(text); 6 }
由於函數調用過程太細,所以這里給出解析配置文件中占位符的最終核心點:PropertyPlaceholderHelper#parseStringValue方法上:
1 protected String parseStringValue( 2 String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) { 3 4 StringBuilder result = new StringBuilder(value); 5 // 獲取前綴"${"的索引位置 6 int startIndex = value.indexOf(this.placeholderPrefix); 7 while (startIndex != -1) { 8 // 獲取后綴"}"的索引位置 9 int endIndex = findPlaceholderEndIndex(result, startIndex); 10 if (endIndex != -1) { 11 // 截取"${"和"}"中間的內容,即配置文件中對應的值 12 String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex); 13 String originalPlaceholder = placeholder; 14 if (!visitedPlaceholders.add(originalPlaceholder)) { 15 throw new IllegalArgumentException( 16 "Circular placeholder reference '" + originalPlaceholder + "' in property definitions"); 17 } 18 // Recursive invocation, parsing placeholders contained in the placeholder key. 19 // 解析占位符鍵中包含的占位符,真正的值 20 placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders); 21 // Now obtain the value for the fully resolved key... 22 // 從Properties中獲取placeHolder對應的propVal 23 String propVal = placeholderResolver.resolvePlaceholder(placeholder); 24 // 如果不存在 25 if (propVal == null && this.valueSeparator != null) { 26 // 查詢":"的位置 27 int separatorIndex = placeholder.indexOf(this.valueSeparator); 28 // 如果存在 29 if (separatorIndex != -1) { 30 // 截取":"前面部分的actualPlaceholder 31 String actualPlaceholder = placeholder.substring(0, separatorIndex); 32 // 截取":"后面的defaulValue 33 String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length()); 34 // 從Properties中獲取actualPlaceholder對應的值 35 propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder); 36 // 如果不存在,則返回defaultValue 37 if (propVal == null) { 38 propVal = defaultValue; 39 } 40 } 41 } 42 if (propVal != null) { 43 // Recursive invocation, parsing placeholders contained in the 44 // previously resolved placeholder value. 45 propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders); 46 result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal); 47 if (logger.isTraceEnabled()) { 48 logger.trace("Resolved placeholder '" + placeholder + "'"); 49 } 50 startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length()); 51 } 52 else if (this.ignoreUnresolvablePlaceholders) { 53 // Proceed with unprocessed value. 54 // 忽略值 55 startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length()); 56 } 57 else { 58 throw new IllegalArgumentException("Could not resolve placeholder '" + 59 placeholder + "'" + " in value \"" + value + "\""); 60 } 61 visitedPlaceholders.remove(originalPlaceholder); 62 } 63 else { 64 startIndex = -1; 65 } 66 } 67 // 返回propVal,就是替換之后的值 68 return result.toString(); 69 }
分析:
該函數的主要作用就是取占位符"${}"或":"中的值進行賦值,比如在配置文件中直接使用xxx="${xxx.xx.xx}"或使用注解掃描時使用@Value("${xxx.xx.xx}")進行屬性值注入的時候,都會走該函數進行解析。
接下來看非常重要的AbstractApplicationContext#refresh()函數:
1 public void refresh() throws BeansException, IllegalStateException { 2 synchronized (this.startupShutdownMonitor) { 3 // Prepare this context for refreshing. 4 // 准備刷新上下文環境 5 prepareRefresh(); 6 7 // Tell the subclass to refresh the internal bean factory. 8 // 創建並初始化BeanFactory 9 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 10 11 // Prepare the bean factory for use in this context. 12 // 填充BeanFactory 13 prepareBeanFactory(beanFactory); 14 15 try { 16 // Allows post-processing of the bean factory in context subclasses. 17 // 提供子類覆蓋的額外處理,即子類處理定義的BeanFactoryPostProcess 18 postProcessBeanFactory(beanFactory); 19 20 // Invoke factory processors registered as beans in the context. 21 // 激活各種BeanFactory處理器 22 invokeBeanFactoryPostProcessors(beanFactory); 23 24 // Register bean processors that intercept bean creation. 25 // 注冊攔截Bean創建的Bean處理器,即注冊BeanPostProcessor 26 registerBeanPostProcessors(beanFactory); 27 28 // Initialize message source for this context. 29 // 初始化上下文中的資源文件,如國際化文件的處理 30 initMessageSource(); 31 32 // Initialize event multicaster for this context. 33 // 初始化上下文事件廣播器 34 initApplicationEventMulticaster(); 35 36 // Initialize other special beans in specific context subclasses. 37 // 給子類擴展初始化其他bean 38 onRefresh(); 39 40 // Check for listener beans and register them. 41 // 在所有bean中查找listener bean,然后注冊到廣播器中 42 registerListeners(); 43 44 // Instantiate all remaining (non-lazy-init) singletons. 45 // 初始化剩下的單例Bean(非延遲加載的) 46 finishBeanFactoryInitialization(beanFactory); 47 48 // Last step: publish corresponding event. 49 // 完成刷新過程,通知聲明周期處理器lifecycleProcessor刷新過程,同時發出ContextRefreshEvent事件通知別人 50 finishRefresh(); 51 } catch (BeansException ex) { 52 if (logger.isWarnEnabled()) { 53 logger.warn("Exception encountered during context initialization - " + 54 "cancelling refresh attempt: " + ex); 55 } 56 57 // Destroy already created singletons to avoid dangling resources. 58 // 銷毀已經創建的bean 59 destroyBeans(); 60 61 // Reset 'active' flag. 62 // 重置容器激活標簽 63 cancelRefresh(ex); 64 65 // Propagate exception to caller. 66 throw ex; 67 } finally { 68 // Reset common introspection caches in Spring's core, since we 69 // might not ever need metadata for singleton beans anymore... 70 resetCommonCaches(); 71 } 72 } 73 }
分析:
該函數中進行了IOC容器的初始化工作,以該函數為切入點,進行相應源碼的分析,一步一步來力求搞清楚。
AbstractApplicationContext#prepareRefresh()
1 protected void prepareRefresh() { 2 // Switch to active. 3 // 設置啟動時間 4 this.startupDate = System.currentTimeMillis(); 5 // 設置context當前狀態 6 this.closed.set(false); 7 this.active.set(true); 8 9 if (logger.isDebugEnabled()) { 10 if (logger.isTraceEnabled()) { 11 logger.trace("Refreshing " + this); 12 } else { 13 logger.debug("Refreshing " + getDisplayName()); 14 } 15 } 16 17 // Initialize any placeholder property sources in the context environment. 18 // 初始化context environment(上下文環境)中的占位符屬性來源,該函數主要提供給子類進行擴展使用 19 initPropertySources(); 20 21 // Validate that all properties marked as required are resolvable: 22 // see ConfigurablePropertyResolver#setRequiredProperties 23 // 對屬性值進行必要的驗證 24 getEnvironment().validateRequiredProperties(); 25 26 // Store pre-refresh ApplicationListeners... 27 if (this.earlyApplicationListeners == null) { 28 this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners); 29 } else { 30 // Reset local application listeners to pre-refresh state. 31 this.applicationListeners.clear(); 32 this.applicationListeners.addAll(this.earlyApplicationListeners); 33 } 34 35 // Allow for the collection of early ApplicationEvents, 36 // to be published once the multicaster is available... 37 this.earlyApplicationEvents = new LinkedHashSet<>(); 38 }
分析:
prepareRefresh()函數,作用較為簡單,主要是做一些設置操作,這里不做過多贅述。
AbstractApplicationContext#obtainFreshBeanFactory()
1 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { 2 // 刷新BeanFactory 3 refreshBeanFactory(); 4 // 返回BeanFactory 5 return getBeanFactory(); 6 }
分析:
該函數主要作用:創建並初始化BeanFactory。
這里簡單介紹一下BeanFactory:它是一個基本的Bean容器,其中BeanDefinition是它的基本結構,BeanFactory內部維護了一個BeanDefinitionMap(要點),BeanFactory可根據BeanDefinition的描述進行bean的創建與管理。

注:DefaultListableBeanFactory為最終默認實現,它實現了所有接口。
進入AbstractRefreshableApplicationContext#refreshBeanFactory()函數
1 @Override 2 protected final void refreshBeanFactory() throws BeansException { 3 // 若已有BeanFactory,則銷毀bean,並銷毀BeanFactory 4 if (hasBeanFactory()) { 5 destroyBeans(); 6 closeBeanFactory(); 7 } 8 try { 9 // 創建BeanFactory對象 10 DefaultListableBeanFactory beanFactory = createBeanFactory(); 11 // 指定序列化編號 12 beanFactory.setSerializationId(getId()); 13 // 定制BeanFactory 設置相關屬性 14 customizeBeanFactory(beanFactory); 15 // 加載BeanDefinition 16 loadBeanDefinitions(beanFactory); 17 // 設置Context的BeanFactory 18 synchronized (this.beanFactoryMonitor) { 19 this.beanFactory = beanFactory; 20 } 21 } catch (IOException ex) { 22 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); 23 } 24 }
分析:
相應代碼已經給出了基本注釋,這里我們主要關注第16行代碼:loadBeanDefinitions(DefaultListableBeanFactory),從該函數可引申非常多重要的知識點,介於篇幅原因,將在后面進行詳細分析。
總結
這里再次總結本文重點:
- PropertyResolver,以及引申出來的PropertyPlaceholderHelper#parseStringValue(占位符解析重要函數)與日常開發也息息相關。
- BeanFactory以及其最終實現類DefaultListableBeanFactory,基礎的IoC容器,提供與bean相關的方法。
- loadBeanDefinitions方法,這里再次強調一下,該方法非常重要。
by Shawn Chen,2018.11.24日,晚。
