文章

IOC-注册BeanDefinitions

IOC-注册BeanDefinitions

本文主要基于 Spring 5.0.6.RELEASE

摘要: 原创出处 http://cmsblogs.com/?p=2697 「小明哥」,谢谢!


获取 XML Document 对象后,会根据该对象和 Resource 资源对象调用 XmlBeanDefinitionReader#registerBeanDefinitions(Document doc, Resource resource) 方法,开始注册 BeanDefinitions 之旅。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// AbstractBeanDefinitionReader.java
private
final
BeanDefinitionRegistry registry; // XmlBeanDefinitionReader.java
public
int registerBeanDefinitions(Document
doc,
Resource resource) throws BeanDefinitionStoreException {
    // <1> 创建 BeanDefinitionDocumentReader 对象    BeanDefinitionDocumentReader
    documentReader = createBeanDefinitionDocumentReader(); // <2> 获取已注册的 BeanDefinition 数量
    int
    countBefore =
    getRegistry().getBeanDefinitionCount(); // <3> 创建 XmlReaderContext 对象    // <4> 注册 BeanDefinition
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 计算新注册的
    BeanDefinition 数量    return getRegistry().getBeanDefinitionCount() - countBefore;
}
  • <1> 处,调用 #createBeanDefinitionDocumentReader() 方法,实例化 BeanDefinitionDocumentReader 对象。

FROM 《Spring 源码深度解析》P16 页

定义读取 Document 并注册 BeanDefinition 功能

  • <2>已注册 处,调用 BeanDefinitionRegistry#getBeanDefinitionCount() 方法,获取 的 BeanDefinition 数量。
  • <3> 处,调用 #createReaderContext(Resource resource) 方法,创建 XmlReaderContext 对象。
  • <4> 处,调用 BeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext) 方法,读取 XML 元素,注册 BeanDefinition 们。
  • <5>算新注册 处,计 的 BeanDefinition 数量。

1. createBeanDefinitionDocumentReader

#createBeanDefinitionDocumentReader(),实例化 BeanDefinitionDocumentReader 对象。代码如下:

1
2
3
4
5
6
7
8
/** * documentReader 的类 * * @see #createBeanDefinitionDocumentReader()
*/
private Class<? extends BeanDefinitionDocumentReader> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
    return BeanUtils.instantiateClass(this.documentReaderClass);
}


  • documentReaderClass 的默认值为 DefaultBeanDefinitionDocumentReader.class 。关于它,我们在后续的文章,详细解析。

2. registerBeanDefinitions

BeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext) 方法,注册 BeanDefinition ,在接口 BeanDefinitionDocumentReader 中定义。代码如下:

1
2
3
4
5
6
7
8
public interface BeanDefinitionDocumentReader {
    /**     * Read bean definitions from the given DOM document and
    * register them with the registry in the given reader context.     * @param doc the DOM document
    * @param readerContext the current context of the reader
    * (includes the target registry and the resource being parsed)     * @throws BeanDefinitionStoreException in case of parsing errors
    */
    void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)    throws BeanDefinitionStoreException;
}

从给定的 Document 对象中解析定义的 BeanDefinition 并将他们注册到注册表中。方法接收两个参数:

  • doc 方法参数:待解析的 Document 对象。
  • readerContext「3. createReaderContext」 方法,解析器的当前上下文,包括目标注册表和被解析的资源。它是根据 Resource 来创建的,见 。

2.1 DefaultBeanDefinitionDocumentReader

BeanDefinitionDocumentReader 有且只有一个默认实现类 DefaultBeanDefinitionDocumentReader 。它对 #registerBeanDefinitions(…) 方法的实现代码如下:

DefaultBeanDefinitionDocumentReader 对该方法提供了实现:

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
@Nullable
private XmlReaderContext readerContext;
@Nullable
private BeanDefinitionParserDelegate delegate;
/** * This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically). * <p>Opens a DOM Document;
then initializes the default settings
* specified at the {
    @code <beans/>
}
level;
then parses the contained bean definitions. */
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext; // 获得 XML
    Document Root Element    // 执行注册
    BeanDefinition    doRegisterBeanDefinitions(doc.getDocumentElement());
}
/** * Register each bean definition within the given root {
    @code <beans/>
}
element. */
@SuppressWarnings("deprecation")  //
for
Environment.acceptsProfiles(String...)protected void doRegisterBeanDefinitions(Element root) {
    // Any nested <beans> elements will cause recursion in
    this method. In// order to propagate and preserve <beans>
    default-* attributes correctly,// keep track of the current (parent) delegate, which may be
    null. Create// the
    new (child) delegate with a reference to the parent for fallback purposes,// then ultimately reset this.delegate back to its original (parent) reference.// this behavior emulates a stack of delegates without actually necessitating one.// 记录老的 BeanDefinitionParserDelegate 对象BeanDefinitionParserDelegate parent =
    this.delegate; // <1> 创建
    BeanDefinitionParserDelegate 对象并进行设置到 delegatethis.delegate = createDelegate(getReaderContext(), root, parent);
    // <2> 检查 <beans /> 根标签的命名空间是否为空,或者是 http://www.springframework.org/schema/beansif (this.delegate.isDefaultNamespace(root)) {    // <2.1> 处理 profile 属性。可参见《Spring3自定义环境配置 <beans profile="">》http://nassir.iteye.com/blog/1535799    String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
    if (StringUtils.hasText(profileSpec)) {        // <2.2> 使用分隔符切分,可能有多个 profile 。        String[]
    specifiedProfiles =
    StringUtils.tokenizeToStringArray(            profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // <2.3> 如果所有 profile 都无效,则不进行注册        // 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;
        }
    }
} // <3> 解析前处理preProcessXml(root);// <4> 解析parseBeanDefinitions(root,
this.delegate);
// <5> 解析后处理postProcessXml(root);
// 设置 delegate 回老的
BeanDefinitionParserDelegate 对象this.delegate = parent;
}



  • <1>解析 BeanDefinition 处,创建 BeanDefinitionParserDelegate 对象,并进行设置到 delegate 。BeanDefinitionParserDelegate 是一个重要的类,它负责 。代码如下: FROM 《Spring 源码深度解析》P16 定义解析 XML Element 的各种方法
1
2
3
4
5
6
7
8
9
protected BeanDefinitionParserDelegate createDelegate(    XmlReaderContext readerContext, Element root, @Nullable
BeanDefinitionParserDelegate parentDelegate) {
    // 创建 BeanDefinitionParserDelegate 对象    BeanDefinitionParserDelegate delegate =
    new
    BeanDefinitionParserDelegate(readerContext);
    // 初始化默认    delegate.initDefaults(root, parentDelegate);
    return delegate;
}

  • <2>http://www.springframework.org/schema/beans 处,检查 标签的命名空间是否为空,或者是 。
    • <2.1>《《Spring3自定义环境配置 》》 处,判断是否 上配置了 profile 属性。不了解这块的胖友,可以看下 。
    • <2.2>多个 处,使用分隔符切分,可能有 profile 。
    • <2.3> 处,判断,如果所有 profile 都无效,则 return 不进行注册。
  • <4>「3.1 parseBeanDefinitions」 处,调用 #parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) 方法,进行解析逻辑。详细解析,见 。
  • <3>前后 / <5> 处,解析 的处理,目前这两个方法都是空实现,交由子类来实现。代码如下:
1
2
3
4
5
6
protected void preProcessXml(Element root) {
}
protected void postProcessXml(Element root) {
}


2.1.1 parseBeanDefinitions

#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
27
/** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    // <1> 如果根节点使用默认命名空间,执行默认解析
    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; // <1> 如果该节点使用默认命名空间,执行默认解析
                if (delegate.isDefaultNamespace(ele)) {
                    parseDefaultElement(ele, delegate); // 如果该节点非默认命名空间,执行自定义解析                }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            } // <2> 如果根节点非默认命名空间,执行自定义解析    }
            else {
                delegate.parseCustomElement(root);
            }
        }



  • Spring 有两种 Bean 声明方式:
    • 配置文件式声明: 。对应 <1> 处。
    • 自定义注解方式: 。对应 <2> 处。
  • <1>根子使用 处,如果 节点或 节点 默认命名空间,调用 #parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 方法,执行默认解析。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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)) {
                // beans        // recurse
                doRegisterBeanDefinitions(ele);
            }
        }



```plain text

  • 详细的解析,见后续文章。

    ```

  • <2>根子不使用自定义 处,如果 节点或 节点 默认命名空间,调用 BeanDefinitionParserDelegate#parseCustomElement(Element ele) 方法,执行 解析。详细的解析,见后续文章。

3. createReaderContext

#createReaderContext(Resource resource) 方法,创建 XmlReaderContext 对象。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private ProblemReporter problemReporter = new FailFastProblemReporter();
private ReaderEventListener eventListener = new EmptyReaderEventListener();
private SourceExtractor sourceExtractor = new NullSourceExtractor();
@Nullable
private NamespaceHandlerResolver namespaceHandlerResolver;
/** * Create the {
    @link XmlReaderContext
}
to pass over to the document reader. */
public XmlReaderContext createReaderContext(Resource resource) {
    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,                            this.sourceExtractor, this, getNamespaceHandlerResolver());
}


关于 XmlReaderContext 的详细解析,见后续文章。

4. 小结

至此,XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource inputSource, Resource resource) 方法中,做的三件事情已经全部分析完毕,下面将对 BeanDefinition 的解析过程做详细分析说明。

另外,XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource inputSource, Resource resource) 方法,整体时序图如下:

时序图

  • 红框部分,就是 BeanDefinition 的解析过程
本文由作者按照 CC BY 4.0 进行授权