1. 程式人生 > >Spring原始碼解析——從XmlBeanFactory的建構函式開始看LoadBeanDefinitions

Spring原始碼解析——從XmlBeanFactory的建構函式開始看LoadBeanDefinitions

        之前的文章聊過ClassPathResource類,通過這個類,我們從classpath載入到了我們的spring配置檔案,之後,就開始執行XmlBeanFactory的構造過程了:

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}

  

     先看我們的super方法:

/**
	 * Create a new AbstractAutowireCapableBeanFactory.
	 */
	public AbstractAutowireCapableBeanFactory() {
		super();
		ignoreDependencyInterface(BeanNameAware.class);
		ignoreDependencyInterface(BeanFactoryAware.class);
		ignoreDependencyInterface(BeanClassLoaderAware.class);
	}

   ignoreDependencyInterface的主要功能是忽略給定介面的自動裝配功能。

   方法原型:

public void ignoreDependencyInterface(Class<?> ifc) {
		this.ignoredDependencyInterfaces.add(ifc);
	}

   這貨其實是個set:
/**
	 * Dependency interfaces to ignore on dependency check and autowire, as Set of
	 * Class objects. By default, only the BeanFactory interface is ignored.
	 */
	private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<Class<?>>();

     例如,我這裡加入了BeanNameAware.class,BeanFactoryAware.class,BeanClassLoaderAware.class,如果對於A中有屬性B的情況,當我們獲取A的時候,如果B沒有被初始化,則會預設初始化B,但是,比如B實現了上述三個介面中任意一個,就會不去自動注入B,轉而使用其他方式對B進行注入。

      追溯完super方法,回到XmlBeanFactory建構函式中,   執行 this.reader.loadBeanDefinitions(resource); 這句話是整個資源載入的切入點。

/**
	 * Load bean definitions from the specified XML file.
	 * @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
	 */
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}

    在loadBeanDefinitions方法中,會呼叫自身建構函式,對傳入的Resource進行封裝:
public class EncodedResource {

	private final Resource resource;

	private String encoding;

	private Charset charset;


	/**
	 * Create a new EncodedResource for the given Resource,
	 * not specifying a specific encoding.
	 * @param resource the Resource to hold
	 */
	public EncodedResource(Resource resource) {
		Assert.notNull(resource, "Resource must not be null");
		this.resource = resource;
	}

	/**
	 * Create a new EncodedResource for the given Resource,
	 * using the specified encoding.
	 * @param resource the Resource to hold
	 * @param encoding the encoding to use for reading from the resource
	 */
	public EncodedResource(Resource resource, String encoding) {
		Assert.notNull(resource, "Resource must not be null");
		this.resource = resource;
		this.encoding = encoding;
	}

	/**
	 * Create a new EncodedResource for the given Resource,
	 * using the specified encoding.
	 * @param resource the Resource to hold
	 * @param charset the charset to use for reading from the resource
	 */
	public EncodedResource(Resource resource, Charset charset) {
		Assert.notNull(resource, "Resource must not be null");
		this.resource = resource;
		this.charset = charset;
	}

   通過兩個成員變數encoding跟charset來看,發現只是為我們的Resource類包裝了編碼的東西。

   接著正式進入loadBeanDefinitions方法:

/*
	1,首先對傳入的Resource做封裝,程式設計EncodedResource,這個物件可能帶著編碼。
	2,通過SAX讀取XML檔案的方式來準備InputSource物件
	3,最後將準備的資料傳入真正核心處理的部分doLoadBeanDefinitions(inputSource, encodedResource.getResource());
 */


/**
	 * 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:
		resourcesCurrentlyBeingLoaded:
			private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
			new NamedThreadLocal<Set<EncodedResource>>("XML bean definition resources currently being loaded");

		通過這個屬性來記錄已經載入的資源,如果載入過程中發現重複,報個異常
		 */
		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輸入流轉為InputSource
				之後進入doLoadBeanDefinitions方法
			*/
			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();
			}
		}
	}

之後進入到doLoadBeanDefinitions方法:

/**
	 * 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
	 */
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
		try {
			int validationMode = getValidationModeForResource(resource);
			Document doc = this.documentLoader.loadDocument(
					inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
			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);
		}
	}

    程式碼沒兩行,倒是異常挺多,估計這裡轉換Document物件時候,也預設包含對文件格式的一些驗證。

     上午先到這裡!