Article directory
1. What is BeanDefinitionRegistry?
BeanDefinitionRegistry
Is a very important interface that exists in the package Spring
of org.springframework.beans.factory.support
. It is the core component of Spring
registration and management in BeanDefinition
.
Let’s review what we said in the previous article BeanDefinition
. In Spring
, a Bean
is an Spring
object managed by , and a BeanDefinition
is a Bean
configuration description, which describes Bean
the data of a . It contains Bean
information such as the class name, whether it is an abstract class, constructor and attribute values. These metadata will guide Spring
how to create and initialize Bean
.
Let's take a look at BeanDefinitionRegistry
the role of . BeanDefinitionRegistry
Its main responsibility is to register and manage these BeanDefinition
. We can think of it as a BeanDefinition
registry 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
, BeanDefinitionRegistry
it is usually BeanFactory
implemented by , especially DefaultListableBeanFactory
and GenericApplicationContext
, which all implement this interface.
2. Why do you need BeanDefinitionRegistry?
If BeanDefinitionRegistry
it doesn't exist, Spring
how will some of its core functionality be affected?
-
Uniformity of resource parsing : configuration information
BeanDefinition
stored as a unified data structure .Bean
If notBeanDefinitionRegistry
, each configuration method (XML
, annotation,Java
configuration) 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. -
Dependency lookup and injection :
BeanDefinitionRegistry
Provides a central location that acts asBean
a central store of definitions and can quickly look upBean
definitions. Without it, when you need to inject aBean
dependency,Spring
you not only need to traverse all configuration sources to find the corresponding oneBean
, but you may also encounterBean
definition inconsistencies, which will significantly reduce performance and accuracy.
Bean
Examples 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, sampleBean
there are definitions in both configuration files, but they reference different classes. If BeanDefinitionRegistry
these definitions are not handled centrally, you may encounter confusion when Spring
trying to initialize , such as trying to create an instance and inject it , and a question arises: which definition should be selected? Or ?sampleBean
Spring
ServiceA
sampleBean
Spring
sampleBean
com.example.SampleBean1
com.example.SampleBean2
This is the so-called " Bean
inconsistency of definitions" problem. If Spring
you 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 Bean
version or type is not what the application expects.
By using it BeanDefinitionRegistry
, Spring
you can detect such problems when your application starts and Bean
provide clear error messages when definitions conflict or are inconsistent, rather than encountering undefined behavior or errors at runtime.
-
Lazy initialization and scope management :
BeanDefinitionRegistry
storedBean
scope and other metadata. Without thisBeanDefinitionRegistry
,Spring
when performingBean
lazy loading or creating from scopeBean
, the original configuration resources need to be re-parsed, which increases processing time and can lead to potential configuration errors. -
Configuration verification : After everything
BeanDefinition
is registeredBeanDefinitionRegistry
,Spring
the configuration can be verified, such as checking circular dependencies, ensuringBean
the integrity of definitions, etc. If notBeanDefinitionRegistry
, it needs to be checkedSpring
at every initialization, which not only results in performance degradation, but also may miss some obscure configuration issues.Bean
-
Life cycle management : There is no
BeanDefinitionRegistry
storage of life cycle callbacks, initialization methods and other information.Spring
When managingBean
the 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
, Spring
centralized Bean
management will be lost, resulting in reduced efficiency, scattered error handling, and increased complexity of life cycle management. BeanDefinitionRegistry
Ensuring Spring
efficient, consistent and stable operation.
3. Use of BeanDefinitionRegistry
3.1 BeanDefinitionRegistry simple example
In this example we will create a simple one Bean
that is registered to DefaultListableBeanFactory
(which implements BeanDefinitionRegistry
the interface) and then get it from the factory and use this Bean
.
The entire code is as follows:
First, we need a Bean
class, which is a simple POJO
class:
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 DefaultListableBeanFactory
and RootBeanDefinition
to 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"
, Bean
which Bean
is MyBean
an instance of the class. Then we get this BeanFactory
from Bean
and call its doSomething
method, print "Hello, world!"
.
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 BeanDefinition
did not explain BeanDefinition
the merging in the previous article, so we will add additional explanations here.
- BeanDefinition
In Spring
, BeanDefinition
is an interface, which defines Bean
the configuration information of , such as Bean
the class name, whether it is a singleton, dependencies, etc. In Spring
, each Bean
corresponds to an BeanDefinition
object.
- The meaning of merger
In Spring
, there is a special one BeanDefinition
called a child BeanDefinition
, which is the one we specify XML
through the attribute in the configuration file . parent
This kind of child BeanDefinition
can inherit BeanDefinition
the configuration information of the parent.
The merging process is to merge BeanDefinition
the configuration information of the child with the configuration information of the parent to form a complete configuration information. BeanDefinition
The merged BeanDefinition
object contains Bean
all the information needed to create Spring
the complete instance.BeanDefinition
Bean
- Merger process
Spring
When an instance needs to be created Bean
, the corresponding BeanDefinition
object will be obtained first. If this BeanDefinition
is a child BeanDefinition
, Spring
its 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
- Flowchart of the merge process
We can Bean
use this feature in a parent-child manner. Here is an XML
example 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 parentBean
and the other is childBean
. parentBean
Yes abstract
, it means that it will not be instantiated and will only be used as a template. childBean
The parent
attribute points to parentBean
, indicating that it inherits parentBean
the configuration of .
parentBean
There is an attribute commonProperty
with a value of commonValue
. childBean
There is an attribute specificProperty
with a value of specificValue
. When Spring
parsing this configuration file and generating it BeanDefinition
, childBean
will BeanDefinition
contain two attributes: commonProperty
and specificProperty
. This is BeanDefinition
the merging process of .
In Java
configuration, we cannot directly simulate XML
the configuration BeanDefinition
merging process , because this is Spring XML
a feature of configuration. Configuration classes usually use Java
code inheritance or combination to reuse bean
definitions, and do not involve merging at the configuration metadata level BeanDefinition
. XML
The merge feature in the configuration BeanDefinition
allows us to define a parent Bean
and then define some children Bean
, which Bean
can inherit Bean
some properties of the parent.
This feature Java
has no direct replacement in configuration, since Java
configuration usually relies more on logic during instantiation than metadata (i.e. BeanDefinition
). In Java
configuration, we can use ordinary Java
features such as inheritance and composition to achieve similar results, but this is not true BeanDefinition
merging. Therefore, when we XML
convert from configuration to Java
configuration, we often need to manually copy the shared properties into Bean
the definition of each.
4.1 Debugging and verifying the merging of BeanDefinitions
The entire code is as follows:
First, create XML
the 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 ParentClass
and ChildClass
as 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.xml
the configuration file and then get childBean
the original BeanDefinition
. Then, we call getMergedBeanDefinition
the method to obtain the merged data BeanDefinition
. We can set breakpoints during this process to view the details of the merge process.
operation result:
You can see from the running results that the Generic bean
sum is printed Root bean
, which represents GenericBeanDefinition
the sum RootBeanDefinition
.
Why are there two different BeanDefinition types (GenericBeanDefinition and RootBeanDefinition) ?
GenericBeanDefinition:
- This is a general
BeanDefinition
implementation class that can be configured for any typebean
. - It is typically used for reading
XML
, annotating, or other forms of configuration. BeanDefinition
It is relatively simple and lightweight compared to other specific ones .- When a usage
<bean>
elementXML
is defined inbean
,bean
an instance is usually created for itGenericBeanDefinition
.
RootBeanDefinition:
- This is a complete
bean
definition, includingbean
all configuration information, such as constructor parameters, property values, method overrides, etc. - It is often used to merge parent and child
bean
definitions. That is, when onebean
definition inherits anotherbean
definition,RootBeanDefinition
it is responsible for holding the final configuration after the merger. - In
GenericBeanDefinition
addition, it contains manybean
internal details related to instantiation, dependency resolution and initialization. - In
Spring
the internal workflow of , although there can be various implementations at the beginningBeanDefinition
, they are usually transformed into the post-processing stage of the containerRootBeanDefinition
, because at this stage a complete and fixedbean
definition is required forbean
its creation.
Debugging Point 1 : BeanFactory
We got the sub bean
's original from it BeanDefinition
. This BeanDefinition
only represents the metadata configured XML
for the child . It is not merged with the parent, and only the attributes can be seen. bean
Bean
specificProperty
Debugging point 2 : getMergedBeanDefinition
After using, the type of the printed on the console BeanDefinition
changes to RootBeanDefinition
, at this time, we get the merged child BeanFactory
from Bean
it BeanDefinition
. Since the child Bean
's BeanDefinition
and the parent Bean
's BeanDefinition
have been merged, you can see a complete set of attributes. Here propertyValues
you see two attribute key-value pairs: commonProperty
and specificProperty
, which indicates that the child Bean
inherits Bean
the attribute values of the parent.
Note that the purpose of this example is to show BeanDefinition
the 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
-
Provide complete BeanDefinition information : In configuration, we often use parent-child
BeanDefinition
(such as through<bean>
tagparent
attributes). The childBeanDefinition
may only define the properties that need to be changed or addedbean
, while the parentBeanDefinition
provides shared default definitions. In this case, the merge operation willBeanDefinition
merge the parent and child information into a complete oneBeanDefinition
for subsequentbean
creation. -
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 cacheBeanDefinition
, avoiding repeated merge operations, thereby improving performance. -
Resolve cyclic dependencies : When processing
bean
cyclic dependencies between processes, it is necessary to throw out what has been processed (such as instantiation and property filling) as early as possiblebean
. At this time, a completeBeanDefinition
message is needed. Therefore,BeanDefinition
merging also plays an important role in solving circular dependency problems.
In short, BeanDefinition
the purpose of merging is to obtain a complete and accurate one BeanDefinition
for Spring IoC
subsequent container bean
creation and dependency resolution.
4.3 Illustration of the relationship between BeanDefinition merging and Spring initialization
- Resource positioning
At this stage, Spring
the 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 XML
annotations Java
or Java
configurations.
- Read configuration
Spring
Bean
Read definition information from a certain configuration source
-
For
XML
configuration, the parser processes each<bean>
element. At this time, in particular there areBean
definitions of parent-child relationships, which are parsed to the originalBeanDefinition
, but not merged. -
For annotations and
Java
configurations,BeanDefinition
they are parsed into independent definitions and usually do not involve parent-child relationships.
- RegisterBeanDefinition
Spring
All parsed results will be BeanDefinition
registered in BeanDefinitionRegistry
.
- ProcessingBeanDefinition
At this stage, preprocessing Spring
is performed BeanDefinition
.
- If there is a parent-child relationship between
XML
the ones read from the configurationBean
, they will be merged. The mergedBeanDefinition
ensures that the childBean
inheritsBean
all the properties of the parent and can override them. - For definitions based on annotations or
Java
configurationsBean
, this merge operation usually does not occur because there is no clear parent-child relationship.
- Bean instantiation and property filling
-
This stage marks
Spring
the beginning of the life cycle. -
All
BeanDefinition
, whether raw or merged, are converted into actualBean
instances at this stage. -
Spring
The container will be responsible for managingBean
the complete life cycle of these, including but not limited to dependency injection and property setting.
- Bean initialization
-
Includes
Bean
initialization methods called, such as methods that implementInitializingBean
an interfaceafterPropertiesSet
orinit-method
custom initialization methods specified through attributes. -
At this stage,
Bean
it is completely ready for use by the application.
- Registered Bean destruction method
-
Spring
Bean
Destruction methods that are tracked and registered . -
This ensures that when
Spring
the container is shut down, it correctly calls everyBean
destruction method, such as those that implementDisposableBean
an interfacedestroy
ordestroy-method
a custom method specified via a property.
BeanDefinition
The merging you can see here happens at Bean
an early stage before actual instantiation, which ensures that when it comes time Spring
to create an Bean
instance, it has a complete, merged definition to rely on.
5. Source code analysis of BeanDefinition merge
5.1 BeanDefinition merge process sequence diagram
5.2 BeanDefinition merging process source code interpretation
Let’s talk about BeanDefinition
the merger that was not mentioned in the previous article. Let’s Spring 5.3.7
analyze the source code. We will show the picture first and analyze it later.
Let’s analyze AbstractBeanFactory
several 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 BeanDefinition
the merging work. When a BeanDefinition
has a parent BeanDefinition
, the definition of Spring
the 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 .BeanDefinition
BeanDefinition
BeanDefinition
BeanDefinition
BeanDefinition
BeanDefinition
BeanDefinition
clone
BeanDefinition
Spring
BeanDefinition
Bean
BeanDefinition
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----- ------------------