1、loadBeanDefinitions(Resource resource)

从指定的XML文件加载bean定义。

1
2
3
4
5
6
7
8
9
10
/**
* 从指定的XML文件加载bean定义。
* @param resource XML文件的资源描述符
* @return 找到的bean定义的数量
* @throws BeanDefinitionStoreException 在加载或解析错误的情况下抛出
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}

首先将Resource包装成EncodeResource:将Resource描述符与用于从资源中读取的特定编码组合在一起,之后调用loadBeanDefinitions(EncodedResource encodedResource)。

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
/**
* 从指定的XML文件加载bean定义。
* @param encodedResource XML文件的资源描述符,允许指定用于解析文件的编码
* @return 找到的bean定义的数量
* @throws BeanDefinitionStoreException 在加载或解析错误的情况下抛出
*/
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);
}

//resourcesCurrentlyBeingLoaded为ThreadLocal类型,将当前encodedResource加入其中
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
//将资源转为流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//核心加载流程
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
//加载结束后,移除资源
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}

2、doLoadBeanDefinitions(InputSource inputSource, Resource resource)

核心流程方法。用于解析资源为Document对象,并注册BeanDefinition。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//使用配置的DocumentLoader实际加载指定的文档。
Document doc = doLoadDocument(inputSource, resource);
//注册给定DOM文档中包含的bean定义。
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
//下面各种异常捕捉,略
}

2.1、doLoadDocument(InputSource inputSource, Resource resource)

使用配置的DocumentLoader实际加载指定的文档。

1
2
3
4
5
6
7
  protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
//1,获取EntityResolver
//2,确定指定的{@link Resource}的验证模式
//3,返回XML解析器是否应该感知XML名称空间。
//默认为DefaultDocumentLoader
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware());
}

首先返回要使用的EntityResolver,如果没有指定,则构建默认的解析器。

1
2
3
4
5
6
7
8
9
10
11
12
protected EntityResolver getEntityResolver() {
if (this.entityResolver == null) {
// Determine default EntityResolver to use.
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader != null) {
this.entityResolver = new ResourceEntityResolver(resourceLoader);
} else {
this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
}
}
return this.entityResolver;
}

确定指定的{@link Resource}的验证模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected int getValidationModeForResource(Resource resource) {
//返回要使用的验证模式。默认为:VALIDATION_AUTO
int validationModeToUse = getValidationMode();
//指示应自动检测验证模式。
if (validationModeToUse != VALIDATION_AUTO) {
return validationModeToUse;
}
//检测要对提供的{@link Resource}标识的XML文件执行哪种验证。
// 如果文件具有{@code DOCTYPE}定义,则使用DTD验证,否则假设XSD验证。
// 如果您想自定义{@link #VALIDATION_AUTO}模式的解析,请覆盖此方法。
int detectedMode = detectValidationMode(resource);
if (detectedMode != VALIDATION_AUTO) {
return detectedMode;
}
//嗯,我们没有得到明确的指示……让我们假设XSD,
// 因为直到检测停止(在找到文档的根标记之前),、
// 显然还没有找到DTD声明
return VALIDATION_XSD;
}

返回XML解析器是否应该感知XML名称空间。

1
2
3
4
public boolean isNamespaceAware() {
//默认false
return this.namespaceAware;
}

loadDocument(InputSource inputSource, EntityResolver entityResolver,ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
使用标准的JAXP-configured的XML解析器在提供的InputSource中加载Document。

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实例,该解析器从XML文档生成DOM对象树。javax.xml.parsers包下抽象类。
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
//获取DocumentBuilder对象
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
//解析为Document
return builder.parse(inputSource);
}

主要用了javax.xml包下的类,通过上面的这几步,获取到Document对象(org.w3c.dom包下类)。

下面来分析BeanDefinition加载的另一个步骤:注册。

2.2、registerBeanDefinitions(Document doc, Resource resource)

注册给定DOM文档中包含的bean定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//通过反射获取BeanDefinitionDocumentReader:用于解析包含Spring bean定义的XML文档。
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,
// 此处调用的是DefaultListableBeanFactory中的getBeanDefinitionCount()方法:返回注册表中定义的bean的数量。
int countBefore = getRegistry().getBeanDefinitionCount();
//首先创建XmlReaderContext传递给documentReader。
//从给定的DOM文档中读取bean定义,并在给定的读取器上下文中将它们注册到注册表中。
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//获取当前注册表中定义数量与没有加载新bean之前数量的差值
return getRegistry().getBeanDefinitionCount() - countBefore;
}
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
//直接访问作为文档元素的子节点,在给定的根{@code <beans/>}元素中注册每个bean定义。
doRegisterBeanDefinitions(doc.getDocumentElement());
}

下面分析核心流程:在给定的根元素中注册每个bean定义。
doRegisterBeanDefinitions(Element root)

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
@SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
protected void doRegisterBeanDefinitions(Element root) {
// 任何嵌套的<beans>元素都将导致此方法中的递归。In
// 为了正确地传播和保存<beans>缺省-*属性,请跟踪当前(父)委托,它可能为空。 Create
// 创建新的(子)委托,并引用父委托进行回退,然后最终重置这个委托。
// 委托回其原始(父)引用。
// 这种行为模拟了一堆委托,实际上并不需要委托。

// 用于解析XML bean定义的有状态委托类。
// 用于主解析器和任何扩展名BeanDefinitionParser或 BeanDefinitionDecorators。
BeanDefinitionParserDelegate parent = this.delegate;
// 创建BeanDefinitionParserDelegate对象
// 初始化默认的惰性初始化、自动装配、依赖项检查设置、init-method、destroy-method和merge设置。
// 通过返回到给定的父元素来支持嵌套的“beans”元素,以防默认值没有在本地显式设置。
this.delegate = createDelegate(getReaderContext(), root, parent);

//Bean定义的Document对象使用了Spring默认的XML命名空间
if (this.delegate.isDefaultNamespace(root)) {
//获取"profile" 根据环境变量设置的环境值,选择对应的profile执行
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
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;
}
}
}

//前置后置在spring中未提供实现
//前置,在开始处理bean定义之前,允许通过处理任何自定义元素类型来扩展XML。
preProcessXml(root);

//解析文档根级别的元素:"import", "alias", "bean".
parseBeanDefinitions(root, this.delegate);
//后置,在我们完成bean定义的处理之后,允许XML通过最后处理任何自定义元素类型来扩展。
postProcessXml(root);

this.delegate = parent;
}

解析文档根级别的元素:”import”, “alias”, “bean”.parseBeanDefinitions(Element root, BeanDefinitionParserDelegate 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) {
//Bean定义的Document对象使用了Spring默认的XML命名空间
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;
//如果根节点或者子节点采用默认命名空间
if (delegate.isDefaultNamespace(ele)) {
//配置文件式解析
//如:<bean id="service" class="com.demo.core.Service"/>
parseDefaultElement(ele, delegate);
} else {
//进行自定义解析
//如:<tx:annotation-driven>
delegate.parseCustomElement(ele);
}
}
}
} else {
//进行自定义解析
delegate.parseCustomElement(root);
}
}

tencent.jpg