Spring Master's Road 12 - BeanDefinitionRegistry and BeanDefinition Merger Analysis

1. What is BeanDefinitionRegistry?

  BeanDefinitionRegistryIs a very important interface that exists in the package Springof org.springframework.beans.factory.support. It is the core component of Springregistration and management in BeanDefinition.

  Let’s review what we said in the previous article BeanDefinition. In Spring, a Beanis an Springobject managed by , and a BeanDefinitionis a Beanconfiguration description, which describes Beanthe data of a . It contains Beaninformation such as the class name, whether it is an abstract class, constructor and attribute values. These metadata will guide Springhow to create and initialize Bean.

  Let's take a look at BeanDefinitionRegistrythe role of . BeanDefinitionRegistryIts main responsibility is to register and manage these BeanDefinition. We can think of it as a BeanDefinitionregistry where new ones are registered BeanDefinition, or existing ones are retrieved and deleted BeanDefinition. It provides methods such as registerBeanDefinition(String, BeanDefinition), removeBeanDefinition(String), and getBeanDefinition(String), for performing these operations.

  Internally Spring, BeanDefinitionRegistryit is usually BeanFactoryimplemented by , especially DefaultListableBeanFactoryand GenericApplicationContext, which all implement this interface.

2. Why do you need BeanDefinitionRegistry?

If BeanDefinitionRegistryit doesn't exist, Springhow will some of its core functionality be affected?

  1. Uniformity of resource parsing : configuration information BeanDefinitionstored as a unified data structure . BeanIf not BeanDefinitionRegistry, each configuration method ( XML, annotation, Javaconfiguration) requires its own specialized data structure. This will not only lead to increased code complexity for resource parsing, but may also create inconsistencies between different parsing mechanisms.

  2. Dependency lookup and injection : BeanDefinitionRegistryProvides a central location that acts as Beana central store of definitions and can quickly look up Beandefinitions. Without it, when you need to inject a Beandependency, Springyou not only need to traverse all configuration sources to find the corresponding one Bean, but you may also encounter Beandefinition inconsistencies, which will significantly reduce performance and accuracy.

BeanExamples of inconsistent definitions are as follows:

<!-- in config1.xml -->
<bean id="sampleBean" class="com.example.SampleBean1" />

<!-- in config2.xml -->
<bean id="sampleBean" class="com.example.SampleBean2" />

  Here, sampleBeanthere are definitions in both configuration files, but they reference different classes. If BeanDefinitionRegistrythese definitions are not handled centrally, you may encounter confusion when Springtrying to initialize , such as trying to create an instance and inject it , and a question arises: which definition should be selected? Or ?sampleBeanSpringServiceAsampleBeanSpringsampleBeancom.example.SampleBean1com.example.SampleBean2

  This is the so-called " Beaninconsistency of definitions" problem. If Springyou don't know which definition is correct, then it may inject the wrong one Bean, causing your application to behave incorrectly or fail. This can also lead to unpredictable errors in the application at runtime because the injected Beanversion or type is not what the application expects.

  By using it BeanDefinitionRegistry, Springyou can detect such problems when your application starts and Beanprovide clear error messages when definitions conflict or are inconsistent, rather than encountering undefined behavior or errors at runtime.

  1. Lazy initialization and scope management : BeanDefinitionRegistrystored Beanscope and other metadata. Without this BeanDefinitionRegistry, Springwhen performing Beanlazy loading or creating from scope Bean, the original configuration resources need to be re-parsed, which increases processing time and can lead to potential configuration errors.

  2. Configuration verification : After everything BeanDefinitionis registered BeanDefinitionRegistry, Springthe configuration can be verified, such as checking circular dependencies, ensuring Beanthe integrity of definitions, etc. If not BeanDefinitionRegistry, it needs to be checked Springat every initialization, which not only results in performance degradation, but also may miss some obscure configuration issues.Bean

  3. Life cycle management : There is no BeanDefinitionRegistrystorage of life cycle callbacks, initialization methods and other information. SpringWhen managing Beanthe life cycle, this information needs to be obtained from the original configuration source. This not only increases the complexity of management, but also makes life cycle callbacks complex and cumbersome.

  In short, no BeanDefinitionRegistry, Springcentralized Beanmanagement will be lost, resulting in reduced efficiency, scattered error handling, and increased complexity of life cycle management. BeanDefinitionRegistryEnsuring Springefficient, consistent and stable operation.

3. Use of BeanDefinitionRegistry

3.1 BeanDefinitionRegistry simple example

  In this example we will create a simple one Beanthat is registered to DefaultListableBeanFactory(which implements BeanDefinitionRegistrythe interface) and then get it from the factory and use this Bean.

The entire code is as follows:

First, we need a Beanclass, which is a simple POJOclass:

package com.example.demo.bean;

public class MyBean {
    
    
    private String message;

    public void doSomething() {
    
    
        System.out.println("Hello, world!");
    }

    public void setMessage(String message){
    
    
        this.message  = message;
    }

    public void getMessage(){
    
    
        System.out.println("Your Message : " + message);
    }
}

We can then use DefaultListableBeanFactoryand RootBeanDefinitionto create and register this Bean:

package com.example.demo;

import com.example.demo.bean.MyBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;

public class DemoApplication {
    
    

    public static void main(String[] args) {
    
    
        // 创建 BeanDefinitionRegistry
        DefaultListableBeanFactory registry = new DefaultListableBeanFactory();

        // 创建一个 BeanDefinition
        BeanDefinition beanDefinition = new RootBeanDefinition(MyBean.class);

        // 注册 BeanDefinition
        registry.registerBeanDefinition("myBean", beanDefinition);

        // 从 BeanFactory 中获取 Bean
        MyBean myBean = registry.getBean("myBean", MyBean.class);

        // 使用 Bean
        myBean.doSomething();  // 输出:Hello, world!
    }
}

  This program will create a named "myBean", Beanwhich Beanis MyBeanan instance of the class. Then we get this BeanFactoryfrom Beanand call its doSomethingmethod, print "Hello, world!".

Insert image description here

3.2 Examples of implementation classes of ImportBeanDefinitionRegistrar

This is mentioned in Part 8 8( Spring Master's Road 8 - The Art of Spring Bean Module Assembly: @Import Detailed Explanation3.5 ). It is a section. You can look back at it. The code will not be pasted again here.

4. Merger of BeanDefinition

We BeanDefinitiondid not explain BeanDefinitionthe merging in the previous article, so we will add additional explanations here.

  1. BeanDefinition

  In Spring, BeanDefinitionis an interface, which defines Beanthe configuration information of , such as Beanthe class name, whether it is a singleton, dependencies, etc. In Spring, each Beancorresponds to an BeanDefinitionobject.

  1. The meaning of merger

  In Spring, there is a special one BeanDefinitioncalled a child BeanDefinition, which is the one we specify XMLthrough the attribute in the configuration file . parentThis kind of child BeanDefinitioncan inherit BeanDefinitionthe configuration information of the parent.

  The merging process is to merge BeanDefinitionthe configuration information of the child with the configuration information of the parent to form a complete configuration information. BeanDefinitionThe merged BeanDefinitionobject contains Beanall the information needed to create Springthe complete instance.BeanDefinitionBean

  1. Merger process

  SpringWhen an instance needs to be created Bean, the corresponding BeanDefinitionobject will be obtained first. If this BeanDefinitionis a child BeanDefinition, Springits parent will be found BeanDefinition, and then the configuration information of the two will be merged to form a complete one BeanDefinition.

  This process is carried out in the method. If you are interested, you can set a breakpoint in this method and take a look at the specific merging process DefaultListableBeanFactory.getMergedBeanDefinition

  1. Flowchart of the merge process

Insert image description here

We can Beanuse this feature in a parent-child manner. Here is an XMLexample of configuration:

<bean id="parentBean" class="com.example.ParentClass" abstract="true">
    <property name="commonProperty" value="commonValue" />
</bean>

<bean id="childBean" parent="parentBean">
    <property name="specificProperty" value="specificValue" />
</bean>

  In this example, we define two bean, one is parentBeanand the other is childBean. parentBeanYes abstract, it means that it will not be instantiated and will only be used as a template. childBeanThe parentattribute points to parentBean, indicating that it inherits parentBeanthe configuration of .

  parentBeanThere is an attribute commonPropertywith a value of commonValue. childBeanThere is an attribute specificPropertywith a value of specificValue. When Springparsing this configuration file and generating it BeanDefinition, childBeanwill BeanDefinitioncontain two attributes: commonPropertyand specificProperty. This is BeanDefinitionthe merging process of .

  In Javaconfiguration, we cannot directly simulate XMLthe configuration BeanDefinitionmerging process , because this is Spring XMLa feature of configuration. Configuration classes usually use Javacode inheritance or combination to reuse beandefinitions, and do not involve merging at the configuration metadata level BeanDefinition. XMLThe merge feature in the configuration BeanDefinitionallows us to define a parent Beanand then define some children Bean, which Beancan inherit Beansome properties of the parent.

  This feature Javahas no direct replacement in configuration, since Javaconfiguration usually relies more on logic during instantiation than metadata (i.e. BeanDefinition). In Javaconfiguration, we can use ordinary Javafeatures such as inheritance and composition to achieve similar results, but this is not true BeanDefinitionmerging. Therefore, when we XMLconvert from configuration to Javaconfiguration, we often need to manually copy the shared properties into Beanthe definition of each.

4.1 Debugging and verifying the merging of BeanDefinitions

The entire code is as follows:

First, create XMLthe configuration file 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="parentBean" class="com.example.demo.bean.ParentClass" abstract="true">
        <property name="commonProperty" value="commonValue" />
    </bean>

    <bean id="childBean" parent="parentBean" class="com.example.demo.bean.ChildClass">
        <property name="specificProperty" value="specificValue" />
    </bean>
</beans>

Then, create ParentClassand ChildClassas follows:

package com.example.demo.bean;

public abstract class ParentClass {
    
    

    private String commonProperty;

    public String getCommonProperty() {
    
    
        return commonProperty;
    }

    public void setCommonProperty(String commonProperty) {
    
    
        this.commonProperty = commonProperty;
    }
}
package com.example.demo.bean;

public class ChildClass extends ParentClass {
    
    

    private String specificProperty;

    public String getSpecificProperty() {
    
    
        return specificProperty;
    }

    public void setSpecificProperty(String specificProperty) {
    
    
        this.specificProperty = specificProperty;
    }
}

The main program is as follows:

package com.example.demo;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DemoApplication {
    
    

    public static void main(String[] args) {
    
    
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        DefaultListableBeanFactory factory = (DefaultListableBeanFactory) context.getBeanFactory();

        // 获取childBean的原始BeanDefinition
        BeanDefinition childBeanDefinition = factory.getBeanDefinition("childBean");
        System.out.println("Child bean definition before merge: " + childBeanDefinition);

        // 获取合并后的BeanDefinition
        BeanDefinition mergedBeanDefinition = factory.getMergedBeanDefinition("childBean");
        System.out.println("Merged bean definition: " + mergedBeanDefinition);
    }
}

  In this example, we first load applicationContext.xmlthe configuration file and then get childBeanthe original BeanDefinition. Then, we call getMergedBeanDefinitionthe method to obtain the merged data BeanDefinition. We can set breakpoints during this process to view the details of the merge process.

operation result:

Insert image description here

  You can see from the running results that the Generic beansum is printed Root bean, which represents GenericBeanDefinitionthe sum RootBeanDefinition.

Why are there two different BeanDefinition types (GenericBeanDefinition and RootBeanDefinition) ?

GenericBeanDefinition:

  • This is a general BeanDefinitionimplementation class that can be configured for any type bean.
  • It is typically used for reading XML, annotating, or other forms of configuration.
  • BeanDefinitionIt is relatively simple and lightweight compared to other specific ones .
  • When a usage <bean>element XMLis defined in bean, beanan instance is usually created for it GenericBeanDefinition.

RootBeanDefinition:

  • This is a complete beandefinition, including beanall configuration information, such as constructor parameters, property values, method overrides, etc.
  • It is often used to merge parent and child beandefinitions. That is, when one beandefinition inherits another beandefinition, RootBeanDefinitionit is responsible for holding the final configuration after the merger.
  • In GenericBeanDefinitionaddition, it contains many beaninternal details related to instantiation, dependency resolution and initialization.
  • In Springthe internal workflow of , although there can be various implementations at the beginning BeanDefinition, they are usually transformed into the post-processing stage of the container RootBeanDefinition, because at this stage a complete and fixed beandefinition is required for beanits creation.

Debugging Point 1 : BeanFactoryWe got the sub bean's original from it BeanDefinition. This BeanDefinitiononly represents the metadata configured XMLfor the child . It is not merged with the parent, and only the attributes can be seen. beanBeanspecificProperty

Insert image description here

Debugging point 2 : getMergedBeanDefinitionAfter using, the type of the printed on the console BeanDefinitionchanges to RootBeanDefinition, at this time, we get the merged child BeanFactoryfrom Beanit BeanDefinition. Since the child Bean's BeanDefinitionand the parent Bean's BeanDefinitionhave been merged, you can see a complete set of attributes. Here propertyValuesyou see two attribute key-value pairs: commonPropertyand specificProperty, which indicates that the child Beaninherits Beanthe attribute values ​​​​of the parent.

Insert image description here

Note that the purpose of this example is to show BeanDefinitionthe merging process of , so we operate directly BeanFactory. In actual application development, we generally do not operate directly BeanFactory.

4.2 The purpose of BeanDefinition merging

  1. Provide complete BeanDefinition information : In configuration, we often use parent-child BeanDefinition(such as through <bean>tag parentattributes). The child BeanDefinitionmay only define the properties that need to be changed or added bean, while the parent BeanDefinitionprovides shared default definitions. In this case, the merge operation will BeanDefinitionmerge the parent and child information into a complete one BeanDefinitionfor subsequent beancreation.

  2. Optimize performance : The results of the merge operation are usually cached, so the next time you get the same result bean, you can get the merged result directly from the cache BeanDefinition, avoiding repeated merge operations, thereby improving performance.

  3. Resolve cyclic dependencies : When processing beancyclic dependencies between processes, it is necessary to throw out what has been processed (such as instantiation and property filling) as early as possible bean. At this time, a complete BeanDefinitionmessage is needed. Therefore, BeanDefinitionmerging also plays an important role in solving circular dependency problems.

In short, BeanDefinitionthe purpose of merging is to obtain a complete and accurate one BeanDefinitionfor Spring IoCsubsequent container beancreation and dependency resolution.

4.3 Illustration of the relationship between BeanDefinition merging and Spring initialization

Insert image description here

  1. Resource positioning

At this stage, Springthe location of the resources that need to be loaded will be determined based on the user's configuration. The resources may come from a variety of configuration methods, such as XMLannotations Javaor Javaconfigurations.

  1. Read configuration

SpringBeanRead definition information from a certain configuration source

  • For XMLconfiguration, the parser processes each <bean>element. At this time, in particular there are Beandefinitions of parent-child relationships, which are parsed to the original BeanDefinition, but not merged.

  • For annotations and Javaconfigurations, BeanDefinitionthey are parsed into independent definitions and usually do not involve parent-child relationships.

  1. RegisterBeanDefinition

SpringAll parsed results will be BeanDefinitionregistered in BeanDefinitionRegistry.

  1. ProcessingBeanDefinition

At this stage, preprocessing Springis performed BeanDefinition.

  • If there is a parent-child relationship between XMLthe ones read from the configuration Bean, they will be merged. The merged BeanDefinitionensures that the child Beaninherits Beanall the properties of the parent and can override them.
  • For definitions based on annotations or Javaconfigurations Bean, this merge operation usually does not occur because there is no clear parent-child relationship.
  1. Bean instantiation and property filling
  • This stage marks Springthe beginning of the life cycle.

  • All BeanDefinition, whether raw or merged, are converted into actual Beaninstances at this stage.

  • SpringThe container will be responsible for managing Beanthe complete life cycle of these, including but not limited to dependency injection and property setting.

  1. Bean initialization
  • Includes Beaninitialization methods called, such as methods that implement InitializingBeanan interface afterPropertiesSetor init-methodcustom initialization methods specified through attributes.

  • At this stage, Beanit is completely ready for use by the application.

  1. Registered Bean destruction method
  • SpringBeanDestruction methods that are tracked and registered .

  • This ensures that when Springthe container is shut down, it correctly calls every Beandestruction method, such as those that implement DisposableBeanan interface destroyor destroy-methoda custom method specified via a property.

BeanDefinitionThe merging you can see here happens at Beanan early stage before actual instantiation, which ensures that when it comes time Springto create an Beaninstance, it has a complete, merged definition to rely on.

5. Source code analysis of BeanDefinition merge

5.1 BeanDefinition merge process sequence diagram

Insert image description here

5.2 BeanDefinition merging process source code interpretation

Let’s talk about BeanDefinitionthe merger that was not mentioned in the previous article. Let’s Spring 5.3.7analyze the source code. We will show the picture first and analyze it later.

Insert image description here

Let’s analyze AbstractBeanFactoryseveral methods of the class.

// 获取本地合并后的 BeanDefinition
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
    
    
    // 从缓存中获取合并后的 BeanDefinition
    RootBeanDefinition mbd = (RootBeanDefinition)this.mergedBeanDefinitions.get(beanName);
    // 如果缓存中的 BeanDefinition 不为空并且未过时,则直接返回
    // 否则,对 BeanDefinition 进行合并
    return mbd != null && !mbd.stale ? mbd : this.getMergedBeanDefinition(beanName, this.getBeanDefinition(beanName));
}

// 获取合并后的 BeanDefinition
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd) throws BeanDefinitionStoreException {
    
    
    // 直接调用 getMergedBeanDefinition 方法,将 containingBd 设为 null
    return this.getMergedBeanDefinition(beanName, bd, (BeanDefinition)null);
}

// 获取合并后的 BeanDefinition
protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd) throws BeanDefinitionStoreException {
    
    
    synchronized(this.mergedBeanDefinitions) {
    
    
        RootBeanDefinition mbd = null;
        RootBeanDefinition previous = null;

        // 如果没有包含的 BeanDefinition,那么从缓存中获取合并后的 BeanDefinition
        if (containingBd == null) {
    
    
            mbd = (RootBeanDefinition)this.mergedBeanDefinitions.get(beanName);
        }

        // 如果缓存中的 BeanDefinition 为空或者过时,那么创建新的 BeanDefinition 进行合并
        if (mbd == null || mbd.stale) {
    
    
            previous = mbd;

            // 如果 bd 没有父名称,即没有继承其他的 bean
            // 那么就直接 clone 这个 bd,生成一个 RootBeanDefinition
            if (bd.getParentName() == null) {
    
    
                if (bd instanceof RootBeanDefinition) {
    
    
                    mbd = ((RootBeanDefinition)bd).cloneBeanDefinition();
                } else {
    
    
                    mbd = new RootBeanDefinition(bd);
                }
            } else {
    
     // 如果 bd 是一个子 BeanDefinition(即有父 BeanDefinition)
                // 首先获取父 BeanDefinition
                BeanDefinition pbd;
                try {
    
    
                    String parentBeanName = this.transformedBeanName(bd.getParentName());
                    if (!beanName.equals(parentBeanName)) {
    
    
                        pbd = this.getMergedBeanDefinition(parentBeanName);
                    } else {
    
    
                        BeanFactory parent = this.getParentBeanFactory();
                        if (!(parent instanceof ConfigurableBeanFactory)) {
    
    
                            throw new NoSuchBeanDefinitionException(parentBeanName, "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName + "': cannot be resolved without a ConfigurableBeanFactory parent");
                        }

                        pbd = ((ConfigurableBeanFactory)parent).getMergedBeanDefinition(parentBeanName);
                    }
                } catch (NoSuchBeanDefinitionException var11) {
    
    
                    throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName, "Could not resolve parent bean definition '" + bd.getParentName() + "'", var11);
                }

                // 创建一个新的 RootBeanDefinition 并覆盖 bd 中的属性
                // 这就完成了父子 BeanDefinition 的合并
                mbd = new RootBeanDefinition(pbd);
                mbd.overrideFrom(bd);
            }

            // 如果合并后的 BeanDefinition 没有指定作用域
            // 则默认设置为 singleton
            if (!StringUtils.hasLength(mbd.getScope())) {
    
    
                mbd.setScope("singleton");
            }

            // 如果定义了父 BeanDefinition 且父 BeanDefinition 的作用域不是 singleton 但子 BeanDefinition 的作用域是 singleton
            // 则将子 BeanDefinition 的作用域设置为父 BeanDefinition 的作用域
            if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
    
    
                mbd.setScope(containingBd.getScope());
            }

            // 如果不存在包含的 BeanDefinition 并且需要缓存 BeanMetadata
            // 那么就将这个新创建并合并的 BeanDefinition 放入 mergedBeanDefinitions 缓存中
            if (containingBd == null && this.isCacheBeanMetadata()) {
    
    
                this.mergedBeanDefinitions.put(beanName, mbd);
            }
        }

        // 如果之前存在过期的 BeanDefinition
        // 那么从过期的 BeanDefinition 中拷贝相关的缓存到新的 BeanDefinition 中
        if (previous != null) {
    
    
            this.copyRelevantMergedBeanDefinitionCaches(previous, mbd);
        }

        // 返回合并后的 BeanDefinition
        return mbd;
    }
}

  This code mainly completes BeanDefinitionthe merging work. When a BeanDefinitionhas a parent BeanDefinition, the definition of Springthe child will be merged with the definition of the parent to generate a new and complete one . This process is the merging of . The merged will be cached for next use. If a has no parent , a copy will be used directly as the merged one . During the entire life cycle of , the merging of may occur multiple times, and each time when is obtained, the merging of will be performed first .BeanDefinitionBeanDefinitionBeanDefinitionBeanDefinitionBeanDefinitionBeanDefinitionBeanDefinitioncloneBeanDefinitionSpringBeanDefinitionBeanBeanDefinition


Welcome to the one-click triple connection~

If you have any questions, please leave a message, let's discuss and learn together

----------------------Talk is cheap, show me the code----- ------------------

Guess you like

Origin blog.csdn.net/qq_34115899/article/details/132035681