谈谈 Spring xml 配置文件中的命名空间,以及一些例外情况

发布于: 2020 年 07 月 06 日

导言

在平时的开发中,我们习惯于复制xml文件,而且能够奏效。但你有没有想过,这里面的元素都是什么含义呢?今天,我们就聊聊其中的命名空间以及一些例外情况,希望能够拓展一些知识点。

我们来看一个简单的配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd">
</beans>

第一个问题:你知道里面的xmlns和xsi是什么意思吗?

xmlns和xsi

最容易记住的方式,是理解它们的英文全称:

  • xmlns:xml namespace,也就是xml命名空间的意思。

  • xsi:XMLSchema-instance,取的其中三个单词首字母,分别是xml的“x”,schema的“s”,instance的“i”。也就是xml元素的定义文件,我们可以通过属性schemaLocation可以指定命名空间对应的xsd文件(xsd文件用于定义元素的组织形式,可以参见:https://www.cnblogs.com/newsouls/archive/2011/10/28/2227765.html)。

注意,xmlns和xsi:schemaLocation一般是对应的(记住这里的“一般”,因为有例外,后面讲),比如:

  • 命名空间“http://www.springframework.org/schema/beans”,对应的是“http://www.springframework.org/schema/beans/spring-beans.xsd”。我们可以点进去这个文件看beans的各个属性。

  • 命名空间“http://www.springframework.org/schema/aop”,对应的是“http://www.springframework.org/schema/aop/spring-aop.xsd”。

  • 我们也可以自定义自己的命名空间和对应的xsd文件。

其中,xmlns:aop的aop是命名空间的名字,其他xmlns:task同理。但beans对应的命名空间是默认的,前面的“<beans”已经指明了,因此也就不能写成xmlns:beans了。

Spring配置文件的一些例外

仔细观察上面的文件,我们可以发现,有两个命名空间没有对应的xsd文件:

  • xmlns:p="http://www.springframework.org/schema/p"

  • xmlns:c="http://www.springframework.org/schema/c"

这两个命名空间分别对应构造器参数和属性。举个例子:我们定义三个bean;​其中名为x的bean,通过构造器传入y,通过属性传入z:

@Component
public class X {
private final Y y;
private Z z;
public X(Y y) {
this.y = y;
}
public void setZ(Z z) {
this.z = z;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("X{");
sb.append("y=").append(y);
sb.append(", z=").append(z);
sb.append('}');
return sb.toString();
}
}

@Component
public class Y {
}

@Component
public class Z {
}

此时配置文件可以这样写:

文件名:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd">
<bean class="X"
c:y-ref="y"
p:z-ref="z"
/>
<bean id="y" class="Y"/>
<bean id="z" class="Z"/>
</beans>

其中,第21行的c就对应于第8行定义的c命名空间,p命名空间同理。

那么第二个问题来了:它们的xsd文件哪去了?

带着问题,我们搜索“xmlns:p="http://www.springframework.org/schema/p"”,得到结果:

In spring, p-namespace is the XML short cut to inject dependency in bean. p-namespace replaces <property> tag of XML. and it only exits in core of spring. We can directly assign the attribute name of the class with p-namespace within bean tag. We can use p-namespace in place of <property> tag in spring XML.

好了,我们现在知道它们没有对应的xsd文件,那么Spring是怎么解析的呢?

我们可以尝试全局搜索"www.springframework.org/schema/p",可以发现它配置在spring-beans包的META-INF文件夹下的spring.handlers文件中:

该文件记录了命名空间及其对应的handler。可以想到:handler会在bean的处理过程中解析对应的值。下面我们以c命名空间看看Spring是怎么解析的。

先写入口类,然后调试即可:

public class Main {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
X x = (X) context.getBean("x");
System.out.println(x);
}
}
输出:
X{y=Y@460ebd80, z=Z@6f3c660a}

我们可以看到,经过一系列xml文件的解析步骤,最终进到了org.springframework.beans.factory.xml.BeanDefinitionParserDelegate中:

其中的node已经走到了c:y-ref这一步。从下图可以看到bean的一些属性都是保存在arrtibutes字段中的。每个属性又是一个node,最终会走到上面说的类中进行decorate,也就是装饰。

继续深入:

我们会发现第1444行取出了当前的命名空间,第1446行会取出spring.handles文件中定义的org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler。

具体地,先读取spring.handlers,获取所有的handlers并放到map中(注意,不只是spring-beans包下面的spring.handlers,其他包里面的spring.handlers也会被搜索),再根据key(也即命名空间)取出对应的handler

取到具体的handler后,会进到decorate中,将对应的值注入进去:

其他handler同理,不再赘述。

扩展

每个命名空间都有对应的handler(或处理机制)。p和c两个命名空间没有对应的xsd的原因,可能是因为其语法比较简单,没有必要专门定义xsd吧。

总结

本文内容分为两块。

首先,介绍了Spring xml配置文件中的一些命名空间含义。其实这是xml文件的概念,并非Spring的特性。

然后,我们介绍了p命名空间和c命名空间这两种没有xsd文件的例外情况,并通过跟踪源码了解它们的执行原理。

发布于: 2020 年 07 月 06 日 阅读数: 14
用户头像

xiaoxi666

关注

翻岩卷浪,尽享激荡人生。 2017.11.09 加入

博客园:http://www.cnblogs.com/xiaoxi666/ GitHub : https://github.com/xiaoxi666

评论

发布
暂无评论
谈谈Spring xml配置文件中的命名空间,以及一些例外情况