Spring IoC - IoC 容器初始化 源碼解析

undefined

前言

本章主要內容是由如下部分組成,java

  • Spring 中容器初始化入口
  • 以最經典的ClassPathXmlApplicationContext 爲例,講解Spring IoC 的容器初始化過程

在學習源碼的過程中,我想強調兩點:spring

  1. 必定要學會抓重點,概括核心類、核心方法、核心步驟。
  2. 理解類、變量、方法名的命名,Spring 源碼的命名是很講究的,不少時候是自解釋的
  3. 必定要學會看Java doc ,同上,這種頂級的框架的java doc 描述很是的詳盡

Spring 容器初始化入口

啓動容器,實際上指的就是實例化ApplicationContext的這個動做。只是在不一樣狀況下可能有不一樣的表現形式。併發

  1. ClassPathXmlApplicationContext 經過XML配置
ApplicationContext context = new ClassPathXmlApplicationContext(applicationContext.xml");
複製代碼
  1. AnnotationConfigApplicationContext 經過java config 類配置
@Configuration
@ComponentScan("ric.study.demo.ioc")
public class BeanDemoConfig {
    public static void main(String... strings) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext(BeanDemoConfig.class);
        System.out.println("Spring container started and is ready");
        ...
    }
}
複製代碼

相似前面這兩種new ***ApplicationContext的方式,不多會用於直接的生產開發。通常都是咱們本身在demo中或者單元測試中會用到。app

  1. WebApplicationContext SpringMVC

這個其實是咱們日常最經常使用的初始化方式,Spring MVC 中 ServletContext 爲 Spring 的 IoC容器提供了宿主環境。是經過ContextLoaderListener 的初始化來創建的。框架

WebApplicationContext 的初始化調用鏈路:ContextLoaderListener.contextInitialized --> ContextLoader.initWebApplicationContext --> ContextLoader.createWebApplicationContext --> ContextLoader.determineContextClass --> ContextLoader.determineContextClass。編輯器

底層是經過反射來實例化的。ide

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
		Class<?> contextClass = determineContextClass(sc);
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
					"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
		}
		return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
	}
複製代碼

這塊內容先簡要提一下,屬於SpringMVC的內容,不是咱們今天要講的Spring IoC 模塊的知識。函數

容器初始化 源碼解析

如今讓咱們正式開始的源碼解讀。會從最經典的ClassPathXmlApplicationContext 上下文爲起點,來描述整個過程。post

在說明以前,我想了想仍是以爲把整個IoC容器初始化的關鍵步驟爲你們梳理一下,以便於你們能在內心有個大概的脈絡,更容易讀懂源碼,更容易抓住重點。再重複提一句,看源碼必定要學會抓重點,概括核心類、核心方法、核心步驟。單元測試

ClassPathXmlApplicationContext 的容器初始化咱們大體分爲下面幾步:

  1. BeanDefinition 的 Resource 定位

    這裏的Resource定位 是經過繼承ResourceLoader 得到的,ResourceLoader表明了加載資源的一種方式,正是策略模式的實現

  2. 從 Resource中解析、載入BeanDefinition

  3. BeanDefinition 在IoC 容器中的註冊

前面說了,實例化這個上下文,就是在啓動 IoC 容器。那咱們確定要從它的構造函數入手。

public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {

		super(parent);
		setConfigLocations(configLocations);
		if (refresh) {
			refresh();
		}
	}
複製代碼

入參中的configLocations在這裏就是你XML配置文件 的 classpath。

setConfigLocations(configLocations);我這裏不展開講,內容不復雜,就是把一些帶有佔位符的地址解析成實際的地址。

再以後就是refresh(),咱們說的容器初始化,就是在這裏面進行的,這裏取名爲refresh,是由於容器啓動以後,再調用refresh()會刷新IoC 容器

這裏先放上IoC容器初始化的時序圖,方便理解,

loadBeanDefinitions.png

refresh() 的源碼:

@Override
public void refresh() throws BeansException, IllegalStateException {
   // 來個鎖,否則 refresh() 還沒結束,你又來個啓動或銷燬容器的操做,那不就亂套了嘛
   synchronized (this.startupShutdownMonitor) {

      // 準備工做,記錄下容器的啓動時間、標記「已啓動」狀態、處理配置文件中的佔位符
      prepareRefresh();

      // 這步比較關鍵,這步完成後,配置文件就會解析成一個個 Bean 定義,註冊到 BeanFactory 中,
      // 固然,這裏說的 Bean 尚未初始化,只是配置信息都提取出來了,
      // 註冊也只是將這些信息都保存到了註冊中心(說到底核心是一個 beanName-> beanDefinition 的 map)
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 設置 BeanFactory 的類加載器,添加幾個 BeanPostProcessor,手動註冊幾個特殊的 bean
      // 這塊待會會展開說
      prepareBeanFactory(beanFactory);

      try {
         // 【這裏須要知道 BeanFactoryPostProcessor 這個知識點,Bean 若是實現了此接口,
         // 那麼在容器初始化之後,Spring 會負責調用裏面的 postProcessBeanFactory 方法。】

         // 這裏是提供給子類的擴展點,到這裏的時候,全部的 Bean 都加載、註冊完成了,可是都尚未初始化
         // 具體的子類能夠在這步的時候添加一些特殊的 BeanFactoryPostProcessor 的實現類或作點什麼事
         postProcessBeanFactory(beanFactory);
         // 調用 BeanFactoryPostProcessor 各個實現類的 postProcessBeanFactory(factory) 方法
         invokeBeanFactoryPostProcessors(beanFactory);

         // 註冊 BeanPostProcessor 的實現類,注意看和 BeanFactoryPostProcessor 的區別
         // 此接口兩個方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
         // 兩個方法分別在 Bean 初始化以前和初始化以後獲得執行。注意,到這裏 Bean 還沒初始化
         registerBeanPostProcessors(beanFactory);

         // 初始化當前 ApplicationContext 的 MessageSource,國際化不是重點,不展開
         initMessageSource();

         // 初始化當前 ApplicationContext 的事件廣播器,這裏也不展開了
         initApplicationEventMulticaster();

         // 從方法名就能夠知道,典型的模板方法(鉤子方法),
         // 具體的子類能夠在這裏初始化一些特殊的 Bean(在初始化 singleton beans 以前)
         onRefresh();

         // 註冊事件監聽器,監聽器須要實現 ApplicationListener 接口。這也不是咱們的重點,過
         registerListeners();

         // 重點,重點,重點
         // 初始化全部的 singleton beans
         //(lazy-init 的除外)
         finishBeanFactoryInitialization(beanFactory);

         // 最後,廣播事件,ApplicationContext 初始化完成
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         // 銷燬已經初始化的 singleton 的 Beans,以避免有些 bean 會一直佔用資源
         destroyBeans();
         // Reset 'active' flag.
         cancelRefresh(ex);
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}
複製代碼

我會從上述流程中,挑如下幾個進行分析,

  1. prepareRefresh() 建立容器前的準備工做
  2. obtainFreshBeanFactory() 建立 BeanFactory
  3. prepareBeanFactory(beanFactory) 對BeanFactory進行一些特徵的設置工做
  4. finishBeanFactoryInitialization(beanFactory); 初始化全部的 singleton beans(DI的入口)

1. prepareRefresh() 建立容器前的準備工做

protected void prepareRefresh() {
   // 記錄啓動時間,
   // 將 active 屬性設置爲 true,closed 屬性設置爲 false,它們都是 AtomicBoolean 類型
   this.startupDate = System.currentTimeMillis();
   this.closed.set(false);
   this.active.set(true);

   if (logger.isInfoEnabled()) {
      logger.info("Refreshing " + this);
   }

   // Initialize any placeholder property sources in the context environment
   initPropertySources();

   // 校驗 xml 配置文件
   getEnvironment().validateRequiredProperties();

   this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
複製代碼

2.★ obtainFreshBeanFactory() 建立 Bean 容器,加載並註冊 Bean

IoC初始化裏面最重要的部分。

關鍵是如下幾步,

  • 初始化BeanFactory
  • 加載Bean
  • 註冊Bean
  • ...

注意:這步完成後,Bean 並無完成初始化,實際的實例並無被建立。

源碼位置:AbstractApplicationContext#obtainFreshBeanFactory()

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        // 關閉舊的 BeanFactory (若是有),建立新的 BeanFactory,加載 Bean 定義、註冊 Bean 等等
		refreshBeanFactory();
        // 返回上一步剛剛建立的BeanFactory
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		if (logger.isDebugEnabled()) {
			logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
		}
		return beanFactory;
	}
複製代碼

源碼位置:AbstractRefreshableApplicationContext#refreshBeanFactory()

protected final void refreshBeanFactory() throws BeansException {
        // 若是 ApplicationContext 已經加載過 BeanFactory,銷燬全部的Bean,關閉BeanFactory
        // 注意點:應用中BeanFactory是能夠有多個的,這裏可不是說全局是否有BeanFactory
        // 而是說當前的ApplicationContext有沒有BeanFactory!
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
            // 初始化一個 DefaultListableBeanFactory
			DefaultListableBeanFactory beanFactory = createBeanFactory();
            // 用於 BeanFactory 的序列化,通常人應該用不到吧...
			beanFactory.setSerializationId(getId());
            // 下面是兩個重點方法
            // 設置 BeanFactory 的兩個重要屬性
            // 是否容許 Bean 覆蓋、是否容許循環引用 TODO 2.1
			customizeBeanFactory(beanFactory);
            
            // 加載BeanDefinition到BeanFactory 單獨拉出來說
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
複製代碼

看到這裏的時候,能夠感受到一個設計思路,ApplicationContext 繼承自 BeanFactory,可是它不該該被理解爲 BeanFactory 的實現類,而是說其內部持有一個實例化的 BeanFactory(DefaultListableBeanFactory)。之後全部的 BeanFactory 相關的操做實際上是委託給這個實例來處理的

2.1 customizeBeanFactory

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
		if (this.allowBeanDefinitionOverriding != null) {
			beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.allowCircularReferences != null) {
			beanFactory.setAllowCircularReferences(this.allowCircularReferences);
		}
	}
複製代碼

BeanDefinition 的覆蓋問題可能會有開發者碰到這個坑,就是在配置文件中定義 bean 時使用了相同的 id 或 name,默認狀況下,allowBeanDefinitionOverriding 屬性爲 null(Boolean類型),若是在同一配置文件中重複了,會拋錯,可是若是不是同一配置文件中,會發生覆蓋

循環引用也很好理解:A 依賴 B,而 B 依賴 A。或 A 依賴 B,B 依賴 C,而 C 依賴 A。

默認狀況下,Spring 容許循環依賴,固然若是你在 A 的構造方法中依賴 B,在 B 的構造方法中依賴 A 是不行的。

2.2 ★ loadBeanDefinitions(beanFactory) 加載BeanDefinition

看下這個方法的聲明,

/** * Load bean definitions into the given bean factory, typically through * delegating to one or more bean definition readers. * @param beanFactory the bean factory to load bean definitions into * @throws BeansException if parsing of the bean definitions failed * @throws IOException if loading of bean definition files failed * @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader */
	protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException;

複製代碼

在ClassPathXmlApplicationContext 是按照解析XML的加載方式。看javadoc的描述,是經過XmlBeanDefinitionReader來載入Bean Definitions。

/** * Loads the bean definitions via an XmlBeanDefinitionReader. * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader * @see #initBeanDefinitionReader * @see #loadBeanDefinitions */
	@Override
	protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

		// Configure the bean definition reader with this context's
		// resource loading environment.
		beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// Allow a subclass to provide custom initialization of the reader,
		// 初始化Reader 不重要,看下這個方法的javadoc就很好理解了
		initBeanDefinitionReader(beanDefinitionReader);
        // 真正重要的步驟!!
        // 用Reader去加載XML配置
		loadBeanDefinitions(beanDefinitionReader);
	}

複製代碼

loadBeanDefinitions(beanDefinitionReader)

/** * Load the bean definitions with the given XmlBeanDefinitionReader. * 看這句註釋:this method is just supposed to load and/or register bean definitions. */
	protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
		Resource[] configResources = getConfigResources();
		if (configResources != null) {
			reader.loadBeanDefinitions(configResources);
		}
		String[] configLocations = getConfigLocations();
		if (configLocations != null) {
            // 這個分支,經過路徑名去獲取Resource,會和上面的方法異曲同工
			reader.loadBeanDefinitions(configLocations);
		}
	}
複製代碼

AbstractBeanDefinitionReader#loadBeanDefinitions(Resource... resources)

@Override
	public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
		Assert.notNull(resources, "Resource array must not be null");
		int counter = 0;
		for (Resource resource : resources) {
            // 遍歷解析XML文件,加載 BeanDefinition
			counter += loadBeanDefinitions(resource);
		}
		return counter;
	}
複製代碼

接下去的源碼不細講,這裏載入分爲兩大步,

  1. 一就是經過調用XML的解析器獲取到 document 對象,完成通用XML解析;
  2. 二就是按照Spring的Bean規則進行解析。Spring的Bean規則進行解析這個過程是BeanDefinitionDocumentReader來實現的,裏面包含了各類Spring Bean定義規則的處理。

這裏我以爲核心知識點就是Spring Bean規則的解析,簡單點來講,裏面包含了咱們在XML配置的那些信息,怎麼解析成容器中 BeanDefinition的規則和步驟。這部分因爲和主要流程關係不大,我就沒貼源碼解析了,會佔掉很大的篇幅,影響閱讀和理解。

在這由於Spring 的 Bean配置方式有不少,解析配置信息到BeanDefinition的實現方式也有不少,XML又是如今少用的方式,因此關於XML中的Spring Bean規則的解析的詳細源碼就先略過了。有興趣的同窗能夠閱讀《Spring 技術內幕》這本書或者其餘的文章書籍。

2.3 註冊Bean

雖然上面說了不講XML 解析 成 BeanDefinition的過程源碼。可是上述loadBeanDefinitions(resource)包含了咱們關鍵的第三步,註冊Bean。這部分仍是須要填一下的。

注意一下前面實例化Reader的代碼,

XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

	/** * Create new XmlBeanDefinitionReader for the given bean factory. * @param registry the BeanFactory to load bean definitions into, * in the form of a BeanDefinitionRegistry */
	public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
		super(registry);
	}
複製代碼

beanDefinitionReader 獲取到 beanFactory 的引用,這個引用會在beanDefinition 被加載完畢要註冊的時候使用到。能夠看到是由於BeanDefinitionRegistry這個接口,賦予了BeanFactory註冊BeanDefinition的特性。

具體執行「註冊Bean」這一動做的源碼,按照上述loadBeanDefinitions(resource)方法一直走下去的話是在DefaultBeanDefinitionDocumentReader#processBeanDefinition()方法中,

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		if (bdHolder != null) {
			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
			try {
				// Register the final decorated instance.
                // 註冊Bean
				BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to register bean definition with name '" +
						bdHolder.getBeanName() + "'", ele, ex);
			}
			// Send registration event.
			getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
		}
	}
複製代碼

源碼位置BeanDefinitionReaderUtils#registerBeanDefinition()

/** * Register the given bean definition with the given bean factory. * @param definitionHolder the bean definition including name and aliases * @param registry the bean factory to register with * @throws BeanDefinitionStoreException if registration failed */
	public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {

		// Register bean definition under primary name.
        // 註冊
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

		// Register aliases for bean name, if any.
        // 若是還有別名的,把別名全都註冊一遍
		String[] aliases = definitionHolder.getAliases();
		if (aliases != null) {
			for (String alias : aliases) {
                // 到時候獲取的時候,就是先把Alias轉化成BeanName,再去獲取對應的Bean
				registry.registerAlias(beanName, alias);
			}
		}
	}
複製代碼

上面的registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());,

源碼位置DefaultListableBeanFactory#registerBeanDefinition()

@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {

		Assert.hasText(beanName, "Bean name must not be empty");
		Assert.notNull(beanDefinition, "BeanDefinition must not be null");

		if (beanDefinition instanceof AbstractBeanDefinition) {
			try {
				((AbstractBeanDefinition) beanDefinition).validate();
			}
			catch (BeanDefinitionValidationException ex) {
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Validation of bean definition failed", ex);
			}
		}

        // 注意哦,這裏是有關 「容許Bean覆蓋」 的邏輯代碼
        // 記得這個配置 allowBeanDefinitionOverriding
		BeanDefinition oldBeanDefinition;

        // beanDefinitionMap 是存放全部BeanDefinition的容器
		oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        
        // not null 說明,有重複名稱的bean
		if (oldBeanDefinition != null) {
			if (!isAllowBeanDefinitionOverriding()) {
                // 判斷是否容許覆蓋,不容許直接拋異常
				throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
						"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
						"': There is already [" + oldBeanDefinition + "] bound.");
			}
			else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                // 打下debug log...用框架定義的 Bean 覆蓋用戶自定義的 Bean
				if (this.logger.isWarnEnabled()) {
					this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
							"' with a framework-generated bean definition: replacing [" +
							oldBeanDefinition + "] with [" + beanDefinition + "]");
				}
			}
			else if (!beanDefinition.equals(oldBeanDefinition)) {
                // 打下debug log...用新的 Bean 覆蓋舊的 Bean
				if (this.logger.isInfoEnabled()) {
					this.logger.info("Overriding bean definition for bean '" + beanName +
							"' with a different definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
			else {
                // 打下debug log...用同等的 Bean 覆蓋舊的 Bean
				if (this.logger.isDebugEnabled()) {
					this.logger.debug("Overriding bean definition for bean '" + beanName +
							"' with an equivalent definition: replacing [" + oldBeanDefinition +
							"] with [" + beanDefinition + "]");
				}
			}
            // 覆蓋了
			this.beanDefinitionMap.put(beanName, beanDefinition);
		}
		else {
            // 判斷是否有其餘Bean已經開始初始化了
			if (hasBeanCreationStarted()) {
				// Cannot modify startup-time collection elements anymore (for stable iteration)
                // 檢測建立 Bean 階段已經開啓,須要對 beanDefinitionMap 進行併發控制
				synchronized (this.beanDefinitionMap) {
					this.beanDefinitionMap.put(beanName, beanDefinition);
					List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
					updatedDefinitions.addAll(this.beanDefinitionNames);
					updatedDefinitions.add(beanName);
					this.beanDefinitionNames = updatedDefinitions;
					if (this.manualSingletonNames.contains(beanName)) {
						Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
						updatedSingletons.remove(beanName);
						this.manualSingletonNames = updatedSingletons;
					}
				}
			}
			else {
				// Still in startup registration phase
                // 最最最正常的分支
                
                // 註冊 到 容器中
				this.beanDefinitionMap.put(beanName, beanDefinition);
                // 這是一個 ArrayList,因此會按照 bean 配置的順序保存每個註冊的 Bean 的名字
				this.beanDefinitionNames.add(beanName);
                // 這是個 LinkedHashSet,表明的是手動註冊的 singleton bean,
                // 注意這裏是 remove 方法,到這裏的 Bean 固然不是手動註冊的
         		// 手動指的是經過調用如下方法註冊的 bean :
         		// registerSingleton(String beanName, Object singletonObject)
         		// 這不是重點,解釋只是爲了避免讓你們疑惑。Spring 會在後面"手動"註冊一些 Bean,
         		// 如 "environment"、"systemProperties" 等 bean,咱們本身也能夠在運行時註冊 Bean 到容器中的
				this.manualSingletonNames.remove(beanName);
			}
			this.frozenBeanDefinitionNames = null;
		}

		if (oldBeanDefinition != null || containsSingleton(beanName)) {
			resetBeanDefinition(beanName);
		}
	}
複製代碼

以上,只是obtainFreshBeanFactory()的內容,到這裏,BeanFactory也算是實例化完成了。


這裏仍是來個分割線。由於接下去會講refresh() 方法的後續步驟的知識點,我想讀者同窗在這裏最好最好,回到 前面refresh()總述的部分,再看一下。(若是你是一邊還打開着IDE,在對照閱讀調試的話,回到最前面refresh() 方法,再繼續往下)

3. prepareBeanFactory(beanFactory)

此方法負責對BeanFactory進行一些特徵的設置工做,這些特徵在代碼中都有體現。

/** * Configure the factory's standard context characteristics, * such as the context's ClassLoader and post-processors. * @param beanFactory the BeanFactory to configure */
	protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
		// Tell the internal bean factory to use the context's class loader etc.
        // BeanFactory 須要加載類,因此須要得到類加載器
        // 設置當前ApplicationContext的類加載器
		beanFactory.setBeanClassLoader(getClassLoader());
        // 內含 Spel 解釋器,暫時不重要
		beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        // 註冊屬性編輯器,暫時不重要
		beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

		// Configure the bean factory with context callbacks.
        // 添加一個ApplicationContextAwareProcessor,主要針對實現了Aware接口的Bean
        // 延伸知識:在Spring中咱們本身的bean能夠經過實現EnvironmentAware等一系列Aware接口,獲取到Spring內部的一些對象。
		beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        // 依賴解析忽略, 設置哪些接口在進行依賴注入的時候應該被忽略
        // 通俗來講,下面幾行的意思就是,若是某個 bean 依賴於如下幾個接口的實現類,在自動裝配的時候忽略它們,
   		// Spring 會經過其餘方式來處理這些依賴。
		beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
		beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
		beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
		beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
		beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

		// BeanFactory interface not registered as resolvable type in a plain factory.
		// MessageSource registered (and found for autowiring) as a bean.
        /** * 下面幾行就是爲特殊的幾個 bean 賦值,若是有 bean 依賴瞭如下幾個,會注入這邊相應的值, * 以前咱們說過,"當前 ApplicationContext 持有一個 BeanFactory",這裏解釋了第一行 * ApplicationContext 還繼承了 ResourceLoader、ApplicationEventPublisher、MessageSource * 因此對於這幾個依賴,能夠賦值爲 this,注意 this 是一個 ApplicationContext * 那這裏怎麼沒看到爲 MessageSource 賦值呢?那是由於 MessageSource 被註冊成爲了一個普通的 bean */
		beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
		beanFactory.registerResolvableDependency(ResourceLoader.class, this);
		beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
		beanFactory.registerResolvableDependency(ApplicationContext.class, this);

		// Register early post-processor for detecting inner beans as ApplicationListeners.
        // 這個 BeanPostProcessor 也很簡單,在 bean 實例化後,若是是 ApplicationListener 的子類,
   		// 這個postProcessor的做用就是將其添加到 listener 列表中,能夠理解成:註冊 事件監聽器
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

		// Detect a LoadTimeWeaver and prepare for weaving, if found.
        // 這裏涉及到特殊的 bean,名爲:loadTimeWeaver,AspectJ相關內容
        // 不是這裏的重點,放過我
		if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
			beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
			// Set a temporary ClassLoader for type matching.
			beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
		}
        // Register default environment beans.
        // Spring 的「智能」操做,會幫咱們默認註冊一些有用的Bean
        // 若是沒有定義 "environment" 這個 bean,那麼 Spring 會 "手動" 註冊一個
		if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
		}
        // 若是沒有定義 "systemProperties" 這個 bean,那麼 Spring 會 "手動" 註冊一個
		if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
		}
        // 若是沒有定義 "systemEnvironment" 這個 bean,那麼 Spring 會 "手動" 註冊一個
		if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
			beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
		}
	}
複製代碼

4. ★ finishBeanFactoryInitialization(beanFactory) 實例化全部單例

這裏會負責初始化全部的 singleton beans。

Spring 會在這個階段完成全部的 singleton beans 的實例化。

到目前爲止,應該說 BeanFactory 已經建立完成,而且全部的實現了 BeanFactoryPostProcessor 接口的 Bean 都已經初始化而且其中的 postProcessBeanFactory(factory)方法已經獲得回調執行了。並且 Spring 已經「手動」註冊了一些特殊的 Bean,如 environmentsystemProperties 等。

剩下的就是初始化 singleton beans 了,咱們知道它們是單例的,若是沒有設置懶加載,那麼 Spring 會在接下來初始化全部的 singleton beans。

源碼位置:AbstractApplicationContext#finishBeanFactoryInitialization()

/** * Finish the initialization of this context's bean factory, * initializing all remaining singleton beans. */
	protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// Initialize conversion service for this context.
        // 初始化"conversionService"的bean,此接口用於類型之間的轉化,不是重點,放過我,本身去看
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}

		// Register a default embedded value resolver if no bean post-processor
		// (such as a PropertyPlaceholderConfigurer bean) registered any before:
		// at this point, primarily for resolution in annotation attribute values.
        // 就是爲了解析註解的值,沒啥重點
		if (!beanFactory.hasEmbeddedValueResolver()) {
			beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
				@Override
				public String resolveStringValue(String strVal) {
					return getEnvironment().resolvePlaceholders(strVal);
				}
			});
		}

		// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
        // 前面說過的,不是這裏的重點
		String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
		for (String weaverAwareName : weaverAwareNames) {
            // 先初始化 LoadTimeWeaverAware 類型的bean
			getBean(weaverAwareName);
		}

		// Stop using the temporary ClassLoader for type matching.
		beanFactory.setTempClassLoader(null);

		// Allow for caching all bean definition metadata, not expecting further changes.
        // 看方法名就知道了,凍結全部BeanDefinition的元數據了
        // 沒什麼別的目的,由於到這一步的時候,Spring 已經開始預初始化 singleton beans 了,
        // 確定不但願這個時候還出現 bean 定義解析、加載、註冊。
		beanFactory.freezeConfiguration();

		// Instantiate all remaining (non-lazy-init) singletons.
        // 開始初始化,進來看吧,重點在裏面
		beanFactory.preInstantiateSingletons();
	}
複製代碼

源碼位置:DefaultListableBeanFactory#preInstantiateSingletons()

@Override
	public void preInstantiateSingletons() throws BeansException {
		if (this.logger.isDebugEnabled()) {
			this.logger.debug("Pre-instantiating singletons in " + this);
		}
		
        // copy 一個包含全部BeanName的集合副本
		List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);

		// 觸發全部非懶加載的 singleton beans的初始化
		for (String beanName : beanNames) {
            // Bean 可能有繼承parent的關係,獲取合併後的RootBeanDefinition
            // 這個知識點用的不多的
			RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            
            // 非抽象、非懶加載的singletons
			if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                // FactoryBean知識點,不瞭解的看另外一篇文章或者本身google
				if (isFactoryBean(beanName)) {
                    // FactoryBean 會在 beanName前面加前綴"&"
					final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
					boolean isEagerInit;
                    // SmartFactoryBean, 非重點,沒深刻了解,放過我
					if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
						isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
							@Override
							public Boolean run() {
								return ((SmartFactoryBean<?>) factory).isEagerInit();
							}
						}, getAccessControlContext());
					}
					else {
						isEagerInit = (factory instanceof SmartFactoryBean &&
								((SmartFactoryBean<?>) factory).isEagerInit());
					}
					if (isEagerInit) {
						getBean(beanName);
					}
				}
				else {
                    // 正常的bean都到這裏來了,重點哦,裏面進行初始化了
                    // 調用鏈很複雜,單獨拉出來說,先繼續
					getBean(beanName);
				}
			}
		}
        
        // 前面流程走完,說明全部的非懶加載singletonBean 完成了初始化

		// Trigger post-initialization callback for all applicable beans...
        // 看註釋就懂了,若是咱們定義的 bean 是實現了 SmartInitializingSingleton 接口的,
        // 那麼在這裏獲得回調,忽略它吧。
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged(new PrivilegedAction<Object>() {
						@Override
						public Object run() {
							smartSingleton.afterSingletonsInstantiated();
							return null;
						}
					}, getAccessControlContext());
				}
				else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}
複製代碼

小知識點 關於 lazy-init

ApplicationContext 實現的默認行爲就是在啓動時將全部 singleton bean提早進行實例化。提早實例化意味着做爲初始化過程的一部分,ApplicationContext 實例會建立並配置全部的singleton bean。一般狀況下這是件好事,由於這樣在配置中的任何錯誤就會即刻被發現(不然的話可能要花幾個小時甚至幾天)

有時候這種默認處理可能並不是你想要的。若是你不想讓一個singleton bean 在ApplicationContext 實如今初始化時被提早實例化,那麼能夠將bean設置爲延遲實例化。一個延遲初始化bean 將告訴IoC 容器是在啓動時仍是在第一次被用到時實例化。

須要說明的是,**若是一個bean被設置爲延遲初始化,而另外一個非延遲初始化的singleton bean 依賴於它,那麼當ApplicationContext 提早實例化singleton bean時,它必須也確保全部上述singleton 依賴bean也被預先初始化,固然也包括設置爲延遲實例化的bean。**所以,若是Ioc容器在啓動的時候建立了那些設置爲延遲實例化的bean的實例,你也不要以爲奇怪,由於那些延遲初始化的bean可能在配置的某個地方被注入到了一個非延遲初始化singleton bean裏面。

結語

以上,本文就是關於Spring IoC 容器初始化的主要內容。

Spring IoC 的設計中,Bean定義的解析和Bean的依賴注入,是兩個獨立的過程,前面全部內容講的就是IoC容器的初始化,資源定位、載入以及解析BeanDefinition而且註冊。

最後一步的實例化全部單例,引入了getBean()方法,這就是Spring IoC 依賴注入的入口。也是下節源碼解讀的主要內容。

另外說一句,上面的源碼解析,確定不會是完備的,只是提取了我認爲重要的東西。

若有疏漏,敬請諒解和本身查閱相關資料學習。若是錯誤,敬請指正!

相關文章
相關標籤/搜索