Spring 深入——IoC 容器 02( 二 )

loadBeanDefinitions() 進行進一步分析 。該方法調用的是本類的一個抽象方法loadBeanDefinitions(DefaultListableBeanFactory var1) , 此方法是模板方法,由子類具體實現:

Spring 深入——IoC 容器 02

文章插圖
而 FSXAC 就是 AbstractXmlApplicationContext 的子類,所以進而分析這個類的具體實現 。
Spring 深入——IoC 容器 02

文章插圖
可以看到:
  1. 創建了 XmlBeanDefinitionReader 類,用于將 XML 文件中的 Bean 讀取出來并加載 。
  2. 調用 XBDR 的 loadBeanDefinitions,開始啟動 BeanDefinition 的加載 。
  3. 在具體的實現中,分別傳入不同的參數 , 但是在此方法中走判斷時,調用了 this.getConfigResources() 這個方法在此類中是返回的 Resource[]是 null,所以走第二個判斷,獲取以字符串數組 , 因為之前在 FSXAC 中就設置好了 。
    Spring 深入——IoC 容器 02

    文章插圖
String[] 傳入調用的是 XmlBeanDefinitionReader 的基類 AbstractBeanDefinitionReader 的方法:
Spring 深入——IoC 容器 02

文章插圖
這里就是將 String 數組中的字符串,一個一個傳入調用本類重載方法,并且對其進行計數 。
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {return this.loadBeanDefinitions(location, (Set)null);}public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {// 取得 ResourceLoader,使用的是 DeaultResourceLoaderResourceLoader resourceLoader = this.getResourceLoader(); // 關鍵代碼1if (resourceLoader == null) {//...略} else {int loadCount;// 調用 DefaultResourceLoader 的 getResource 完成具體的 Resource 定位if (!(resourceLoader instanceof ResourcePatternResolver)) { // 關鍵代碼2Resource resource = resourceLoader.getResource(location);loadCount = this.loadBeanDefinitions((Resource)resource);//... 略} else {// 調用 DefaultResourceLoader 的 getResources 完成具體的 Resource 定位try {Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);loadCount = this.loadBeanDefinitions(resources);//... 略}//... 略}}}可以看到最終調用的是兩個參數的方法:(String location, Set<Resource> actualResources) , 通過上面代碼的簡要分析,我們提取出兩個重要的信息:
  1. ResourceLoader 的作用?
  2. DefaultResourceLoader 的 getResource 完成了具體的 Resource 定位
首先第一個,Spring 將資源的定義和加載區分開來,這里需要注意的是資源的加載也就是 Resource 的加載,而不是 BeanDefinition 的加載 。Resource 定義了統一的資源(抽象并統一各種資源來源),ResourceLoader 定義了這些資源的統一加載 。所以 BeanDefinition 資源的定位過程應該是:將不同 BD 資源獲取途徑經過 Spring 統一封裝為 Resource,再由 ResourceLoader 進行資源加載 , 獲取這些 Resource,給 BeanDefinition 的載入做準備 。
而在這個 FSXAC 的例子中,這個 ResourceLoader 就是 DefaultResourceLoader,來看看是怎么具體實現 getResource().
public Resource getResource(String location) {Assert.notNull(location, "Location must not be null");Iterator var2 = this.protocolResolvers.iterator();Resource resource;do {if (!var2.hasNext()) {if (location.startsWith("/")) {// 處理以 / 標識的 Resource 定位return this.getResourceByPath(location);}// 處理帶有 classpath 表示的 Resourceif (location.startsWith("classpath:")) {return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader());}try {// 處理 URL 表示的 Resource 定位URL url = new URL(location);return new UrlResource(url);} catch (MalformedURLException var5) {// 處理既不是 classpath 也不是 URL 標識的 Resource 定位// 則將 getResource 的責任交給 getResourceByPath(),這個方法時 protected,默認實現是得到一個 ClassPathContextResource 對象,通常會由子類實現該方法 。return this.getResourceByPath(location);}}ProtocolResolver protocolResolver = (ProtocolResolver)var2.next();resource = protocolResolver.resolve(location, this);} while(resource == null);return resource;}通過上述分析,找到了熟悉的方法名: protected Resource getResourceByPath(String path){}
這個方法由子類 FSXAC 實現,這個方法返回的是:FileSystemResource 對象,通過這個對象,Spring 就可以進行相關的 I/O 操作,完成 BeanDefinition 定位 。
實際上這么多過程和細節,都是為了實現一個功能,對 path 進行解析,然后生成一個 FileSystemResource 對象,并返回,給 BeanDefinition 載入過程做準備 。

推薦閱讀