【Spring源码三千问】BeanDefinition注册、Bean注册、Dependency注册有什么区别?

前言

通常我们都在讲:将 bean 注册到 spring 容器(BeanFactory 容器)。
其实在 Spring 中,注册这个动作在好几个地方都有出现过,比如:

  1. registerBeanDefinition
  2. registerResolvableDependency
  3. registerBean

它们有什么区别呢?平时我们在写通用代码或者框架时,又要怎样是使用呢?

版本约定

Spring 5.3.9 (通过 SpringBoot 2.5.3 间接引入的依赖)

正文

在 Spring 中,注册这个动作在很多地方都有出现,主要集中在 BeanFactory 和 ApplicationContext 中:
register

其中,跟 Bean 相关的主要有:

  1. registerBeanDefinition(BeanDefinition 的注册)
  2. registerResolvableDependency(Dependency 注册)
  3. registerBean(通过 class 注册 Bean)
  4. registerSingleton(向容器中注册一个单例 bean)

registerBeanDefinition(BeanDefinition 的注册)

前面分析过 BeanDefinition扫描注册过程
BeanDefinition 的扫描注册过程分了三个阶段:

  1. BeanDefinition 的扫描
    即从 class 类定义中扫描出 BeanDefinition

  2. BeanDefinition 基本属性的填充
    基本属性包括:@Lazy、@Primary、@DependsOn、@Role、@Description 等。

  3. BeanDefinition 的注册
    BeanDefinition 最终会注册到 BeanDefinitionRegistry 中

所以,BeanDefinition 的注册主要是将 bean 的定义从 class 类定义中扫描出来,并注册到 BeanDefinitionRegistry 中

常用 API

BeanDefinitionBuilder

BeanDefinitionBuilder 可以很方便的完成 BeanDefinition 的创建

BeanDefinition bd = BeanDefinitionBuilder.rootBeanDefinition(FooService.class)
        .setScope(BeanDefinition.SCOPE_SINGLETON)
        .addPropertyValue("id", "123")
        .addPropertyValue("name", "zhangsan")
        .getBeanDefinition();
GenericApplicationContext#registerBeanDefinition()

这个接口可以通过编程的方式注册 BeanDefinition

StaticApplicationContext context = new StaticApplicationContext();
AbstractBeanDefinition bd = BeanDefinitionBuilder.rootBeanDefinition(FooService.class)
        .setScope(BeanDefinition.SCOPE_SINGLETON)
        .addPropertyValue("id", "123")
        .addPropertyValue("name", "zhangsan")
        .getBeanDefinition();
// 注册 BeanDefinition
context.registerBeanDefinition("fooService", bd);

FooService bean = context.getBean(FooService.class);

registerResolvableDependency(Dependency 注册)

相应的API为: ConfigurableListableBeanFactory#registerResolvableDependency()
功能描述:
向容器中注册一个可解析的依赖。
将对象注册成一个特殊的依赖类型。这种方式注册的对象可以通过 @Autowired 进行注入,但是它并没有在 BeanFactory 中定义为一个 bean。

public void registerResolvableDependency(Class<?> dependencyType, Object autowiredValue) {
     Assert.notNull(dependencyType, "Dependency type must not be null");
     if (autowiredValue != null) {
         if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) {
             throw new IllegalArgumentException("Value [" + autowiredValue +
                     "] does not implement specified dependency type [" + dependencyType.getName() + "]");
         }
         // 将指定的类型 和 对象 添加到 resolvableDependencies 中
         this.resolvableDependencies.put(dependencyType, autowiredValue);
     }
 }

注意:
这种方式可以注册类或者接口的可解析的依赖,而不去走注册 BeanDefinition 的流程和 bean 的创建流程。
registerResolvableDependency 注册的可解析依赖不能通过 applicationContext.getBean() 直接拿到,而是需要通过 @Autowired 来进行注入。

@Autowired 或 @Resource 做注入时,都会调用到 DefaultListableBeanFactory#doResolveDependency()
doResolveDependency() 的时候,就会优先从 resolvableDependencies 中去获取依赖进行解析。

@Autowired 或 @Resource 的解析请戳: 【Spring源码三千问】@Resource 与 @Autowired 的区别

源码例子:
@see AbstractApplicationContext#prepareBeanFactory()
@see WebApplicationContextUtils#registerWebApplicationScopes()

BeanFactory、ApplicationContext、ApplicationEventPublisher、ResourceLoader
ServletRequest、ServletResponse、HttpSession、WebRequest 等都是通过 registerResolvableDependency() 注册的可解析依赖

// AbstractApplicationContext#prepareBeanFactory()  
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);

registerBean(通过 class 注册 Bean)

相应的API为: GenericApplicationContext#registerBean()
功能描述:
通过给定的 bean类注册 bean。使用给定的 Supplier 获取 bean 的新实例,还可以选择自定义其 BeanDefinition 元数据。
底层是通过包装成一个 ClassDerivedBeanDefinition 来完成 BeanDefinition 的注册,创建 bean 实例时,通过给定的 Supplier 获取 bean 的新实例。

 public <T> void registerBean(String beanName, Class<T> beanClass,
         Supplier<T> supplier, BeanDefinitionCustomizer... customizers) {
     // 将给定的 class 包装成一个 ClassDerivedBeanDefinition 进行注册
     ClassDerivedBeanDefinition beanDefinition = new ClassDerivedBeanDefinition(beanClass);
     if (supplier != null) {
         beanDefinition.setInstanceSupplier(supplier);
     }
     for (BeanDefinitionCustomizer customizer : customizers) {
         customizer.customize(beanDefinition);
     }

     String nameToUse = (beanName != null ? beanName : beanClass.getName());
     registerBeanDefinition(nameToUse, beanDefinition);
 }

GenericApplicationContext#registerBean() 相当于以 class 的方式注册 BeanDefinition。
相对于直接构造 BeanDefinition 来进行注册的方式,看起来更加简单。

registerSingleton(向容器中注册一个单例 bean)

相应的API为: SingletonBeanRegistry#registerSingleton()
功能描述:
通过指定的 name 将给定的对象作为 singleton bean 注册到容器中。
它假定给定的 bean 是完全初始化好的,所以它不会执行任何初始化回调,比如:InitializingBean#afterPropertiesSet();给定实例也不会收到任何销毁回调(如DisposableBean的销毁方法)。

 public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
     Assert.notNull(beanName, "Bean name must not be null");
     Assert.notNull(singletonObject, "Singleton object must not be null");
     synchronized (this.singletonObjects) {
         Object oldObject = this.singletonObjects.get(beanName);
         if (oldObject != null) {
             throw new IllegalStateException("Could not register object [" + singletonObject +
                     "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
         }
         // 将 singletonObject 添加到一级缓存中
         addSingleton(beanName, singletonObject);
     }
 }

通过 registerSingleton() 注册的 bean,可以通过 @Autowired 进行注入,也可以通过 applicationContext.getBean() 进行获取。
因为注册的这个 bean 是添加到了 BeanFactory 容器中的。

源码例子:

// AbstractApplicationContext#prepareBeanFactory()  
// Register default environment beans.
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
    beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
    beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
    beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
    beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
}

小结

跟 Bean 相关的注册方法主要有:

  1. registerBeanDefinition(BeanDefinition 的注册)
  2. registerResolvableDependency(Dependency 注册)
  3. registerBean(通过 class 注册 Bean --> 其实是注册 ClassDerivedBeanDefinition)
  4. registerSingleton(向容器中注册一个单例 bean)

它们可以分为 3 类:

  • 第一类:注册 BeanDefinition
    registerBeanDefinition、registerBean
    这种方式产生的 bean 会走完整的 BeanDefinition 注册流程 和 bean 的创建流程。

  • 第二类:注册 Dependency
    registerResolvableDependency
    只是注册一个可解析的依赖,不会做为一个 bean 注册到 BeanFactory 容器中

  • 第三类:注册单例 bean
    registerSingleton
    直接将完全初始化好的 bean 对象注册到 BeanFactory 容器中,它不会执行任何初始化回调 和 销毁回调。


SpringIoC源码视频讲解:

课程地址
SpringIoC源码解读由浅入深https://edu.51cto.com/sd/68e86

如果本文对你有所帮助,欢迎点赞收藏!

源码测试工程下载:
老王读Spring IoC源码分析&测试代码下载
老王读Spring AOP源码分析&测试代码下载

公众号后台回复:下载IoC 或者 下载AOP 可以免费下载源码测试工程…

阅读更多文章,请关注公众号: 老王学源码
gzh


系列博文:
【老王读Spring IoC-0】Spring IoC 引入
【老王读Spring IoC-1】IoC 之控制反转引入
【老王读Spring IoC-2】IoC 之 BeanDefinition 扫描注册
【老王读Spring IoC-3】Spring bean 的创建过程
【老王读Spring IoC-4】IoC 之依赖注入原理
【老王读Spring IoC-5】Spring IoC 小结——控制反转、依赖注入

相关阅读:
【Spring源码三千问】@Resource 与 @Autowired 的区别
【Spring源码三千问】bean name 的生成规则
【Spring源码三千问】BeanDefinition详细分析
【Spring源码三千问】Spring 是怎样解决循环依赖问题的?
【Spring源码三千问】哪些循环依赖问题Spring解决不了?
【Spring源码三千问】@Lazy为什么可以解决特殊的循环依赖问题?
【Spring源码三千问】BeanDefinition注册、Bean注册、Dependency注册有什么区别?
【Spring源码三千问】Bean的Scope有哪些?scope=request是什么原理?
【Spring源码三千问】为什么要用三级缓存来解决循环依赖问题?二级缓存行不行?一级缓存行不行?

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老王学源码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值