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)