【spring源碼分析】IOC容器初始化(一)

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


ClassPathXmlApplicationContext類繼承關係

ClassPathXmlApplicationContext類的繼承關係很是的龐大,在IOC容器初始化的過程當中,常常使用委派的方式進行函數的調用,所以需特別注意類之間的繼承關係。經過閱讀源碼,可粗略的將IOC容器初始化過程分爲兩步:面試

 ① 解析xml文件,導入beanspring

 ② 經過反射生成beanwindows

所以下面將從這兩大步對IOC初始化進行分析。app

導入bean階段程序調用鏈

首先給出導入bean的調用鏈:ide

經過程序調用鏈可知:函數

#1.導入bean的過程大體分爲三個階段:源碼分析

  ①導入bean(loadBeanDefinitions)post

  ②解析bean(parseBeanDefinition)ui

  ③註冊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內部維護了一個BeanDefinition Map(要點),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日,晚。

相關文章
相關標籤/搜索