• 7-Spring架构源码分析-IoC 之注册 BeanDefinitions


    IoC 之注册 BeanDefinitions

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

    // 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • <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 对象。代码如下:

    /**
     * documentReader 的类
     *
     * @see #createBeanDefinitionDocumentReader() 
     */
    private Class documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
    
    protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
    	return BeanUtils.instantiateClass(this.documentReaderClass);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • documentReaderClass 的默认值为 DefaultBeanDefinitionDocumentReader.class 。关于它,我们在后续的文章,详细解析。

    2. registerBeanDefinitions

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

    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;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

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

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

    DefaultBeanDefinitionDocumentReader 对该方法提供了实现:

    @Nullable
    private XmlReaderContext readerContext;
    
    @Nullable
    private BeanDefinitionParserDelegate delegate;
        
    /**
     * This implementation parses bean definitions according to the "spring-beans" XSD
     * (or DTD, historically).
     * 

    Opens a DOM Document; then initializes the default settings * specified at the {@code } 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 } element. */ @SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...) protected void doRegisterBeanDefinitions(Element root) { // Any nested elements will cause recursion in this method. In // order to propagate and preserve 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 对象,并进行设置到 delegate this.delegate = createDelegate(getReaderContext(), root, parent); // <2> 检查 根标签的命名空间是否为空,或者是 http://www.springframework.org/schema/beans if (this.delegate.isDefaultNamespace(root)) { // <2.1> 处理 profile 属性。 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
    • 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
    • <1> 处,创建 BeanDefinitionParserDelegate 对象,并进行设置到 delegate 。BeanDefinitionParserDelegate 是一个重要的类,它负责解析 BeanDefinition。代码如下:

      FROM 《Spring 源码深度解析》P16

      定义解析 XML Element 的各种方法

      protected BeanDefinitionParserDelegate createDelegate(
              XmlReaderContext readerContext, Element root, @Nullable BeanDefinitionParserDelegate parentDelegate) {
          // 创建 BeanDefinitionParserDelegate 对象
          BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
          // 初始化默认
          delegate.initDefaults(root, parentDelegate);
          return delegate;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • <2> 处,检查 标签的命名空间是否为空,或者是 http://www.springframework.org/schema/beans 。

      • <2.1> 处,判断是否 上配置了 profile 属性。
      • <2.2> 处,使用分隔符切分,可能有多个 profile 。
      • <2.3> 处,判断,如果所有 profile 都无效,则 return 不进行注册。
    • <4> 处,调用 #parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) 方法,进行解析逻辑。 。

    • <3> / <5> 处,解析前后的处理,目前这两个方法都是空实现,交由子类来实现。代码如下:

      protected void preProcessXml(Element root) {}
      
      protected void postProcessXml(Element root) {}
      
      • 1
      • 2
      • 3
    2.1.1 parseBeanDefinitions

    #parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) 方法,进行解析逻辑。代码如下:

    /**
     * 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);
        }
    }
    
    • 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
    • Spring 有

      两种

      Bean 声明方式:

      • 配置文件式声明: 。对应 <1> 处。
      • 自定义注解方式: 。对应 <2> 处。
    • <1> 处,如果节点或节点使用默认命名空间,调用 #parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 方法,执行默认解析。代码如下:

      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);
      	}
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 详细的解析,见后续文章。
    • <2> 处,如果节点或节点不使用默认命名空间,调用 BeanDefinitionParserDelegate#parseCustomElement(Element ele) 方法,执行自定义解析。详细的解析,见后续文章。

    3. createReaderContext

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

    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());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

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

    4. 小结

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

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

    在这里插入图片描述

    时序图

    • 红框部分,就是 BeanDefinition 的解析过程
  • 相关阅读:
    无法远程连接到kafka
    【角点检测】 基于matlab GUI图像角点检测【含Matlab源码 2082期】
    在{{}}中拼接字符
    ChatGLM2 大模型微调过程中遇到的一些坑及解决方法(更新中)
    AI绘画提示词创作指南:DALL·E 2、Midjourney和 Stable Diffusion最全大比拼 ⛵
    【GNS3 GraduProj】路由器Ansible脚本测试(文件备份)
    DotNet 中 npgsql无法正常使用的处理
    Ubuntu18.04 ros 安装ZED SDK---基于x86架构
    一张图搞定英文星期、月份、季节总也搞不定的星期,月份,季节,一张图搞定,还有必用的常见搭配,再也不担心用错介词了~
    自媒体账号的权重规则你知道哪些?
  • 原文地址:https://blog.csdn.net/xianghanscce/article/details/126065441