Spring默认标签之alias、import标签的解析

Spring默认标签之alias、import标签的解析

alias标签的解析

​ 在对 bean 进行定义时,除了使用id属性来指定名称之外,为了提供多个名称,可以使用 alias 标签来指定。而所有的这些名称都指向同一个 bean,在某些情况下提供别名非常有用,比如为了让应用的每一个组件能更容易地又对公共组件进行引用。

​ 然而,在定义bean时就指定所有的别名并不是总是恰当的。有时我们期望能在当前位置为那些在别处定义的bean 引人别名。在XML配置文件中,可用单独的**元素来完成bean** 别名的定义。

如配置文件中定义了 一个 JavaBean:
<bean id="testBean" class="com.test"/>
要给这个JavaBean增加别名,以方 方便不同对象来调用。我们就可以直接使用bean标签中 的 name 属性:
<bean id="testBean" name="testBean,testBean2" class="com.test"/>

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

<bean id="testBean" class="com.test"/>
<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标签的解析过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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标签的用法如下:

1
2
3
4
5
<beans>
<import resource="customerContext.xml" />
<import resource="systemContext.xml" />
......
</beans>

applicationContext.xml文件中使用import方式导入有模块配置文件,以后若要新增模块,修改这个文件即可。这样会大大简化后期维护的复杂度。

以下为Spring解析import标签的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
protected void importBeanDefinitionResource(Element ele) {
// 获取resource属性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
// 如果resource属性不存在,则直接return
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}

// Resolve system properties: e.g. "${user.dir}"
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

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

// Discover whether the location is an absolute or relative URI
// 判断location是绝对还是相对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*:"
}

// Absolute or relative?
// 如果是绝对URI 直接根据地址加载对应配置文件
if (absoluteLocation) {
try {
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isDebugEnabled()) {
logger.debug("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 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.isDebugEnabled()) {
logger.debug("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));
}

分析上述代码,Spring解析import标签的大致流程如下:

  1. 获取resource属性所表示的路径。
  2. 解析路径中的系统属性。
  3. 判断location是绝对路径还是相对路径
  4. 如果是绝对路径则递归调用bean的解析过程,进行另一次的解析。
  5. 如果是相对路径则计算出绝对路径并进行解析。
  6. 通知监听器,解析完成。
Author: Aaron
Link: https://xjsir.cn/2024/01/17/Spring_tag_parse2/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.