alias、beans标签的解析
前言
前面的文章已经讲述了bean和import标签的解析,这篇文章讲述的是alias标签和beans标签的解析。话不多说,开始。
alias标签的解析
在对bean进行定义时,除了使用id属性来指定名称之外,为了提供多个名称,可以使用alias标签来指定。而所有的这些名称都指向同一个bean,在某些情况下提供别名非常有用,比如为了让一个应用的每一个组件能更容易地对公共组件进行引用。
然而,在定义bean时就指定所有的别名并不是总是恰当的。有时我们期望能在当前位置为那些在别处定义的bean引入别名。在XML配置文件中,可以使用单独的<alias/>元素来完成bean别名的定义。如配置文件中定义了一个bean:
<bean id="testBean" class="com.joe.TestBean"/>
若要给上面的这个bean增加别名,以方便不同对象来调用。我们可以使用bean标签中的name属性:
<bean id="testBean" name="testBean,testBean2" class="com.joe.TestBean"/>
但是,Spring中还有一种声明别名的方式:
<bean id="testBean" class="com.joe.TestBean"/> <alias name="testBean" alias="testBean,testBean2"/>
上面的例子可能比较模糊,再来一个比较具体的例子:
组件A在XML配置文件中定义了一个名为componentA的DataSource类型的bean,但是组件B却想在其XML文件中以componentB命名来引用此bean。而且在主程序MyApp的XML配置文件中,希望以myApp的名字来引用此bean。最后容器加载3个XML文件来生成最终的ApplicationContext。在此情形下,可通过在配置文件中添加下列alias元素来实现:
<alias name="componentA" alias="componentB"/> <alias name="componentA" alias="myApp"/>
通过这样的配置,每个组件和主程序就可通过唯一的名字来引用同一个数据源而互相不干扰。
了解了alias标签的使用,接下来进行更深入的了解Spring中对于alias标签的解析:
protected void processAliasRegistration(Element ele) {
//获取beanName
String name = ele.getAttribute(NAME_ATTRIBUTE);
//获取alias
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 {
//注册alias
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中。
嵌入式beans标签的解析
对于嵌入式的beans标签,相信大家使用过或者至少接触过,非常类似于import标签所提供的功能,使用方式如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="testBean" class="com.joe.TestBean"/> <beans> <bean id="test" class="com.joe.Test"/> </beans> </beans>
对于嵌入式beans标签来讲,并没有太多可讲,与单独的配置文件并没有太多的差别,无非就是递归调用beans的解析过程:
protected void doRegisterBeanDefinitions(Element root) { //专门处理解析 BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { //处理profile属性 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } //解析前处理,空方法,留给子类实现 preProcessXml(root); parseBeanDefinitions(root, this.delegate); //解析后处理,空方法,留给子类实现 postProcessXml(root); this.delegate = parent; }
逻辑:程序首先处理profile属性,profile主要用于我们切换环境,比如开发、测试、生产环境,非常方便。然后调用parseBeanDefinitions()进行解析,不过在解析方法之前分别调用preProcessXml()和postProcessXml()方法来进行解析前、后处理,目前这两个方法是空实现。
这个方法在注册BeanDefinitions中已经讲述过,这里就不再赘述了。
参考:《Spring源码深度解析》 郝佳 编著: