一、什么是BeanDefinitionRegistry?
想要弄清楚什么是BeanDefinitionRegistry,首先得知道什么是BeanDefinition。不清楚的小伙伴可以先看此篇文章:什么是BeanDefinition
简单说明一下:在Spring中,一个Bean就是被Spring管理的对象,而一个BeanDefinition则是一个Bean的配置描述,它描述了Bean的元数据,包括类名、是否为抽象类、构造函数和属性值等相关信息,这些元数据是告诉Spring如何创建和初始化相对应的Bean。
那么什么是BeanDefinitionRegistry呢?
可以这么理解:一个存放BeanDefinition的注册表,用于存储和管理所有的BeanDefinition。
BeanDefinitionRegistry源码如下:
public interface BeanDefinitionRegistry extends AliasRegistry {
// 注册一个 BeanDefinition
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException;
// 根据 beanName 删除一个 BeanDefinition
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
// 根据 beanName 获得一个 BeanDefinition
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
// 根据 beanName 判断是否包含一个 BeanDefinition
boolean containsBeanDefinition(String beanName);
// 返回所有存在 BeanDefinition 的名字
String[] getBeanDefinitionNames();
// 返回 BeanDefinition 的个数
int getBeanDefinitionCount();
// 根据 beanName 判断该 Bean 是否被注册
boolean isBeanNameInUse(StringbeanName);
}
二、BeanDefinition有什么作用?
资源解析的统一性
BeanDefinition作为一个统一的数据结构存储了Bean相关配置信息,但是Spring有不同的配置方式,包括XML、注解以及Java配置。如果不使用BeanDefinitionRegistry,我们需要各自专门的数据结构去存储和管理,会导致资源解析复杂度增加,甚至可能存在不同解析机制之间产生不一致性。
依赖查找和注入
BeanDefinitionRegistry担任了BeanDefiniiton中央注册器的角色,可以通过它快速查找BeanDefinition,不需要遍历所有不同的配置源(XML 注解 Java配置)来查找对应的BeanDefinition。
BeanDefinition不一致简单案例:
<!-- in config1.xml -->
<bean id="sampleBean" class="com.example.SampleBean1" />
<!-- in config2.xml -->
<bean id="sampleBean" class="com.example.SampleBean2" />
这里,sampleBean 在两个配置文件中都有定义,但它们引用了不同的类。如果没有BeanDefinitionRegistry集中处理这些定义,那么Spring在尝试初始化sampleBean时可能会遭遇混淆,比如Spring尝试创建ServiceA的实例并为它注入sampleBean时,就会出现一个问题:Spring应该选择哪一个sampleBean的定义?com.example.SampleBean1还是com.example.SampleBean2?
通过使用BeanDefinitionRegistry,Spring可以在应用程序启动时检测这类问题,并在Bean定义冲突或不一致时提供明确的错误消息,而不是在运行时遭遇不确定的行为或错误。
延迟初始化和作用域管理
当需要根据作用域创建Bean或进行延迟加载时,Spring容器可以直接从注册表中获取相应的BeanDefinition对象,而无需重新解析配置资源。如果没有BeanDefinitionRegistry进行统一管理的话,Spring则需要重新去解析原始的配置资源。
配置验证
当所有BeanDefinition注册到BeanDefinitionRegistry后,Spring可以进行配置校验,例如检查循环依赖、确保Bean定义的完整性等。如果没有BeanDefinitionRegistry,Spring需要在每次Bean初始化时进行检查,这不仅导致性能下降,还可能漏掉某些隐晦的配置问题。
生命周期管理
没有BeanDefinitionRegistry存储生命周期回调、初始化方法等信息,Spring在管理Bean的生命周期时,需要从原始的配置源获取这些信息。这不仅增加了管理的复杂度,还会使生命周期回调会变得复杂和笨重。
总结:使用BeanDefinitionRegistry的目的就是为了集中式、统一化去管理BeanDefinition。Spring容器可以在启动时一次性解析配置资源(XML 注解 Java配置),可以通过BeanDefinitionRegistry快速获取到所需的、特定的(延迟加载的Bean或者不同作用域的Bean)BeanDefinition对象,而无需重新解析配置资源,从而提高性能且保证BeanDefinition的正确配置和行为。
三、BeanDefinitionRegistry使用简单案例
我们将创建一个简单的 Bean,注册到 DefaultListableBeanFactory(它实现了 BeanDefinitionRegistry 接口),然后从工厂中获取并使用这个Bean。
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);
}
}
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!
}
}