1. 程式人生 > >spring 原始碼解讀與設計詳解:5 XmlBeanDefinitionReader與Resource

spring 原始碼解讀與設計詳解:5 XmlBeanDefinitionReader與Resource

spring資源載入過程簡單說分三步:

1、定位資源,也就是找到配置檔案

2、載入檔案,將檔案解析為一個個元素和屬性

3、將bean物件註冊到ioc容器中,如果存在依賴注入,則根據配置檔案選擇是否在載入容器的時候將依賴注入載入,預設是true

下面我們一步一步分析DefaultListableBeanFactory以及資源載入過程:

ClassPathResource resource = new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader  reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
1、在DefaultListableBeanFactory中一個非常重要的常量beanDefinitionMap,描述了配置檔案中的bean相關的物件,都封裝成BeanDefinition物件,放到beanDefinitionMap中,以beannamekey。參考原始碼為:
/** Map of bean definition objects, keyed by bean name */
	private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(64);

2、我們先來看第一步定位資源

ClassPathResourceresource = new ClassPathResource("beans.xml");

我們首先從Resource的根類org.springframework.core.io.Resource原始碼來分析:

 * @author Juergen Hoeller
 * @since 28.12.2003
 * @see #getInputStream()
 * @see #getURL()
 * @see #getURI()
 * @see #getFile()
 * @see WritableResource
 * @see ContextResource
 * @see FileSystemResource
 * @see ClassPathResource
 * @see UrlResource
 * @see ByteArrayResource
 * @see InputStreamResource
 * @see PathResource
 */
public interface Resource extends InputStreamSource {}

ClassPathResource是具體的一種resource了。看類的註釋:

/**
 * {@link Resource} implementation for class path resources.
  * 翻譯:為 classpath 資源的實現類
 * Uses either a given ClassLoader or a given Class for loading resources.
 * 
 * <p>Supports resolution as {@code java.io.File} if the class path
 * resource resides in the file system, but not for resources in a JAR.
*翻譯:支援資原始檔在classpath的系統檔案中,且不在jar包中
 * Always supports resolution as URL.
 * 
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @since 28.12.2003
 * @see ClassLoader#getResourceAsStream(String)
 * @see Class#getResourceAsStream(String)
 */

public class ClassPathResource extends AbstractFileResolvingResource {}

我們來看classpathresouce的構造方法,可以看到這句程式碼完成了將引數賦予了成員變數並例項化了類構造器。

/**
	 * Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
	 * A leading slash will be removed, as the ClassLoader resource access
	 * methods will not accept it.
	 * <p>The thread context class loader will be used for
	 * loading the resource.
	 * @param path the absolute path within the class path
	 * @see java.lang.ClassLoader#getResourceAsStream(String)
	 * @see org.springframework.util.ClassUtils#getDefaultClassLoader()
	 */
	public ClassPathResource(String path) {
		this(path, (ClassLoader) null);
	}

跟進this(path, (ClassLoader) null);

/**
	 * Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
	 * A leading slash will be removed, as the ClassLoader resource access
	 * methods will not accept it.
	 * @param path the absolute path within the classpath
	 * @param classLoader the class loader to load the resource with,
	 * or {@code null} for the thread context class loader
	 * @see ClassLoader#getResourceAsStream(String)
	 */
	public ClassPathResource(String path, ClassLoader classLoader) {
		Assert.notNull(path, "Path must not be null");
		String pathToUse = StringUtils.cleanPath(path);
		if (pathToUse.startsWith("/")) {
			pathToUse = pathToUse.substring(1);
		}
		this.path = pathToUse;
		this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
	}

3DefaultListableBeanFactory factory = newDefaultListableBeanFactory();

這行程式碼例項化了一個工廠,為下面的reader做準備。

4

XmlBeanDefinitionReader  reader = newXmlBeanDefinitionReader(factory);

重點是最後一行程式碼:

reader.loadBeanDefinitions(resource);

/**
	 * Load bean definitions from the specified XML file.
	*翻譯:載入制定xml檔案的bean的定義
	 * @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
	 */
	@Override
	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
		return loadBeanDefinitions(new EncodedResource(resource));
	}
	

/**
	 * 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());
		}

		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();
			}
		}
	}

逐行跟進,最後定位都核心實現程式碼:

DefaultBeanDefinitionDocumentReader:

/**
	 * This implementation parses bean definitions according to the "spring-beans" XSD
	 * (or DTD, historically).
	 * <p>Opens a DOM Document; then initializes the default settings
	 * specified at the {@code <beans/>} level; then parses the contained bean definitions.
	 */
	@Override
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;
		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();
		doRegisterBeanDefinitions(root); // 核心實現,下篇文章繼續講解
	}