1. 程式人生 > 其它 >第4講:XmlBeanDefinitionReader與Resource

第4講:XmlBeanDefinitionReader與Resource

技術標籤:Spring框架原始碼解讀springjava

觀察defaultlistablebeanfactory的屬性

分析ClasspathResource的建立

分析DefaultListableBeanFactory的建立

1、classPathResource的構造方法

2、分析DefaultListableBeanFactory的建立

3、XmlBeanDefinitinRead的建立

1、classPathResource內部結構很簡單,有兩個重要屬性:

String path:儲存資源路徑的地址

ClassLoader classLoader:類載入器(負責將配置檔案(磁碟)載入到jvm中(記憶體),當然也負責類檔案載入到jvm中)

所以Resource類並沒有做什麼實際性的操作,只是定位資源資訊而已

2、DefaultListableBeanFactory(IOC容器)

就是平時我們所說的Ioc容器,它裡面定義了很多屬性:

我最關心的一個屬性:

Map<String, BeanDefinition> beanDefinitionMap:儲存bean定義資訊

其他幾個次要關心的屬性:

Map<Class<?>, String[]> allBeanNamesByType: 儲存bean名稱和bean型別的關聯關係

Map<Class<?>, String[]> singletonBeanNamesByType: 儲存單例bean名稱和單例bean型別的關聯關係

List<String> beanDefinitionNames: 儲存所有bean定義資訊的名稱

說實話,上述這幾個屬性不用可以去記,後面章節用到了你就知道了,這麼多屬性記住主要的就行了

所以說這個類本身就代表了一個容器,裡面有對這些屬性的一系列操作(crud),可以大膽猜測平時我們獲取bean應該就是直接跟這個類打交道,它就是IOC

3、XmlBeanDefinitinRead

這個類好像也是什麼都沒幹,只是定義了一些屬性而已,當然建立它的時候有會把BeanFactory當作BeanDefinitionRegistry屬性。因為BeanFactory實現了BeanDefinitionRegistry介面。

4、reader.loadBeanDefinitions(resource)

這個方法才在真正解析bean定義,上面的3個步驟都是在做一些準備工作。

/**
	 * Load bean definitions from the specified XML file.
	 * @param encodedResource the resource descriptor for the XML file,
	 * allowing to specify an encoding to use for parsing the file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 */
	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}
            //從ThreadLocal中獲取當前執行緒的Set<EncodedResource>  如需詳細瞭解ThreadLocal,連結:https://www.jianshu.com/p/3c5d7f09dfbd
		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 {
              //獲取resource的輸入流 詳解見程式碼塊1
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
                       //把inputstream包裝成一個InputSource 
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
                        //真正載入beanDefinitions開始了 詳解見程式碼塊2
				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();
			}
		}
	}

程式碼塊1:encodedResource.getResource().getInputStream()

/**
	 * This implementation opens an InputStream for the given class path resource.
	 * @see java.lang.ClassLoader#getResourceAsStream(String)
	 * @see java.lang.Class#getResourceAsStream(String)
	 */
	@Override
	public InputStream getInputStream() throws IOException {
		InputStream is;
		if (this.clazz != null) {
                  //clazz是要給Class物件,通過Class物件將一個路徑資源直接轉換成要給輸入流(詳解見:內部程式碼塊1)
			is = this.clazz.getResourceAsStream(this.path);
		}
		else if (this.classLoader != null) {
			is = this.classLoader.getResourceAsStream(this.path);
		}
		else {
			is = ClassLoader.getSystemResourceAsStream(this.path);
		}
		if (is == null) {
			throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
		}
		return is;
	}
 
   內部程式碼塊1:
   public InputStream getResourceAsStream(String name) {
       //通過下面的程式碼可以看到,其實還是通過呼叫類載入器的方法,實現把一個資源轉換成一個輸入流的
        name = resolveName(name);
        ClassLoader cl = getClassLoader0();
        if (cl==null) {
            // A system class.
            return ClassLoader.getSystemResourceAsStream(name);
        }
        return cl.getResourceAsStream(name); //(詳解見:內部程式碼塊2)
    }
    
    內部程式碼塊2:
    public InputStream getResourceAsStream(String name) {
        //可以看到ClassLoader又是通過URL物件來實現對資源的解析的
        URL url = getResource(name);
        try {
            return url != null ? url.openStream() : null;(詳解見:內部程式碼塊3)
        } catch (IOException e) {
            return null;
        }
    }   
    
    內部程式碼塊3:
    public final InputStream openStream() throws java.io.IOException {
        //更新的程式碼就不看了,我這裡猜想內部實現細節應該是會通過建立InputStream來獲取到資源輸入流
        return openConnection().getInputStream();
    }

程式碼塊2:doLoadBeanDefinitions(inputSource, encodedResource.getResource())

/**
	 * Actually load bean definitions from the specified XML file.
	 * @param inputSource the SAX InputSource to read from
	 * @param resource the resource descriptor for the XML file
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
	 * @see #doLoadDocument
	 * @see #registerBeanDefinitions
	 */
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
                  //獲取到Document物件(詳解見:程式碼塊3)
			Document doc = doLoadDocument(inputSource, resource);
                 //獲取到Document物件(詳解見:程式碼塊4)
			return registerBeanDefinitions(doc, resource);
		}
		catch (BeanDefinitionStoreException ex) {
			throw ex;
		}
		catch (SAXParseException ex) {
			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
					"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
		}
		catch (SAXException ex) {
			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
					"XML document from " + resource + " is invalid", ex);
		}
		catch (ParserConfigurationException ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"Parser configuration exception parsing XML from " + resource, ex);
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"IOException parsing XML document from " + resource, ex);
		}
		catch (Throwable ex) {
			throw new BeanDefinitionStoreException(resource.getDescription(),
					"Unexpected exception parsing XML document from " + resource, ex);
		}
	}

程式碼塊3:doLoadDocument(inputSource, resource)

 /**
	 * Actually load the specified document using the configured DocumentLoader.
	 * @param inputSource the SAX InputSource to read from
	 * @param resource the resource descriptor for the XML file
	 * @return the DOM Document
	 * @throws Exception when thrown from the DocumentLoader
	 * @see #setDocumentLoader
	 * @see DocumentLoader#loadDocument
	 */
   //通過DocumentLoader載入Document物件,具體細節不深入研究
	protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
		return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
				getValidationModeForResource(resource), isNamespaceAware());
	}

程式碼塊4:registerBeanDefinitions(doc, resource)

/**
	 * Register the bean definitions contained in the given DOM document.
	 * Called by {@code loadBeanDefinitions}.
	 * <p>Creates a new instance of the parser class and invokes
	 * {@code registerBeanDefinitions} on it.
	 * @param doc the DOM document
	 * @param resource the resource descriptor (for context information)
	 * @return the number of bean definitions found
	 * @throws BeanDefinitionStoreException in case of parsing errors
	 * @see #loadBeanDefinitions
	 * @see #setDocumentReaderClass
	 * @see BeanDefinitionDocumentReader#registerBeanDefinitions
	 */
   //該方法的返回值是beanDefinitin的個數(感覺沒卵用)
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		//獲取BeanDefinitionDocumentReader物件,用於真正解析Document物件  (詳解見:內部程式碼塊1)
           BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
           //獲取Document中BeanDefinition的個數
		int countBefore = getRegistry().getBeanDefinitionCount();
           //真正解析BeanDefinition (詳解見:第六講)
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 
           //註冊BeanDefinition的數量
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
 
   內部程式碼塊1:
   /**
	 * Create the {@link BeanDefinitionDocumentReader} to use for actually
	 * reading bean definitions from an XML document.
	 * <p>The default implementation instantiates the specified "documentReaderClass".
	 * @see #setDocumentReaderClass
	 */
   //該方法會返回要給BeanDefinitionDocumentReader 的實現類 DefaultBeanDefinitionDocumentReader (該類的介紹詳解見:內部程式碼塊2)
	protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
		return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
	} 
   
   內部程式碼塊2:
   //BeanDefinitionDocumentReader 該類是DefaultBeanDefinitionDocumentReader 的預設實現類,該類中定義了XML文件的結構、元素和屬性名,
   //也就是說xml中出現的元素名稱、元素的屬性名都定義在了這個類的屬性中,如:
   //String NESTED_BEANS_ELEMENT = "beans"; String ALIAS_ELEMENT = "alias"; String NAME_ATTRIBUTE = "name";String ALIAS_ATTRIBUTE = "alias";IMPORT_ELEMENT = "import";RESOURCE_ATTRIBUTE = "resource";PROFILE_ATTRIBUTE = "profile";
   //更多的名稱可在 BeanDefinitionParserDelegate類中檢視,為什麼我會突然提到這個類呢?因為DefaultBeanDefinitionDocumentReader類中確實有引用到這個類
   //BeanDefinitionDocumentReader 類中有一個重要的屬性:BeanDefinitionParserDelegate,它就是真正用於解析BeanDefinition的,是解析BeanDefinition的一個代理 (詳解見:第六講)