上一篇文章提到过Spring中的标签包括默认标签和自定义标签,而两种标签的用法以及解析方式存在着很大的不同,先说说默认标签的解析。
首先我们需要明确两个概念:属性和子元素。
- 属性是指一个
bean
标签它自带的一些属性,例如id
、name
、parent
等,都存放于AbstractBeanDefinition
的各项成员变量中。 - 子元素是指
bean
标签下的一些子标签的内容,例如:
<bean id="mybbTestBean" class="bean.MyTestBean">
<meta key="testStr" value="aaaa"/>
</bean>
其中的meta
就是一个子元素。
默认标签的解析是在parseDefaultElement
函数中进行的,分别对import
、alias
、bean
和beans
做了不同的处理。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
1 bean标签的解析和注册
进入processBeanDefinition(ele, delegate)
方法:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
大致逻辑如下:
- 委托
BeanDefinitionParserDelegate
的parseBeanDefinitionElement
方法进行元素解析,返回bhHolder
。这个bdHolder
实例已经包含我们配置文件中配置的各种属性了,例如class
、name
、id
、alias
等。 - 当返回的
bdHolder
不为空的情况下,若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析。 - 解析完成后,需要对解析后的
bdHolder
进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils
的registerBeanDefinition
方法。 - 最后发出响应事件,通知相关的监听器,这个
bean
就加载完成了。
1.1 解析BeanDefinition
我们进入到BeanDefinitionDelegate
类的parseBeanDefinitionElement
方法。
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
/**
* Parses the supplied {@code <bean>} element. May return {@code null}
* if there were errors during parse. Errors are reported to the
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 解析id属性
String id = ele.getAttribute(ID_ATTRIBUTE);
// 解析name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
// name属性也是alias的一种
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
// 对标签其他属性进行解析,如class、parent等
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
// 如果不存在beanName那么根据Spring提供的命名规则为当前bean生成
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
// 封装进BeanDefinitionHolder中
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
parseBeanDefinitionElement
的主要工作包括如下内容:
- 提取元素中的
id
以及name
属性。 - 进一步解析其他所有属性并统一封装至
GenericBeanDefinition
类型的实例中。 - 如果检测到
bean
没有指定beanName
,就用默认规则为此bean
生成beanName
- 将获取到的信息封装到
BeanDefinitionHolder
的实例中。
进一步查看步骤2中对标签其他属性的解析过程。
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
// 解析class属性
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
// 解析parent属性
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 创建用于承载属性的AbstractBeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 解析默认bean的各种属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 解析元数据
parseMetaElements(ele, bd);
// 解析lookup-method属性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析replaced-method属性
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析构造函数参数
parseConstructorArgElements(ele, bd);
// 解析property子元素
parsePropertyElements(ele, bd);
// 解析qualifier子元素
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
return null;
}
1.1.1 解析子元素constructor-arg
举个Spring构造函数配置的例子:
<bean id="helloBean" class="com.HelloBean">
<constructor-arg index="0">
<value>mutianjie</value>
</constructor-arg>
<constructor-arg index="1">
<value>你好</value>
</constructor-arg>
</bean>
上面的配置实现的功能是对HelloBean自动寻找对应的构造函数,并在初始化的时候将设置的参数传入进去。对于constructor-arg
子元素的解析,Spring是通过parseConstructorArgElements
函数来实现的,具体代码如下:
/**
* Parse constructor-arg sub-elements of the given bean element.
*/
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
parseConstructorArgElement((Element) node, bd);
}
}
}
在循环中,parseConstructorArgElements
函数遍历了所有的<constructor-arg>
标签,提取所有的子元素。具体的解析放在了另一个函数parseConstructorArgElement
中,该函数的具体代码如下:
/**
* Parse a constructor-arg element.
*/
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
// 提取index属性
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
// 提取type属性
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
// 提取name属性
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(indexAttr)) {
try {
int index = Integer.parseInt(indexAttr);
if (index < 0) {
error("'index' cannot be lower than 0", ele);
}
else {
try {
this.parseState.push(new ConstructorArgumentEntry(index));
// 解析ele对应的属性元素
Object value = parsePropertyValue(ele, bd, null);
// 封装解析出来的元素
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
// 不允许重复指定相同的参数
error("Ambiguous constructor-arg entries for index " + index, ele);
}
else {
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
}
}
finally {
this.parseState.pop();
}
}
}
catch (NumberFormatException ex) {
error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
}
}
else {
// 没有index属性则自动寻找
try {
this.parseState.push(new ConstructorArgumentEntry());
Object value = parsePropertyValue(ele, bd, null);
ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
if (StringUtils.hasLength(typeAttr)) {
valueHolder.setType(typeAttr);
}
if (StringUtils.hasLength(nameAttr)) {
valueHolder.setName(nameAttr);
}
valueHolder.setSource(extractSource(ele));
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
}
finally {
this.parseState.pop();
}
}
}
根据以上代码总结出来的流程为:
如果配置中指定了index
属性,那么操作步骤如下:
- 使用
parsePropertyValue
方法解析constructor-arg
的子元素 - 使用
ConstructArgumentValues.ValueHolder
类型来封装解析出来的元素 - 将
type
、name
和index
属性一并封装在ConstructArgumentValues.ValueHolder
中,并添加至当前BeanDefinition
的constructorArgumentValues
的indexedArgumentValue
中。
如果配置中未指定index
属性,前两步操作与指定index
属性的做法相同,但是第三步中,valueHolder
会添加至当前BeanDefinition
的constructorArgumentValues
的genericArgumentValue
中。
在构造函数解析中,解析constructor-arg
的子元素使用的是parsePropertyValue
方法,它是用来解析某一个具体的构造函数参数的。这个方法的代码如下:
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
String elementName = (propertyName != null ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element");
// 一个属性只能对应一种类型,如 ref, value, list等
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we're looking for.
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
subElement = (Element) node;
}
}
}
// 解析constructor-arg的ref属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
// 解析constructor-arg的value属性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
if (hasRefAttribute) {
// ref属性的处理,使用RuntimeBeanReference封装对应的ref名称
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
}
else if (hasValueAttribute) {
// value属性的处理,使用TypedStringValue封装
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
else if (subElement != null) {
// map、list等子元素的处理,如
/**
* <constructor-arg>
* <map>
* <entry key="key" value="value"/>
* </map>
* </constructor-arg>
*/
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
值得注意的是,除了ref
和value
两种子元素之外,还有嵌套子元素如map
、list
、array
等。这类嵌套子元素的处理在parsePropertySubElement
方法中完成,对嵌套子元素进行分类处理。
1.1.2 解析子元素property
parsePropertyElements
函数完成了对property
属性的提取。property
使用方式如下:
<bean id="test" class="test.TestClass">
<property name="testStr" value="aaa"/>
</bean>
<!--或者-->
<bean id="a">
<property name="p">
<list>
<value>aa</value>
<value>bb</value>
</list>
</property>
</bean>
而具体的解析过程与前面的子元素一样,也是使用单数形式的方法parsePropertyElement
来对所有的property
子元素进行解析,代码如下:
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}
parsePropertyElement
方法的实现如下:
public void parsePropertyElement(Element ele, BeanDefinition bd) {
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
可以看到上面函数与构造函数注入方式不同的是将返回值使用PropertyValue
封装,并记录在BeanDefinition
中的propertyValues
属性中。
1.2 AbstractBeanDefinition属性
至此我们便完成了对XML文档到GenericBeanDefinition
的转换,也就是说,所有的配置都可以在GenericBeanDefinition
中找到对应的配置。
1.3 解析默认标签中的自定义标签元素
我们来回顾一下默认标签解析函数的起始函数:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
我们已经用大量篇幅分析了BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele)
这句代码。接下来,我们进入到下一行代码delegate.decorateBeanDefinitionIfRequired(ele, bdHolder)
中。
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}
这段代码的作用是对beanDefinition
进行装饰,当Spring中的bean
使用的是默认的标签配置,但是其中子元素使用了自定义的配置时,这句代码就会起作用,如:
<bean id="test" class="test.MyClass">
<mybean:user username="aaa"/>
</bean>
这并不是自定义标签类型的解析,因为它并不是以bean
的形式而是以自定义属性的形式出现的。decorateBeanDefinitionIfRequired方法实现了寻找自定义标签并根据自定义标签寻找命名空间处理器,并进行进一步的解析。
1.4 注册解析的BeanDefinition
接下来还剩下的工作是对BeanDefinition的注册,也就是BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())
方法的实现。
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 使用beanName作为唯一标识注册
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 注册所有的别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
可以看到,对BeanDefinition
的注册分为两部分:通过beanName
和别名进行注册。
1.4.1 通过beanName注册BeanDefinition
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
/**
* 注册前的最后一次校验,这里的校验不同于之前的XML文件校验
* 主要是对与AbstractBeanDefinition属性中的methodOverrides校验
* 校验methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在
*/
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 这里使用ConcurrentHashMap进行并发访问全局变量beanDefinitionMap
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
// 处理注册已经注册的beanName情况
if (!isAllowBeanDefinitionOverriding()) {
// 如果对应的beanName已经注册且在配置中配置了bean不允许被覆盖,则抛出异常
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
// 注册beanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
// 重置所有beanName对应的缓存
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
一共分为4个步骤。
- 针对
AbstractBeanDefinition的methodOverride
属性进行校验,这里的methodOverride
存放的实际上就是<look-up>
和<replaced-method>
两个子标签所解析的结果。 - 对
bean
已经注册过的情况处理。如果设置了不允许bean
的覆盖,则需要抛出异常,否则直接覆盖。 - 加入
beanDefinitionMap
中缓存,使用ConcurrentHashMap
进行并发访问。 - 清除解析之前留下的对应的
bean
的缓存,例如解决循环依赖时使用的缓存singletonObjects
、earlySingletonObjects
等。
1.4.2 通过别名注册BeanDefinition
通过别名注册BeanDefinition
的registerAlias
方法是在SimpleAliasRegistry
类中定义的。
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized (this.aliasMap) {
if (alias.equals(name)) {
// alias与beanName相同,从aliasMap中移除该alias
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
}
}
else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// 该alias被使用并且指向的是当前beanName
return;
}
// 该alias被使用但是指向的是另外的bean
if (!allowAliasOverriding()) {
// 若用户不允许alias覆盖则抛出异常
throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
if (logger.isDebugEnabled()) {
logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
registeredName + "' with new target name '" + name + "'");
}
}
// 当A->B存在时,若再次出现A->C->B时会出现异常
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
}
}
}
}
由以上代码可知,注册alias的步骤如下:
alias
与beanName
相同时,不需要处理并移除原有的alias
。- 若
alias
已经使用并指向了另外的beanName
,则根据用户是否允许alias
覆盖进行处理。 alias
循环检查。- 注册
alias
。
2 alias标签的解析
在上一节中已经讲过了对于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
中。