Spring之默认标签的解析(十)alias、import、beans等标签的解析

Spring之默认标签的解析(十)alias、import、beans等标签的解析


通过上面较长的篇幅我们终于分析完了默认标签中对 bean 标签的处理,那么我们之前提到过,对配置文件的解析包括对import标签、 alias标签、 bean标签、 beans标签的处理,现在我们已经完成了最重要 也是最核心的功能,其他的解析步骤骤也都是围绕第 3 个解析而进行的 。在分析了第 3个解析步骤后,再回过头来看着对 alias标签的解析。

alias标签的解析

在对 bean 进行定义时,除了使用 id 属性来指定名称之外,为了提供多个名称,可以使用 alias标签来指定。 而所有的这些名称都指向同一个bean,在某些情况下提供别名非常有用,比如为了让应用的每一个组件能更容易地对公共组件进行引用 。
然而,在定义 bean时就指定所有的别名并不是总是恰当的。 有时我们期望能在当前位置为那些在另lj处定义的 bean 引人别名。 在 XMLf日置文件中,可用单独的元素来完成bean别名的定义。 如配置文件文件中定义了一个 JavaBean:

	<bean id="testBean" class="com.test"/>

要给这个 JavaBean增加别名,以方便不同对象来调用。 我们就可以直接使用 bean标签中的name 属性:

	<bean id="testBean" name="testBean, teatBean2" class="com.test"/>

同样, Spring 还有另外 一 种卢明别名的方式:

	<bean id="testBean" class="com.test"/>
	<alias name="teatBean" alias="testBean, teatBean2"/>

考虑一个更为具体的例子,组件 A 在 XML 配置文件中定义了一个名为 componentA 的DataSource 类型的 bean,但组件B 却想在其 XML 文件中以 componentB 命名来引用此bean。 而且在主程序 MyApp 的 XML配置文件中,希望以 myApp 的名字来引用此 bean。 最后容器加载3个XML 文件来生成最终的 ApplicationContext。 在此情 下,可通过在配置文件中添加下

    <alias name="componentA" alias="componentB"/>
	<alias name="componentA" alias="MyApp"/>

这样一来,每个组件及主程序就可通过唯一名字来引用同一个数据源而互不干扰 。在之前的文档已经讲过了别于 bean 中 name 元素的解析,那么我们现在再来深入分析下对于 alias标签的解析过程。

protected void processAliasRegistration(Element ele) {
	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));
	}
}

可以发现,跟之前讲过的 bean 中的 alias解析大同小异,都是将别名与 beanName组成一 对注册至registry中。 这里不再赘述。

import标签的解析

对于 Spring 配置文件的编 写 , 我想,经历过庞大项目的人,都有那种恐惧的心理,太多的配置文件了 。 所以,分模块是我们经常想到的方法。比如使用 import标签,例如我们可以构造这样的 Spring配置文件:

  • applicationContext.xml
<?xml version="1.0" encoding=”gb2312"?>
<!DOCTYPE beans PUBLIC "-//Spring//DTD BEAN//EN" "http://www.springframework.org/dtd/Spr工ng-beaas.dtd"> 
<beans>
	<import resource="customerContext.xml" /> 
	<import resource="systemContext.xml" />
</beans>

applicationContext.xml 文件中使用 import的方式导人有模块配置文件,以后若有新模块的加入,那就可以简单修改这个文件了。 这样大大简化了配置后期维护的复杂度, 并使配置模块化, 易于管理。 我们来看看 Spring是如何解析 import配置文件的呢?

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;
		}
	
		// 解析系统属性,格式如 "${user.dir}"
		location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
	
		Set<Resource> actualResources = new LinkedHashSet<>(4);
	
		// 判定该位置是绝对URI还是相对URI
		boolean absoluteLocation = false;
		try {
			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*:"
		}
	
		// 如果是绝对路径则直接根据地质加载对应的配置文件
		if (absoluteLocation) {
			try {
				int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
				if (logger.isTraceEnabled()) {
					logger.trace("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 {
			// 如果是相对路径就计算出绝对路径
			try {
				int importCount;
				/* 
				 * Resource存在多个字实现类,如VfsResoure、FileSystemResource等
				 * 每个resource的createRelative 方式实现都不一样,所以这里先试用子类的方法尝试解析
				 */ 
				 
				Resource relativeResource = getReaderContext().getResource().createRelative(location);
				if (relativeResource.exists()) {
					importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
					actualResources.add(relativeResource);
				}
				else {
					// 如果解析不成功,则使用默认的解析器ResourcePatternResolver进行解析
					String baseLocation = getReaderContext().getResource().getURL().toString();
					importCount = getReaderContext().getReader().loadBeanDefinitions(
							StringUtils.applyRelativePath(baseLocation, location), actualResources);
				}
				if (logger.isTraceEnabled()) {
					logger.trace("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[0]);
		getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
	}

配合注释会很好理解,总结一下大致流程:

  1. 获取 resource属性所表示的路径。
  2. 解析路径中的系统属性,格式如“${ user.dir}”。
  3. 判定 location是绝对路径还是相对路径。
  4. 如果是绝对路径则递归调用 bean 的解析过程,进行另一次的解析 。
  5. 如果是相对路径则计算出绝对路径并进行解析。
  6. 通知监听器,解析完成 。

嵌入式beans等标签的解析

对于嵌入式的 beans标签,相信大家使用过或者至少接触过,非常类似于 import标签所提供的功能,使用如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans ... ... >
    <bean id="test" class="com.Test"/>
    
    <beans>
    	.........
    </beans>
</beans>

对于嵌入式 beans 标签来讲,并没有太多可讲,无非是递归调用 beans 的解析过程,重新调用doRegisterBeanDefinitions函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值