【spring 原始碼】IOC 之bean例項的建立

朽年發表於2019-03-29

在對refreshBeanFactory()詳細解析之前,先來看看spring的applicationContext的繼承樹:

【spring 原始碼】IOC 之bean例項的建立
這裡有兩個重要的介面,一個是BeanFactory,顧名思義是bean的工廠、容器,bean建立好了就回放入這個容器裡,我們要用的bean例項都是從bean工廠提供的。另一個介面是ResourceLoader,它的實現類實現了讀取配置檔案,將配置封裝成bean例項,然後將bean放入bean工廠。

BeanFactory 簡介

BeanFactory繼承樹
ApplicationContext 繼承了 ListableBeanFactory,這個 Listable 的意思就是,通過這個介面,我們可以獲取多個 Bean,最頂層 BeanFactory 介面的方法都是獲取單個 Bean 的。 ApplicationContext 繼承了 HierarchicalBeanFactory,Hierarchical 單詞本身已經能說明問題了,也就是說我們可以在應用中起多個 BeanFactory,然後可以將各個 BeanFactory 設定為父子關係。 AutowireCapableBeanFactory 這個名字中的 Autowire 大家都非常熟悉,它就是用來自動裝配 Bean 用的,但是仔細看上圖,ApplicationContext 並沒有繼承它,不過不用擔心,不使用繼承,不代表不可以使用組合,如果你看到 ApplicationContext 介面定義中的最後一個方法 getAutowireCapableBeanFactory() 就知道了。 ConfigurableListableBeanFactory 也是一個特殊的介面,看圖,特殊之處在於它繼承了第二層所有的三個介面,而 ApplicationContext 沒有。這點之後會用到。

ResourceLoader簡介

接下來進入正題:

refreshBeanFactory() 重新整理beanfactory

// AbstractRefreshableApplicationContext.java - 120

@Override
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 覆蓋、是否允許迴圈引用
      customizeBeanFactory(beanFactory);
      // 載入 Bean 到 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);
   }
}
複製程式碼

整個過程主要分為了三個步驟:

 1.resource定位

 2.beanDefinition的載入。

 3.向ioc容器註冊beanDefinition的過程。
複製程式碼

loadBeanDefinitions(beanFactory) 載入 Bean:

// AbstractXmlApplicationContext.java 80

@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // 給這個 BeanFactory 例項化一個 XmlBeanDefinitionReader
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
 
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
 
   // 初始化 BeanDefinitionReader,其實這個是提供給子類覆寫的,
   initBeanDefinitionReader(beanDefinitionReader);
   // 重點來了,繼續往下
   loadBeanDefinitions(beanDefinitionReader);
}
複製程式碼

讀取配置的操作在 XmlBeanDefinitionReader 中,其負責載入配置、解析。 // AbstractXmlApplicationContext.java 120

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    //如果呼叫的構造器是new ClassPathXmlApplicationContext(String path, Class<?> clazz),獲取其路徑下的資源
   Resource[] configResources = getConfigResources();
   if (configResources != null) {
      reader.loadBeanDefinitions(configResources);
   }
   //如果構造器呼叫的的是 new ClassPathXmlApplicationContext(String configLocation),獲取其資源
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      reader.loadBeanDefinitions(configLocations);
   }
}
複製程式碼

這裡兩個分支都是在載入bean例項,過程類似,只分析reader.loadBeanDefinitions(configResources);

//AbstractBeanDefinitionReader.java L177

public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
	Assert.notNull(resources, "Resource array must not be null");
	int counter = 0;
	for (Resource resource : resources) {
	    //這裡用了典型的模板模式,實際呼叫的是XmlBeanDefinitionReader#loadBeanDefinitions
		counter += loadBeanDefinitions(resource);
	}
	return counter;
}
複製程式碼

//XmlBeanDefinitionReader.java L314

    /**
	 * Load bean definitions from the specified XML file.
	 * 從xml里載入bean例項
	 */
	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		...
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<EncodedResource>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}
複製程式碼

相關文章