0%

spring源码(二)-XmlBeanDefinitionReader

BeanDefinitionReader

BeanDefinitionReader 的作用是读取 Spring配置文件中的内容,将之解析为BeanDefinition并注册到BeanDefinitionRegistry工厂中。所以接口中定义的基本方法包括获取Bean定义注册工厂,加载Bean定义资源文件等方法。

源码

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
/**
* BeanDefinitionReader 的作用是读取 Spring配置文件中的内容,
* 将之解析为BeanDefinition并注册到 BeanDefinitionRegistry 工厂中。
* @see org.springframework.core.io.Resource
*/
public interface BeanDefinitionReader {

/**
* 返回用于注册bean定义的bean工厂,工厂通过BeanDefinitionRegistry暴露出来
*/
BeanDefinitionRegistry getRegistry();

/**
* 资源加载器,主要应用于根据给定的资源文件地址返回对应的Resource
*/
@Nullable
ResourceLoader getResourceLoader();

/**
* 返回类加载器
*/
@Nullable
ClassLoader getBeanClassLoader();

/**
* BeanName生成器
* 为没有明确指定bean名称的Bean生成一个名称
*/
BeanNameGenerator getBeanNameGenerator();


/**
* 从指定的资源加载bean定义,返回找到的bean定义的数量
*/
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;

/**
* 指定多个资源加载bean定义,返回找到的bean定义的数量
*/
int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;

/**
* 从指定的资源位置加载bean定义
* 该位置也可以是位置模式,前提是此bean定义读取器的ResourceLoader是ResourcePatternResolver。
*/
int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;

/**
* 加载多个配置文件路径
*/
int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;

}

BeanDefinitionReader实现类

类图如下图所示,可以看到 BeanDefinitionReader 下有一个抽象子类 AbstractBeanDefinitionReaderAbstractBeanDefinitionReader下有三个子类。

image-20220822195840816

  • EnvironmentCapable:指示包含和公开Environment引用的组件的接口。
  • AbstractBeanDefinitionReader:为 BeanDefinitionReader 接口的抽象实现类,实现了 EnvironmentCapable,提供了获取/设置环境的方法
  • XmlBeanDefinitionReader:读取 XML 文件定义的 BeanDefinition
  • PropertiesBeanDefinitionReader:可以从属性文件,Resource,Property 对象等读取 BeanDefinition
  • GroovyBeanDefinitionReader:可以读取 Groovy 语言定义的 Bean

AbstractBeanDefinitionReader

该类实现了 BeanDefinitionReaderEnvironmentCapable 接口的抽象类,提供属性:注册 bean 定义的bean 工厂、资源加载器、加载 bean 类的类加载器、环境、BeanName 生成器。

源码

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
/**
* 实现BeanDefinitionReader接口的 bean 定义读取器的抽象基类。
* 提供通用属性,例如要处理的 bean 工厂和用于加载 bean 类的类加载器。
* @see BeanDefinitionReaderUtils
*/
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {

/** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());

//注册bean定义的bean工厂
private final BeanDefinitionRegistry registry;

//资源加载起
@Nullable
private ResourceLoader resourceLoader;

//类加载器
@Nullable
private ClassLoader beanClassLoader;

//环境
private Environment environment;

//beanName生成器
private BeanNameGenerator beanNameGenerator = DefaultBeanNameGenerator.INSTANCE;

/**
* 为给定的 bean 工厂创建一个新的 AbstractBeanDefinitionReader。
* 如果传入的 bean factory 不仅实现了 BeanDefinitionRegistry 接口,还实现了 ResourceLoader 接口,那么它也会被用作默认的 ResourceLoader。
* 这通常是org.springframework.context.ApplicationContext实现的情况。
* 如果给定一个普通的 BeanDefinitionRegistry,默认的 ResourceLoader 将是一个PathMatchingResourcePatternResolver 。
* 如果传入的 bean 工厂也实现了EnvironmentCapable它的环境将被这个读者使用。否则,阅读器将初始化并使用StandardEnvironment 。
* 所有的 ApplicationContext 实现都是 EnvironmentCapable,而普通的 BeanFactory 实现不是
* @see #setResourceLoader
* @see #setEnvironment
*/
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
this.registry = registry;

// 如果传入的registry实现了ResourceLoader采用传入的,否则采用PathMatchingResourcePatternResolver
if (this.registry instanceof ResourceLoader) {
this.resourceLoader = (ResourceLoader) this.registry;
}
else {
this.resourceLoader = new PathMatchingResourcePatternResolver();
}

// 查看继承的环境是否可用,不可用则新建StandardEnvironment
if (this.registry instanceof EnvironmentCapable) {
this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
}
else {
this.environment = new StandardEnvironment();
}
}
}

该类中最核心的方法是 loadBeanDefinitions()方法,首先查看覆盖父接口的三个方法:

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
   @Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int count = 0;
for (String location : locations) {
//通过调用loadBeanDefinitions(String locations)
count += loadBeanDefinitions(location);
}
return count;
}

@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}

@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int count = 0;
for (Resource resource : resources) {
//调用子类实现的loadBeanDefinitions(Resource resource)
count += loadBeanDefinitions(resource);
}
return count;
}

本类中新增的loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources)

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
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
//获取资源加载器
ResourceLoader resourceLoader = getResourceLoader();
//对资源加载器判空
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
}

if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
//根据资源路径调用ResourcePatternResolver的getResources方法,此方法可以加载多个资源
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//根据资源来加载bean定义,调用本类loadBeanDefinitions(Resource... resources)方法
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
}
return count;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
//此方法只能加载一个资源 (绝对路径)
Resource resource = resourceLoader.getResource(location);
//调用父类的loadBeanDefinitions(resources)方法,然后走不同的子类实现
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isTraceEnabled()) {
logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
}
return count;
}
}

根据资源加载器的不同,来处理资源路径,从而返回多个或一个资源,然后再将资源作为参数传递给 loadBeanDefinitions(Resource... resources)方法,该方法用于处理多个资源,归根结底,最后还是调用 BeanDefinitionReader#loadBeanDefinitions(resource)方法,该方法的具体实现就交给了子类进行处理

XmlBeanDefinitionReader

该类作为 AbstractBeanDefinitionReader 的扩展类,继承了 AbstractBeanDefinitionReader 所有的方法,同时也扩展了很多新的方法,主要用于读取 XML 文件中定义的 bean。具体使用如下:

新建application-context.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans

https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
">

<!-- <context:component-scan base-package="com.wch.study.spring"></context:component-scan>-->
<bean id="user" class="com.wch.study.spring.entity.User" name="user4,user5;user6">
</bean>

<alias name="user" alias="user2"/>
<alias name="user5" alias="user3"/>
</beans>

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void xmlBeanDefinitionReaderTest(){
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory();
AbstractBeanDefinitionReader beanDefinitionReader=new XmlBeanDefinitionReader(beanFactory);
int beanDefinitions = beanDefinitionReader.loadBeanDefinitions("application-context.xml");
System.out.println("加载了:"+beanDefinitions+"个");
//根据bean name 获取
User user = (User) beanFactory.getBean("user");
System.out.println("user:"+user);
//根据别名获取
System.out.println("user2:"+beanFactory.getBean("user2"));
System.out.println("user3:"+beanFactory.getBean("user3"));

//根据bean名称获取别名数组
String[] user2s = beanFactory.getAliases("user2");
for (String s : user2s) {
System.out.println(s);
}
}

输出:

加载了:1个
user:User{name=’null’, address=’null’}
user2:User{name=’null’, address=’null’}
user3:User{name=’null’, address=’null’}
user
user5
user3
user6
user4

解析

从我们写的这段代码进入 reader.loadBeanDefinitions("spring-config.xml"),上面已经提到最终会调用BeanDefinitionReader#loadBeanDefinitions(resource)方法,该方法的具体实现就交给了子类进行处理,而我们指定的子类实现为XmlBeanDefinitionReader,调试进入子类实现,可以看到它加载Resource并封装为EncodedResource,并指定编码,当然它的 encoding、 charset 都是null。

1
2
3
4
5
6
7
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return this.loadBeanDefinitions(new EncodedResource(resource)); >1
}

public EncodedResource(Resource resource) {
this(resource, (String)null, (Charset)null);
}

然后进入到loadBeanDefinitions(EncodedResource encodedResource) 方法,该方法主要根据encodedResource获得InputSource对象,并将InputSource对象参数和Resource对象参数传递给下层方法。

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
	//当前正在加载的XML bean定义资源,使用ThreadLocal ,这样可以避免资源重复加载
private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
new NamedThreadLocal<Set<EncodedResource>>("XML bean definition resources currently being loaded"){
@Override
protected Set<EncodedResource> initialValue() {
return new HashSet<>(4);
}
};

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}

Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
//判断currentResources中是否包含encodedResource,如果有则抛出异常,没有则加入(检测循环加载)
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
//获取Resource对应的字节流
try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
//inputStream 流转为一个 InputSource 对象
InputSource inputSource = new InputSource(inputStream);
//如果资源有编码格式,那就给 inputSource 对象也设置上编码格式
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//该方法做进一步的解析操作,也是创建BeanDefinition的关键
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
//移除当前正在加载的XML bean定义资源
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}

然后进入doLoadBeanDefinitions(InputSource inputSource, Resource resource) 方法,该方法主要是将xml 资源文件转换为Document对象并根据Dcoument对象注册BeanDefinition

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
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {

try {
//将资源文件解析成 Document 对象
Document doc = doLoadDocument(inputSource, resource);
//根据返回的Dcoument注册Bean信息
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}

来看一下 doLoadDocument(InputSource inputSource, Resource resource)方法:

1
2
3
4
5
6
7
//定义从资源文件加载 到 转换为 Document 的功能
private DocumentLoader documentLoader = new DefaultDocumentLoader();

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware()); >1
}

doLoadDocument 方法里最终调用的是 documentLoader.loadDocument ()方法。 该方法的调用,一共需要五个参数:

  • InputSource:要调用的资源文件。
  • EntityResolver: 处理文件的验证方式。
  • ErrorHandler: 错误处理器。
  • validationMode: XML 文件的验证模式。
  • namespaceAware: 是否开启自动感知名称空间。

documentLoader.loadDocument ()点击会进入到 DocumentLoader接口,该接口下只有一个实现:DefaultDocumentLoader。 具体的调用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//根据校验模式,命名空间感知创建DocumentBuilderFactory对象
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
//创建DocumentBuilder
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
//将inputSource解析为Document对象
return builder.parse(inputSource);
}

通过DocumentBuilder的parse方法就会将 XML 里的配置解析为 Document对象。使用这个Document对象可以获取 XML 文件中的节点并且创建节点。

createDocumentBuilderFactory(validationMode, namespaceAware):创建DocumentBuilderFactory对象

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
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
throws ParserConfigurationException {

// 1、获取DocumentBuilderFactory实例
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);

// 2、如果开启xml验证的话,则验证xml
if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
factory.setValidating(true);
// 如果xml验证模式为XSD则需要强制指定由此代码生成的解析器将提供对XML名称空间的支持
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
// Enforce namespace aware for XSD...
factory.setNamespaceAware(true);
try {
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
}
catch (IllegalArgumentException ex) {
ParserConfigurationException pcex = new ParserConfigurationException(
"Unable to validate using XSD: Your JAXP provider [" + factory +
"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
pcex.initCause(ex);
throw pcex;
}
}
}

return factory;
}

createDocumentBuilder(factory, entityResolver, errorHandler):创建DocumentBuilder对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
protected DocumentBuilder createDocumentBuilder(DocumentBuilderFactory factory,
@Nullable EntityResolver entityResolver, @Nullable ErrorHandler errorHandler)
throws ParserConfigurationException {

//创建DocumentBuilder对象
DocumentBuilder docBuilder = factory.newDocumentBuilder();
// 如果entityResolver不为空,则给docBuilder设置entityResolver
if (entityResolver != null) {
docBuilder.setEntityResolver(entityResolver);
}
// 如果errorHandler不为空,则给docBuilder设置errorHandler
if (errorHandler != null) {
docBuilder.setErrorHandler(errorHandler);
}
return docBuilder;
}

接下来看doLoadBeanDefinitions方法中调用的registerBeanDefinitions(doc, resource)方法。

1
2
3
4
5
6
7
8
9
10
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 创建BeanDefinitionDocumentReader对象,完成 BeanDefinition 的解析和注册
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//之前的bd数量
int countBefore = getRegistry().getBeanDefinitionCount();
//注册bd
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 本次注册的bd数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}

这个方法中重点关注documentReader.registerBeanDefinitions(doc, createReaderContext(resource))方法。点击进入就会到 BeanDefinitionDocumentReader接口,该接口的作用就是定义读取 Docuemnt 并注册 BeanDefinition

该接口就一个实现类 DefaultBeanDefinitionDocumentReader,接下来就看 DefaultBeanDefinitionDocumentReader 类中的 registerBeanDefinitions 方法。

1
2
3
4
5
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}

该方法有两个入参:

  • Document:代指 Spring 的配置文件信息,通过 BeanDefinitionReader 解析 Resrouce 实例得到。
  • XmlReaderContext :主要包含了 BeanDefinitionReaderResrouce

然后进入doRegisterBeanDefinitions(doc.getDocumentElement())方法,进行解析

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
protected void doRegisterBeanDefinitions(Element root) {
//任何嵌套的 <beans> 元素都将导致此方法中的递归。
// 为了正确传播和保留 <beans> 默认属性,请跟踪当前(父)委托,该委托可能为空。
// 使用对父级的引用创建新的(子级)委托以用于回退目的,然后最终将 this.delegate 重置回其原始(父级)引用。
// 这种行为模拟了一堆委托,而实际上不需要一个委托。

//记录当前父委托
BeanDefinitionParserDelegate parent = this.delegate;

// 创建 delegate 对象,解析 Element 的各种方法
this.delegate = createDelegate(getReaderContext(), root, parent);

// 验证 XML 文件的命名空间,
// 即判断是否含有 xmlns="http://www.springframework.org/schema/beans"
if (this.delegate.isDefaultNamespace(root)) {
// 获取profile属性的值 <beans profile="dev,;prd"></beans>
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
//将字符串按照指定的字符转换成String[]数组,如字符串中不包含指定字符,则将整个字符串放进数组。
//如指定字符有多个,是分别按单个字符来切割的。这里按照逗号 分号 空格 分隔 ",; "
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// 判断你指定的环境是否与加载的配置文件的环境一致,如果不一致直接返回
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 前置空方法
preProcessXml(root);
// 解析方法
parseBeanDefinitions(root, this.delegate);
// 后置空方法
postProcessXml(root);

this.delegate = parent;
}

来看其中最重要的方法 parseBeanDefinitions(root, this.delegate);

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
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//是否是DefaultNamespace
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
// 如果符合Spring的命名规则,对该标签进行解析。
// 实例 <bean id="user" class="com.gongj.bean.User"></bean>
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
// 解析用户自定义的规则
// <tx:annotation-driven/>
delegate.parseCustomElement(ele);
}
}
}
}
else {
// 解析用户自定义的规则
delegate.parseCustomElement(root);
}
}

而两种方式的读取及解析差别是非常大的,如果采用 Spring 默认的配置。Spring 当然知道该怎么做,但是如果是自定义的,那么就需要用户实现一些接口及配置,对于根节点或者子节点如果是默认命名空间的话,采用 parseDefaultElement 方法进行解析,否则使用delegate.parseCustomElement 方法对自定义命名空间进行解析。 而判断是否是默认命名空间还是自定义命名空间的办法其实是使用node.getNamespaceURI获取命名空间,并与 Spring 中固定的命名空间http://www.springframework.org/scherna/beans 进行比对,如果一致则认为是默认,否则就认为是自定义。


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
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// 解析import标签 <import resource="spring-config.xml"></import>
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
// 解析alias标签 <alias name="user" alias="user2"></alias>
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
// 解析bean标签 <bean id="user" class="com.gongj.bean.User"></bean>
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
// 解析beans标签
/**
* <beans profile="dev">
* <bean id="user" class="User"></bean>
* <import resource="spring-config.xml"></import>
* <alias name="user" alias="user2"></alias>
* </beans>
*/
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
//调用doRegisterBeanDefinitions,再次重复解析xml的过程
doRegisterBeanDefinitions(ele);
}
}