3.2alias标签解析 ~ 3.4嵌入式Beans标签解析

3.2alias标签解析
终于解析完了默认标签中对bean标签的解析,解析来对配置文件中import标签、alias标签、beans标签进行分析。alias标签,别名标签。在配置文件中,有两种声明别名的方式。1)bean标签的name属性(name支持配置多个,可以用,;分隔)
<bean id="testBean" name="testBean1,testBean2" class="TestBean" />
2)alias标签(alias不支持配置多个)
<bean id="testBean" class="TestBean" />
<alias name="testBean" alias="testBean1" />
<alias name="testBean" alias="testBean2" />
DefaultBeanDefinitionDocumentReader
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        //对import标签处理
        importBeanDefinitionResource(ele);
    }
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        //对alias标签处理
        processAliasRegistration(ele);
    }
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        //对bean标签处理
        processBeanDefinition(ele, delegate);
    }
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // recurse
        //对beans标签处理
        doRegisterBeanDefinitions(ele);
    }
}


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 {
            	//注册别名
				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));
		}
	}

//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) {
        //如果beanName与alias相同的话不记录alias,并删除对应的alias
        if (alias.equals(name)) {
            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)) {
                    // An existing alias - no need to re-register
                    return;
                }
                //如果alias不允许被覆盖则抛出异常
                if (!allowAliasOverriding()) {
                    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时候则会抛出异常
            //当beanName A 指定 别名 B时候
            checkForAliasCircle(name, alias);
            this.aliasMap.put(alias, name);
            if (logger.isTraceEnabled()) {
                logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
            }
        }
    }
}
这里没什么特别的。注册简单来说就是把别名、beanName放入一个Map<String, String> aliasMap = new ConcurrentHashMap<>(16) 中。key为别名,value为beanName。
3.3Import标签的解析
解析import标签做的就是获取resource中的配置文件,然后转成Document,Document转成BeanDefinition......import标签的好处:简化配置后期维护复杂度,并使配置模块化,易于管理。import标签配置
<beans>
	<import resource="applicationProperties1.xml" />
    <import resource="applicationProperties2.xml" />
</beans>
DefaultBeanDefinitionDocumentReader
protected void importBeanDefinitionResource(Element ele) {
    //获取resource属性
    String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
    //如果不存在resource属性则不做处理
    if (!StringUtils.hasText(location)) {
        getReaderContext().error("Resource location must not be empty", ele);
        return;
    }

    // Resolve system properties: e.g. "${user.dir}"
    //解析系统属性,格式如${user.dir}
    location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

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

    // Discover whether the location is an absolute or relative URI
    boolean absoluteLocation = false;
    try {
        //判断localtion是绝对路径还是相对路径
        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.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 {
        // No URL -> considering resource location as relative to the current file.
        //如果是相对路径则根据相对路径算出绝对路径
        try {
            int importCount;
            //Resource存在多个子实现类,如VfsResource,FileSystemResource
            //而每一个resource的createRelative方式实现都不一样,所以这里先使用子类的方法尝试解析
            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.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的解析过程,进行另一次的解析。这里的loadBeanDefinitions(relativeResource)其实就是从第2章开始的解析又走了一次,把relativeResource这个配置文件中的配置解析成BeanDefinition存起来........5)如果是相对路径则计算出绝对路径并进行解析6)通知监听器,解析完成。
3.4嵌入式beans标签解析嵌入式beans标签,非常类似于import标签所提供的功能。将beans转换为Document,Document转成BeanDefinition......
<beans xmlns="http://www.springframework.org/schema/beas">
	<bean id="test" class="Test" />
    <beans></beans>
</beans>



来自为知笔记(Wiz)

上一篇:webpack + ts 配置路径别名无死角方法总结


下一篇:1-5:学习shell之命令的使用