掘金 后端 ( ) • 2024-06-30 17:28

theme: fancy highlight: atelier-heath-light


思考,输出,沉淀。用通俗的语言陈述技术,让自己和他人都有所收获。
作者:毅航😜


在上一篇详解Spring中IOC容器的顶层接口中,并对Spring中常见的IOC容器进行了介绍分析。简单来看,BeanFactory作为Spring容器的顶层父类接口,其主要定义了IOC容器对于Bean实例存取的一些公共方法。在此基础上Spring还扩展出了支持Bean枚举的ListableBeanFactory接口、实现容器间继承关系的HierarchicalBeanFactory接口,以及自动装配的基石的AutowireCapableBeanFactory接口。

但如果要实现IOC的特性,仅仅依靠这几个接口是远远不够的,今天我们便继续来扒一扒与单例Bean实例相关的SingletonBeanRegistry接口。

前言

众所周知,BeanFactory作为IOC容器的顶层接口,负责管理Spring容器中的所有Bean的创建、配置和管理,并定义了获取Bean、检查Bean存在性、获取Bean类型等容器的基本操作。而Spring为了更好的管理单利Bean实例,其内部将单例Bean的管理功能抽象到SingletonBeanRegistry接口中,从而使得单例Bean的管理与其他Bean管理功能解耦。

image.png

单例Bean的管理——SingletonBeanRegistry

Spring的中,SingletonBeanRegistry的主要用于管理单例Bean的注册和获取,同时提供将单例Bean实例注册到注册表中的方法。其内部构造如下:


public interface SingletonBeanRegistry {
  
   void registerSingleton(String beanName, Object singletonObject);

   @Nullable
   Object getSingleton(String beanName);

   boolean containsSingleton(String beanName);

   String[] getSingletonNames();

}

其中registerSingleton的作用是将一个单例Bean实例注册到注册表中,通过该方式开发人员可以手动向Spring的容器中添加单例Bean实例,而无需要通过Spring的配置文件或注解来定义这些BeangetSingleton方法则是一种的根据Bean名称获取已经注册的单例Bean实例的方法。

进一步,containsSingleton方法对外提供了检查指定名称的单例Bean是否已经注册的功能,而getSingletonNames则是允许开发者获取容器中所有单例Bean的名称。

不难发现,SingletonBeanRegistry所暴露的一些方法主要完成的工作主要为单例Bean的获取和注册。看到这你可能会觉得SingletonBeanRegistryBeanFactory功能很类似,但其实两者还是存在一定差异性。

具体来看,BeanFactorySpring的核心接口之一,其主要负责管理Spring容器中的所有Bean的创建、配置和管理。 并提供了一种高级的配置机制来管理Bean以支持不同类型的Bean的管理。

SingletonBeanRegistry则是一个专门用于管理单例Bean的接口,主要提供了注册、获取和管理单例Bean的方法。事实上,SingletonBeanRegistry并不负责创建Bean,它的作用是维护已经创建好的单例Bean实例。

总的来看,SingletonBeanRegistry专注于单例Bean的注册和管理,而BeanFactory负责整个Spring容器中所有Bean的创建、配置和管理。 这样的设计使得每个类的职责更加单一。每个接口只承担一种责任。这样划分方式使代码更简洁、更易维护,并且更容易理解和扩展。

与此同时,将单例Bean的管理功能抽象到SingletonBeanRegistry接口中,使得单例Bean的管理与其他Bean管理功能解耦。这样,如果需要在不同的上下文中管理单例Bean,可以直接使用SingletonBeanRegistry接口,而不必依赖于整个BeanFactory接口。

例如,ConfigurableBeanFactory接口继承了SingletonBeanRegistry接口与BeanFactory。这意味着ConfigurableBeanFactory的实现类既具有完整的Bean管理功能,又具备单例Bean的管理功能。 这样的设计使得Spring框架将单例Bean管理功能与其他Bean管理功能解耦,进而实现了模块间高内聚低耦合的设计。

进一步,你可能会想既然Spring内部有对于单例Bean的管理接口,对于多例Bean是如何进行管理的呢? 要想解答这一问题,我们首先要明白Spring内部对于Bean作用域的划分。

Spring中的Bean是存在单例多例之分。具体来看,所谓的单例即在整个Bean的生命周期内,该Bean实例是唯一的。反之,如果Bean每次请求时都会创建一个新的实例,且Bean实例不会在容器中进行缓存,则该Bean的作用范围即为多例

如果一个Bean的作用范围为多例, 那么该多例Bean本身并不需要在Spring容器中全局共享和缓存,因此多例Bean的生命周期管理相对简单,其大致可以分为如下三个阶段:

  1. 创建并初始化:当每次有请求到达Spring时,如果有对该Bean的需要,则每次创建一个新的Bean实例,然后再执行依赖注入和相关初始化方法。
  2. 使用:客户端使用Bean实例。
  3. 销毁:使用完毕后,实例会被垃圾回收机制自动回收。

(注:Spring容器不负责管理多例Bean的销毁(除非显式调用销毁方法)。)

为了更深入理解多例Bean,我们通过一个简单的代码来展示对于多例Bean的定义和获取过程。


import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

public class PrototypeBeanExample {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        PrototypeBean bean1 = context.getBean(PrototypeBean.class);
        PrototypeBean bean2 = context.getBean(PrototypeBean.class);

        System.out.println(bean1 == bean2); // 输出: false

        context.close();
    }
}

@Configuration
class AppConfig {
    @Bean
    @Scope("prototype")
    public MyPrototypeBean PrototypeBean() {
        return new PrototypeBean();
    }
}

class PrototypeBean {
   
}

在上述代码中,我们定义了PrototypeBean这一对象,同时将其注入到Spring容器中,并指定Scope的类型为prototype。然后,我们通过context.getBean来分别获取容器中的PrototypeBean对象,并比较两对象是否相等。

事实上,每次调用context.getBean(MyPrototypeBean.class)都会创建一个新的PrototypeBean实例,因此bean1bean2是不同的实例。

总结

本文深入探讨了Spring框架中SingletonBeanRegistry接口的作用和管理机制。其作为专门用于管理单例Bean的接口SingletonBeanRegistry提供了注册、获取和检查单例Bean的方法,使单例Bean的管理与其他Bean的管理功能解耦。进一步,SingletonBeanRegistry接口的引入,使得在Spring内部将单例Bean的管理功能抽象到SingletonBeanRegistry接口中,这让单例Bean的管理与其他Bean管理功能解耦。如果需要在不同的上下文中管理单例Bean,可以直接使用SingletonBeanRegistry接口,而不必依赖于整个BeanFactory接口。例如,一些轻量级的容器或模块可能只需要单例Bean管理功能,而不需要完整的Bean工厂功能,此时完全可以通过实现SingletonBeanRegistry接口来完成。

本文还解释了多例Bean的生命周期管理,展示了如何通过代码定义和获取多例Bean