返回

单例Bean获取过程源码解析

后端

Spring 单例 Bean:深入剖析获取过程

什么是单例 Bean?

Spring 中的 Bean 是一种可重用的组件,它代表应用程序中的某个对象或功能。单例 Bean 是最常见的 Bean 类型,它在整个应用程序生命周期中只会被创建一次,并且可以被多个对象共享。

单例 Bean 的获取过程

当应用程序需要使用某个 Bean 时,它会向 Spring 容器请求该 Bean。Spring 容器会执行以下步骤来获取单例 Bean:

  1. 查找 Bean 定义: 容器会检查 Bean 定义映射表,查找与请求 Bean 名称相对应的 Bean 定义。Bean 定义包含了有关 Bean 的元数据,例如其类型和依赖关系。
  2. 检查 Bean 实例: 如果 Bean 定义存在,容器会检查该 Bean 是否已经实例化。如果是,则直接返回该 Bean 的实例。
  3. 实例化 Bean: 如果 Bean 尚未实例化,容器会使用 Bean 定义中的信息创建该 Bean 的实例。它会自动执行依赖注入,将必要的依赖项注入到 Bean 中。
  4. 存储 Bean 实例: 容器会将实例化的 Bean 存储在 Bean 映射表中,以供以后使用。
  5. 返回 Bean 实例: 最后,容器会将 Bean 实例返回给请求对象。

源码解析

Spring 容器获取单例 Bean 的源码位于 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry 类中。以下是核心方法的源码解析:

public Object getBean(String name) throws BeansException {
    Object sharedInstance = getSingleton(name);
    return (sharedInstance != NULL_OBJECT ? sharedInstance : getSingleton(name, true));
}

getBean() 方法首先调用 getSingleton() 方法获取单例 Bean。如果单例 Bean 已存在,则直接返回。否则,它会调用 getSingleton() 方法并传入 true 参数,表示需要创建单例 Bean。

public Object getSingleton(String name, boolean allowEarlyReference) throws BeansException {
    // 获取 Bean 定义映射表
    Object bean = doGetSingleton(name, allowEarlyReference);
    if (bean == null) {
        throw new BeanCreationNotAllowedException(name, "Singleton bean creation not allowed while singletons of this factory are in creation");
    }
    return bean;
}

getSingleton() 方法首先调用 doGetSingleton() 方法获取单例 Bean。如果单例 Bean 已存在,则直接返回。否则,它会抛出异常,表示无法创建单例 Bean。

protected Object doGetSingleton(String name, boolean allowEarlyReference) throws BeansException {
    // 获取 Bean 定义映射表
    BeanDefinition bd = this.beanDefinitionMap.get(name);
    if (bd == null) {
        return null;
    }
    Object bean = this.beanFactory.getBean(name, bd, allowEarlyReference);
    // 将 Bean 存储在 Bean 映射表中
    this.singletonObjects.put(name, (bean != NULL_OBJECT ? bean : null));
    return bean;
}

doGetSingleton() 方法首先从 Bean 定义映射表中获取 Bean 定义。如果 Bean 定义不存在,则返回 null。如果 Bean 定义存在,则调用 getBeanFactory() 方法获取 Bean。getBeanFactory() 方法会实例化该 Bean,并将其存储在 Bean 映射表中。最后,返回该 Bean 的实例。

总结

Spring 容器获取单例 Bean 的过程是一个多步骤的过程,涉及查找 Bean 定义、检查 Bean 实例、实例化 Bean 和存储 Bean 实例。通过理解这个过程,你可以更好地理解 Spring Bean 管理的机制。

常见问题解答

Q:单例 Bean 是否总是线程安全的?
A: 不一定。Spring 默认情况下不会保证单例 Bean 的线程安全性。如果你需要一个线程安全的 Bean,你可以在 Bean 定义中指定 @Scope("singleton") 注解并实现 ThreadLocal

Q:如何避免循环依赖注入?
A: Spring 通过使用代理或延迟加载来避免循环依赖注入。它会在 Bean 完全初始化之前创建 Bean 的代理对象,并注入该代理对象。

Q:我可以覆盖单例 Bean 的默认作用域吗?
A: 是的。你可以使用 @Scope 注解指定 Bean 的作用域。例如,@Scope("prototype") 将创建一个原型 Bean,而不是单例 Bean。

Q:如何手动注册单例 Bean?
A: 你可以使用 registerSingleton() 方法手动将 Bean 注册为单例 Bean。但是,一般情况下,最好使用 Spring 的自动装配机制。

Q:单例 Bean 的优点是什么?
A: 单例 Bean 的优点包括:

  • 减少内存消耗,因为它只需要创建一个实例。
  • 提高性能,因为 Bean 只需要初始化一次。
  • 简化对象管理,因为它不需要跟踪多个 Bean 实例。