1. 程式人生 > >spring4.2.9 java專案環境下ioc原始碼分析(五)——refresh之obtainFreshBeanFactory方法(@3預設標籤import,alias解析)

spring4.2.9 java專案環境下ioc原始碼分析(五)——refresh之obtainFreshBeanFactory方法(@3預設標籤import,alias解析)

接上篇文章,到了具體解析的時候了,具體的分為兩種情況,一種是預設名稱空間的標籤<bean>;另一種是自定義名稱空間的標籤比如<context:xxx>,<tx:xxx>等。先看下預設的名稱空間的標籤解析。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
		//獲取名稱空間
		if (delegate.isDefaultNamespace(root)) {
			//獲取所有子節點
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					//子節點的名稱空間
					if (delegate.isDefaultNamespace(ele)) {
						//預設名稱空間標籤
						parseDefaultElement(ele, delegate);
					}
					else {
						//自定義名稱空間標籤
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			//自定義名稱空間標籤
			delegate.parseCustomElement(root);
		}
	}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		//import
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		//alias
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		//bean
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		//beans
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}

可以清晰的看到,預設的標籤這裡解析四種,import,alias,bean,beans.一個一個分析

對import標籤的解析再方法importBeanDefinitionResource中

protected void importBeanDefinitionResource(Element ele) {
		//獲取resource屬性
		String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
		//為空報錯
		if (!StringUtils.hasText(location)) {
			getReaderContext().error("Resource location must not be empty", ele);
			return;
		}

		// Resolve system properties: e.g. "${user.dir}"
		//如果存在佔位符從系統屬性中取到,這說到過很多次了
		location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

		Set<Resource> actualResources = new LinkedHashSet<Resource>(4);

		// Discover whether the location is an absolute or relative URI
		boolean absoluteLocation = false;
		try {
			//判斷是不是絕對路徑,帶字首的classpath:和classpath*:,file:http://解析為絕對路徑
			//其他為相對路徑,瞎寫報錯
			absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
		}
		catch (URISyntaxException ex) {
			// cannot convert to an URI, considering the location relative
			// unless it is the well-known Spring prefix "classpath*:"
		}

		// Absolute or relative?
		//絕對路徑
		if (absoluteLocation) {
			try {
				//再次根據資源去解析
				int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
				}
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error(
						"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
			}
		}
		else {
			// No URL -> considering resource location as relative to the current file.
			//相對路徑先去找到絕對路徑再去解析
			try {
				int importCount;
				Resource relativeResource = getReaderContext().getResource().createRelative(location);
				//比如這裡相對路徑寫錯,檔案不存在
				if (relativeResource.exists()) {
					importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
					actualResources.add(relativeResource);
				}
				else {
					String baseLocation = getReaderContext().getResource().getURL().toString();
					importCount = getReaderContext().getReader().loadBeanDefinitions(
							StringUtils.applyRelativePath(baseLocation, location), actualResources);
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
				}
			}
			catch (IOException ex) {
				getReaderContext().error("Failed to resolve current resource location", ele, ex);
			}
			catch (BeanDefinitionStoreException ex) {
				getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
						ele, ex);
			}
		}
		Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
		getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
	}

雖然程式碼不少但是基本邏輯很簡單,就是找到resource的屬性,找到其對應的絕對路徑,然後再次進行解析。看了程式碼是不是很熟悉,我們一般是怎麼用的?

絕對路徑這樣寫:

        <import resource="classpath:springmvc.xml"/> 
	<import resource="classpath*:springmvc.xml"/> 
	<import resource="file:C:\guangju\workspace2\springlearn\src\main\resources\springmvc.xml"/> 

相對路徑這麼寫(相對當期檔案):

比如說你的當前檔案再Context目錄下,你要引用的檔案再根目錄下,就這麼寫

<import resource="../springmvc.xml"/> 

如果當前檔案和引用檔案在同一目錄下就這麼寫

        <import resource="springmvc.xml"/> 
	<import resource="/springmvc.xml"/> 

然後繼續看alias的解析方法processAliasRegistration

這個標籤是怎麼用的呢,主要是給註冊的bean註冊別名,預設情況下一個bean的id就可以索引到該bean但是如果不想用id,或者在不同的場景下用不同的名稱就需要用標籤<alias>

具體這麼用

        <bean id="dao" class="com.pactera.aop.DaoImpl" ></bean>
	<alias name="dao" alias="dao1"/>
這樣這個bean就有個兩個別名,用dao,dao1都可以得到此bean.看程式碼。
protected void processAliasRegistration(Element ele) {
		//獲取name屬性
		String name = ele.getAttribute(NAME_ATTRIBUTE);
		//獲取別名
		String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
		boolean valid = true;
		if (!StringUtils.hasText(name)) {
			getReaderContext().error("Name must not be empty", ele);
			valid = false;
		}
		if (!StringUtils.hasText(alias)) {
			getReaderContext().error("Alias must not be empty", ele);
			valid = false;
		}
		if (valid) {
			try {
				//註冊
				getReaderContext().getRegistry().registerAlias(name, alias);
			}
			catch (Exception ex) {
				getReaderContext().error("Failed to register alias '" + alias +
						"' for bean with name '" + name + "'", ele, ex);
			}
			
			getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
		}
	}
註冊程式碼registerAlias
public void registerAlias(String name, String alias) {
		Assert.hasText(name, "'name' must not be empty");
		Assert.hasText(alias, "'alias' must not be empty");
		//當前的別名和name相同,在別名中移除此別名
		if (alias.equals(name)) {
			this.aliasMap.remove(alias);
		}
		else {
			//別名在aliasMap已經存在直接返回
			String registeredName = this.aliasMap.get(alias);
			if (registeredName != null) {
				if (registeredName.equals(name)) {
					// An existing alias - no need to re-register
					return;
				}
				if (!allowAliasOverriding()) {
					throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
							name + "': It is already registered for name '" + registeredName + "'.");
				}
			}
			//如果是迴圈引用報錯
			checkForAliasCircle(name, alias);
			//別名和beanname放入map
			this.aliasMap.put(alias, name);
		}
	}

典型的迴圈引用

<bean id="dao" class="com.pactera.aop.DaoImpl" name="dao1"></bean>
<alias name="dao1" alias="dao"/>