Spring源码学习笔记——Bean加载

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 在分析源码时,默认大家已经掌握Spring框架的基本使用!如果还不会Spring的萌新,这里推荐几个Spring框架入门的教程:

快速入门:狂神讲的Spring教程,这个很适合刚入门,想快速过一遍Spring框架的小伙伴!

加强巩固:尚硅谷-Spring5框架最新版教程,尚硅谷的教程质量一向很不错,非常全面,也适合初学框架的新手!

学完框架,自己多练习使用,只有熟悉使用了,看源码才不那么迷茫!切不可一味的堆积课程进度快餐式学习,要反复消化哦~


下面进入正题:


分析源码离不开注释,先搭建起来Spring源码阅读环境:超详细图解!教小白学妹基于IDEA+Gradle+jdk11搭建Spring框架源码阅读环境

本文主要内容参考《Spring源码深度解析》这本书,以及一些技术博客分享。

Spring源码系列文章肝了整整2个月,希望大家三连支持一下!


image.png

image.png

Spring源码分析——Bean的加载

0. 前言引入

熟悉Spring框架的小伙伴都知道,Spring有两大核心模块:IOC (控制反转 ) 和 AOP (面向切面编程)。对于Spring IOC,我们又通常将其称为 IOC 容器,IOC 的2个实现方式分别为依赖注入(DI)和依赖查找(DL)。


注:由于依赖查找(DL)使用的很少,因此 IOC 也被叫做依赖注入。


IOC 和 DI 、DL 的关系图:


image.png

image.png

Spring IOC 实现了依赖注入,通过一个核心的 Bean 工厂 (BeanFactory) 来负责各个 Bean 的实例化和依赖管理。各个 Bean 不需要考虑各自复杂的创建过程,进而实现解耦。


对于 IOC 来说,最重要的概念就是容器。容器管理着 Bean 的生命周期,控制着 Bean 的依赖注入。


Spring 作者 Rod Johnson 设计了两个接口用以表示容器:


BeanFactory

BeanFactory 粗暴简单,可以理解为就是个 HashMap结构,Key 是 BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 “低级容器”。

ApplicationContext

ApplicationContext 可以称之为 “高级容器”。因为他比 BeanFactory 多了更多的功能。他继承了多个接口。因此具备了更多的功能。例如资源的获取,支持多种消息(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等待。所以你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 Bean。

我们通过UML图来看一下BeanFactory与ApplicationContext的关系:


image.png

image.png

从UML关系图中,也可以看出ApplicationContext肯定会比BeanFactory更加复杂。在之后 Spring源码分析——容器扩展中会详细分析(努力更新中)~


如果对Spring IOC的功能进行粗略概括的话,其主要分为如下2个功能点:


XML标签的解析与加载(也可以是注解),读取 XML 配置文件,将XML配置文件中的标签信息解析为 BeanDefinition 的形式(即,从配置文件或者注解中获取 Bean 的定义信息解析为 BeanDefinition 对象,并为其注册一些扩展功能)。

—> BeanDefinition :XML标签解析,总体来说主要就是完成对默认标签的4个标签进行解析,即:标签、标签、标签(最为复杂)、标签。其中的过程弯弯绕绕,不过我们只要清楚,目的是将XML 配置文件中的配置转换为 BeanDefinition 对象。

@Bean —> BeanDefinition

Bean加载,通过XML解析后得到的Bean 的定义信息( BeanDefinition )获取 Bean 对象实例。

BeanDefinition —> Bean对象

此外,IOC 还具有:自动装配、支持集合、指定初始化方法和销毁方法等功能。

如下图所示:


image.png

image.png

本文主要是对Bean加载这一流程进行解析,而XML标签的解析加载,这里不作为重点!


1. FactoryBean接口和BeanFactory接口

我们先来分析一下在Spring Bean加载中,常用的2个工厂接口:


BeanFactory 是 Bean 的工厂,使用简单工厂模式, ApplicationContext 的父类,IOC 容器的核心(容器顶级接口),负责生产、实例化、配置Bean对象以及建立这些Bean对象间的依赖。BeanFactory 实例化后并不会自动实例化 Bean,只有当 Bean 被使用时才实例化与装配依赖关系,属于延迟加载,适合多例模式。

FactoryBean 是 Bean(工厂 Bean),使用了工厂方法模式,作用是生产其他 Bean 实例,可以通过实现该接口,提供一个工厂方法来自定义实例化 Bean 的逻辑。FactoryBean 接口由 BeanFactory 中配置的对象实现,这些对象本身就是用于创建对象的工厂,如果一个 Bean 实现了这个接口,那么它就是创建对象的工厂 Bean,而不是 Bean 实例本身。

1.1 FactoryBean接口

FactoryBean接口中定义了三个方法:

public interface FactoryBean<T> {
    // 获取由FactoryBean创建的Bean实例:
    @Nullable
    T getObject() throws Exception;
    // 获得FactoryBean创建的Bean的类型:
    @Nullable
    Class<?> getObjectType();
    // 判断Bean实例的作用域是否是单例Singleton,是则返回true,否则返回false
    // 如果该实例是单例模式的,则该Bean实例会放到Spring容器的单例缓存池中:
    default boolean isSingleton() {
        return true;
    }
}

1.2 BeanFactory接口

BeanFactory接口中定义了如下一些方法:

public interface BeanFactory {
    // 如果Bean实例对象对应的beanName开头带有&符号,说明是一个FactoryBean类型的对象
    String FACTORY_BEAN_PREFIX = "&";
    // 根据beanName从BeanFactory中获取Bean对象
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    // 从BeanFactory中获取指定类型(requiredType)的Bean对象
    <T> T getBean(Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
    // 判断BeanFactory中是否包含指定beanName的Bean对象
    boolean containsBean(String name);
    // 当前Bean对象是否是单例Singleton
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    // 当前Bean对象是否是Prototyp模式
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    oolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    // 根据beanName获取Bean对象的类型
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException;
    // 获取指定Bean的别名列表
    String[] getAliases(String name);
}

1.3 基本使用案例

首先新建一个实体类Student:

public class Student {
   private int age;
   private String name;
   private String school;
   // setter/getter方法省略
}

下面新建一个用来作为Bean对象的类,令其实现 FactoryBean 接口:

public class StudentFactoryBean implements FactoryBean<Student> {
   private String studentInfo;
   public String getStudentInfo() {
      return studentInfo;
   }
   public void setStudentInfo(String studentInfo) {
      this.studentInfo = studentInfo;
   }
   @Override
   public Student getObject() throws Exception {
      Student stu = new Student();
      String[] splitInfo = studentInfo.split(",");
      stu.setAge(Integer.valueOf(splitInfo[0]));
      stu.setName(splitInfo[1]);
      stu.setSchool(splitInfo[2]);
      return stu;
   }
   @Override
   public Class<Student> getObjectType() {
      return Student.class;
   }
   @Override
   public boolean isSingleton() {
      return false;
   }
}

上面代码中,最核心的就是T getObject() 方法的重写!下面我们在XML配置文件中将其注入Spring容器:

<bean id="studentFactoryBean" class="top.csp1999.StudentFactoryBean">
   <property name="studentInfo" value="18,csp,Haust" />
</bean>

2. Bean加载总体流程概述

先来看一个Demo:

声明2个要注册到IOC中的对象ComponentAComponentB

public class ComponentA {
}
public class ComponentB {
}

测试从IOC中获取这两个Bean对象:

public class BeanFactoryTest {
  public static void main(String[] args) {
    // 加载与解析XML配置文件,获得BeanFactory:
    BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring-bf.xml"));
    // 从BeanFactory中获取Bean对象
    Object a = beanFactory.getBean("componentA");
    Object b = beanFactory.getBean("componentB");
    System.out.println(a);// com.myspring.test.xmltest.ComponentA@1c93084c
    System.out.println(b);// com.myspring.test.xmltest.ComponentB@6ef888f6
  }
}

由上面的Demo我们可以知道,Spring通过调用 BeanFactorygetBean() 方法来加载 Bean,那么我们进入 AbstractBeanFactory 来看一下源码:

/**
 * 根据参数name,requiredType,args获取Bean对象实例:
 *
 * @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
 * @param requiredType 检索所需的Bean类型 -> 获取Bean对象时,不仅要根据name去检索,还要判断Bean的类型是否一致
 * @param args 这个参数用到再做分析
 * @return 该方法返回目标Bean的一个实例
 * @throws BeansException if the bean could not be created
 */
public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
    throws BeansException {
  // 调用真正去获取Bean对象的方法:
  // 注:Spring框架源码的命名规范非常严谨,doXxx方法(内层方法)内封装的是具体执行逻辑的代码,而调用doXxx的方法是其外层方法
  return doGetBean(name, requiredType, args, false);
}
/**
 * 真正去获取Bean对象的方法:
 * @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
 * @param requiredType 检索所需的Bean类型
 * @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
 * @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
 * @return 返回Bean的一个实例
 * @throws BeansException if the bean could not be created
 */
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
    String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    throws BeansException {
  // transformedBeanName(name)方法:根据传入的name参数,获取真正的Bean对应的beanName,什么意思呢?
  // Spring中管理的Beand对象是可以指定设置别名的,Spring Bean设置别名的两种方式:参考 https://blog.csdn.net/qq_34129814/article/details/7
  // 参数传进来的name,有可能是一个别名(eg: alias设置的别名),也有可能是一个&开头的name(解释如下):
  // (1)别名name(eg: alias设置的别名),transformedBeanName(name)方法就是通过别名重定向出来真实beanName名称
  // (2)&开头的name,说明,你要获取的Bean实例对象,是一个FactoryBean对象。
  // FactoryBean:如果某个Bean的配置非常复杂,使用Spring管理不容易、不够灵活,想要使用编码的形式去构建它,
  // 那么你就可以提供一个构建该Bean实例的工厂,这个工厂就是FactoryBean接口实现类。FactoryBean接口实现类还是需要使用Spring管理的。
  // 这里就涉及到两种对象,一种是FactoryBean接口实现类(IOC管理的),另一个就是FactoryBean接口内部管理的对象。
  // 如果要拿FactoryBean接口实现类,使用getBean时传的beanName需要带“&”开头。
  // 如果你要FactoryBean内部管理的对象,你直接传beanName不需要带“&”开头。
  String beanName = transformedBeanName(name);
  // 用于保留返回值(要返回的Bean实例对象)
  Object beanInstance;
  // 根据transformedBeanName方法转换后的真实beanName,直接尝试从缓存中获取Bean的共享单实例(单例):
  // 注:
  // 第一个getSingleton(String beanName)方法是一个参数的,
  // 后面还有一个重载的getSingleton方法(2个参数),2个不要搞混了!
  Object sharedInstance = getSingleton(beanName);
  // CASE1:
  // 如果缓存中有对应的数据,此时缓存数据可能是普通单实例,也可能是 FactoryBean,所以需要根据name来进行判断
  if (sharedInstance != null && args == null) {
    if (logger.isTraceEnabled()) {
      if (isSingletonCurrentlyInCreation(beanName)) {
        logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
            "' that is not fully initialized yet - a consequence of a circular reference");
      }
      else {
        logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
      }
    }
    // 这里为什么又要套呢?为啥不直接拿回去用呢?
    // 其实,你从IOC中拿到的对象,它可能是普通单实例,也可能是FactoryBean实例。
    // 如果是FactoryBean实例,这个时候还要进行处理。主要是看name是带“&” 还是不带“&”,
    // 带“&”:则说明这次getBean方法想要拿FactoryBean对象。
    // 不带“&”:则说明是要拿FactoryBean内部管理的实例。
    /**
     * 获取给定Bean实例的对象,如果是FactoryBean类型,则可以是该实例本身或其创建的子Bean对象。
     * 方法参数:
     * sharedInstance: 缓存中拿到的单实例对象
     * name: 未处理“&”的name
     * beanName: 处理过“&”和别名后的name
     * mbd: 合并过后的bd(BeanDefinition)信息。
     */
    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  }
  // CASE2:
  // 如果根据beanName从缓存中没有找到对应的数据,那么我们就需要自己创建了...
  else {
    // 一、原型循环依赖问题判定:创建Bean时,判断是否出现循环依赖的情况
    // 举个例子:
    // prototypeA -> B, B -> prototypeA
    // 1.会向正在创建中的原型集合内添加一个字符串 “A”
    // 2.创建prototypeA对象,只是一个早期对象。
    // 3.处理prototypeA的依赖,发现A依赖了B类型的对象
    // 4.触发了Spring.getBean(“B”)的操作。
    // 5.根据B的构造方法反射创建出来了B的早期实例
    // 6.Spring处理B对象的依赖,发现依赖了A。
    // 7.Spring转头回来再次去获取A去了。getBean(“A”).
    // 8.条件会返回true,最终抛出异常,算是结束了循环依赖注入。
    if (isPrototypeCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
    }
    // Check if bean definition exists in this factory.
    // 如果beanDefinitionMap中也就是已经加载的类中不包括beanName则尝试从parentBeanFactory中检测
    BeanFactory parentBeanFactory = getParentBeanFactory();
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
      // Not found -> check parent.
      String nameToLookup = originalBeanName(name);
      if (parentBeanFactory instanceof AbstractBeanFactory) {
        return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
            nameToLookup, requiredType, args, typeCheckOnly);
      }
      else if (args != null) {
        // Delegation to parent with explicit args.
        // 递归到BeanFactory中寻找
        return (T) parentBeanFactory.getBean(nameToLookup, args);
      }
      else if (requiredType != null) {
        // No args -> delegate to standard getBean method.
        return parentBeanFactory.getBean(nameToLookup, requiredType);
      }
      else {
        return (T) parentBeanFactory.getBean(nameToLookup);
      }
    }
    // 如果不仅仅是做类型检查则是创建Bean,这里要进行记录
    if (!typeCheckOnly) {
      markBeanAsCreated(beanName);
    }
    StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
        .tag("beanName", name);
    try {
      if (requiredType != null) {
        beanCreation.tag("beanType", requiredType::toString);
      }
      // 二、获取合并BD信息
      // 将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,
      // 如果指定BeanName是子Bean的话同时会合并父类的相关属性
      // 为什么需要合并呀?因为BD支持继承
      // BD: 在XML配置文件中用 parent 属性可以定义父 <bean> 和子 <bean> ,父 <bean> 用 RootBeanDefinition表示
      RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
      // 判断当前BD是否为抽象BD,抽象BD不能创建实例,只能作为父BD让子BD去继承。
      checkMergedBeanDefinition(mbd, beanName, args);
      // 三、depends-on属性处理..
      // <bean name="A" depends-on="B" ... />
      // <bean name="B" .../>
      // 循环依赖问题
      // <bean name="A" depends-on="B" ... />
      // <bean name="B" depends-on="A" .../>
      // Spring是处理不了这种情况的,需要报错..
      // Spring需要发现这种情况的产生。
      // 怎么发现呢? 依靠两个Map,一个map是 dependentBeanMap 另一个是 dependenciesForBeanMap
      // 1. dependentBeanMap 记录依赖当前beanName的其他beanName
      // 2. dependenciesForBeanMap 记录当前beanName依赖的其它beanName集合
      String[] dependsOn = mbd.getDependsOn();
      // 若存在依赖则需要递归实例化依赖的bean
      if (dependsOn != null) {
        for (String dep : dependsOn) {
          // 判断循环依赖..
          if (isDependent(beanName, dep)) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'
          }
          // 假设<bean name="A" depends-on="B" ... />
          // dep:B,beanName:A
          // 以B为视角 dependentBeanMap {"B":{"A"}}
          // 以A为视角 dependenciesForBeanMap {"A" :{"B"}}
          // 缓存依赖调用
          registerDependentBean(dep, beanName);
          try {
            getBean(dep);
          }
          catch (NoSuchBeanDefinitionException ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
          }
        }
      }
      // CASE-SINGLETON:Create bean instance.
      // 创建单实例的逻辑
      // 实例化依赖的Bean后便可以实例化mbd本身了(singleton模式的创建)
      if (mbd.isSingleton()) {
        // 第二个getSingleton方法,这个方法更倾向于创建实例并返回:
        sharedInstance = getSingleton(beanName, () -> {
          try {
            // 创建单例Bean的核心方法
            return createBean(beanName, mbd, args);
          }
          catch (BeansException ex) {
            // Explicitly remove instance from singleton cache: It might have been put there
            // eagerly by the creation process, to allow for circular reference resolution.
            // Also remove any beans that received a temporary reference to the bean.
            destroySingleton(beanName);
            throw ex;
          }
        });
        //这 里为啥不直接返回,还调用getObjectForBeanInstance(...)?
        // 这里为什么又要套呢?为啥不直接拿回去用呢?
        // 其实,你从IOC中拿到的对象,它可能是普通单实例,也可能是FactoryBean实例。
        // 如果是FactoryBean实例,这个时候还要进行处理。主要是看name是带“&” 还是 不带“&”,
        // 带“&”说明这次getBean想要拿FactoryBean对象。
        // 否则是要拿FactoryBean内部管理的实例。
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
      }
      // CASE-PROTOTYPE: 创建多实例
      else if (mbd.isPrototype()) {
        // It's a prototype -> create a new instance.
        // prototype原型模式的创建(new)
        Object prototypeInstance = null;
        try {
          // 记录当前线程相关的正在创建的原型对象beanName
          beforePrototypeCreation(beanName);
          // createBean方法创建对象
          prototypeInstance = createBean(beanName, mbd, args);
        }
        finally {
          // 从正在创建中的集合中移除beanName对应的Bean。
          afterPrototypeCreation(beanName);
        }
        beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
      }
      // CASE-OTHER: 这一情况笔记复杂,不做分析!
      else {
        // 指定的scope上实例化bean
        String scopeName = mbd.getScope();
        if (!StringUtils.hasLength(scopeName)) {
          throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
        }
        Scope scope = this.scopes.get(scopeName);
        if (scope == null) {
          throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
        }
        try {
          Object scopedInstance = scope.get(beanName, () -> {
            beforePrototypeCreation(beanName);
            try {
              return createBean(beanName, mbd, args);
            }
            finally {
              afterPrototypeCreation(beanName);
            }
          });
          beanInstance = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
        }
        catch (IllegalStateException ex) {
          throw new ScopeNotActiveException(beanName, scopeName, ex);
        }
      }
    }
    catch (BeansException ex) {
      beanCreation.tag("exception", ex.getClass().toString());
      beanCreation.tag("message", String.valueOf(ex.getMessage()));
      cleanupAfterBeanCreationFailure(beanName);
      throw ex;
    }
    finally {
      beanCreation.end();
    }
  }
  return adaptBeanInstance(name, beanInstance, requiredType);
}

注意:从上述代码中,可以看到获取Bean对象实例的方法是由getBean()外层方法去调用doGetBean()内层方法,这种嵌套结构在Spring框架源码中非常常见。

通过上面的注释,大致了解到Spring加载 Bean 的过程,下面通过流程图再巩固一下(图片出自其他文章,文章末尾会声明出处):


image.png

image.png

上面是跟踪了 getBean 的调用链创建的流程图,为了能够很好地理解 Bean 加载流程,省略一些异常、日志和分支处理和一些特殊条件的判断。


从上面的流程图中,可以看到一个 Bean 加载会经历这么几个阶段(用绿色标记):


获取 BeanName,对传入的 name 进行解析,转化为可以从 Map 中获取到 BeanDefinition 的 beanName。

合并 Bean 定义,对父类的定义进行合并和覆盖,如果父类还有父类,会进行递归合并,以获取完整的 Bean 定义信息。

实例化,使用构造或者工厂方法创建 Bean 实例。

属性填充,寻找并且注入依赖,依赖的 Bean 还会递归调用 getBean 方法获取。

初始化,调用自定义的初始化方法。

获取最终的 Bean,如果是 FactoryBean 需要调用getObject 方法,如果需要类型转换调用 TypeConverter 进行转化。

3. doGetBean方法详细解析(上)

3.1 beanName的转换方法

transformedBeanName(name)该方法的作用是,根据传入的 name 参数,获取真正的 Bean 对应的 beanName。该方法的 name 参数,有可能是一个别名(alias 属性设置的别名),也有可能是一个&开头的 name:


① 别名name(alias 属性设置的别名),transformedBeanName(name)方法就是通过别名重定向出来真实beanName 名称的!Spring Bean设置别名的两种方式如下:

<!-- 
    方式一: 
  *alias* 属性设置别名, 使用alias设置别名,alias的 name 要和 bean 的 ID 相同, 可以设置多个别名!
-->
<bean id="addOne" class="...">
    <alias name="addOne" alias="add"/>
    <alias name="addOne" alias="doAdd"/>
</bean>
<!-- 
    方式二: 
  *name* 属性设置别名, 多个别名用逗号隔开!
-->
<bean id="addOne" class="..." name="add,doAdd"">
</bean>

& 符号开头的 name ,说明,你要获取的 Bean 实例对象,是一个FactoryBean对象。

getBean("&beanName");// 这种情况获取的就是一个 FactoryBean 对象

获取真正的beanName的步骤如下:

  • 去除 FactoryBean 的修饰符,一般就是去除 name 参数的 & 前缀;
  • 取指定的 alias 所表示的最终 beanName,例如别名 A 指向别名 B,别名 B 指向名称为 CBean,则最后返回 C
/**
 * 真正去获取Bean对象的方法:
 * @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
 * @param requiredType 检索所需的Bean类型
 * @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
 * @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
 * @return 返回Bean的一个实例
 * @throws BeansException if the bean could not be created
 */
protected <T> T doGetBean(
    String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    throws BeansException {
    // 根据传入的name参数,获取真正的Bean对应的beanName
  String beanName = transformedBeanName(name);
    // 用于保留返回值(要返回的Bean实例对象)
  Object beanInstance;
    ...
}

我们跟进transformedBeanName(name);方法内部查看:

/**
 * Return the bean name, stripping out the factory dereference prefix if necessary,
 * and resolving aliases to canonical names.
 * (翻译:返回Bean的真实名称,必要时去除工厂引用前缀,并将别名解析为规范名称)
 *
 * 根据传入的name参数(该参数可能是别名,也可能是&开头的name),获取真正的Bean对应的beanName:
 *
 * @param name 获取Bean对象时,要传入的name参数(该参数可能是别名,也可能是&开头的name)
 * @return 返回转换后真正的Bean名称(beanName)
 */
protected String transformedBeanName(String name) {
  // 1.BeanFactoryUtils.transformedBeanName(name) 调用工厂工具类的转换方法:
  // 根据传入的name参数去获取转换后的name参数(去掉"&"符号):eg -> name="&abc" 则转换后获得 name="abc"
  // 2.canonicalName方法 -> 根据Bean的别名name,获取真实的beanName:
  // BeanFactoryUtils.transformedBeanName(name)方法转换后的name是最终值么?
  // 不是!BeanFactoryUtils.transformedBeanName(name)方法仅仅是去掉name参数的"&"符号而已!
  // 转换后得到的name还有可能是alias属性指定的别名:
  // aliasMap保存别名信息:
  // {"C":"B", "B":"A"} A有一个别名叫做“B”,但是别名“B” 它又被别名了,它有一个别名叫做“C”
  // 假设调用getBean(name)方法时,name传的是“C”,那最终要得到什么? 要得到“A”(真正的beanName),而不是"B"!
  return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}

BeanFactoryUtils.transformedBeanName(name)方法去除 name 参数中的 FactoryBean 的修饰符&

/**
 * Return the actual bean name, stripping out the factory dereference
 * prefix (if any, also stripping repeated factory prefixes if found).
 * (翻译:返回实际的bean名称,删除工厂取消引用前缀(如果有的话,还删除重复的工厂前缀(如果找到)))
 * 
 * 根据传入的name参数去获取转换后的name参数(去掉"&"符号):eg -> name="&abc" 则转换后获得 name="abc"
 * @param name 获取Bean对象时,要传入的name参数(该参数可能是别名,也可能是&开头的name)
 * @return 返回转换后真正的Bean名称(beanName)
 * @see BeanFactory#FACTORY_BEAN_PREFIX
 */
// 位于BeanFactoryUtils工具类中的方法
public static String transformedBeanName(String name) {
  Assert.notNull(name, "'name' must not be null");
  // 如果条件成立:说明调用getBean方法时传的参数name,不是“&”符号开头的,所以直接返回name即可
  if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
    return name;
  }
  // 执行到这里,说明name一定是“&”符号开头的,说明这次getBean方法就是要拿封装为Bean的FactoryBean实例对象(有点套娃的感觉)!
  // 调用transformedBeanNameCache方法将去掉“&”符号得到的结果缓存起来。提高后续程序运行性能的。
  // 科普一下:map.computeIfAbsent(key, value)方法
  // 当map中对应的key为null或者 key->value是null时,这次put操作就会成功,写成功也会返回value。
  // 否则就会失败,并且返回原有key->value。
  return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
    do {
      // 假设 name="&abc" 则转换后获得 name="abc"(将其返回)
      beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
    }
    while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
    return beanName;
  });
}

canonicalName方法,取指定的 alias 所表示的最终 beanName

/**
 * Determine the raw name, resolving aliases to canonical names.
 * (翻译:确定原始名称,将别名解析为规范名称。)
 *
 * 根据Bean的别名name,获取真实的beanName:
 * @param name 根据Bean的别名name
 * @return 返回真实的beanName
 */
// 位于SimpleAliasRegistry类中的方法:
public String canonicalName(String name) {
  String canonicalName = name;
  String resolvedName;
  do {
    // aliasName 和 beanName 的映射关系被注册到 SimpleAliasRegistry 中,
    // do-while循环从该注册器中根据 aliasName 取到 真实的 beanName:
    resolvedName = this.aliasMap.get(canonicalName);
    if (resolvedName != null) {
      canonicalName = resolvedName;
    }
  }
  while (resolvedName != null);
  return canonicalName;
}
/** Map from alias to canonical name. */
//  位于SimpleAliasRegistry类中的成员属性:存储 映射别名和真实beanName的集合
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);

3.2 尝试从缓存中加载Bean的单实例

根据上面transformedBeanName方法转换 name 后得到的真实 beanNamegetSingleton(beanName)方法直接尝试从缓存中获取 Bean 的共享单实例(单例):

/**
 * 真正去获取Bean对象的方法:
 * @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
 * @param requiredType 检索所需的Bean类型
 * @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
 * @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
 * @return 返回Bean的一个实例
 * @throws BeansException if the bean could not be created
 */
protected <T> T doGetBean(
    String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    throws BeansException {
    // 根据传入的name参数,获取真正的Bean对应的beanName
  String beanName = transformedBeanName(name);
    // 用于保留返回值(要返回的Bean实例对象)
  Object beanInstance;
    // 根据transformedBeanName方法转换后的真实beanName,直接尝试从缓存中获取Bean的共享单实例(单例):
    Object sharedInstance = getSingleton(beanName);
    ...
}

在分析getSingleton(beanName)方法之前,我们先来了解下Spring IOC中的三级缓存,以及三级缓存是如何解决循环依赖问题的:


3.2.1 Spring中的三级缓存

所谓三级缓存,其实就是org.springframework.beans.factory包下DefaultSingletonBeanRegistry类中的三个成员属性:

// Spring一级缓存:用来保存实例化、初始化都完成的对象
// Key:beanName
// Value: Bean实例
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// Spring二级缓存:用来保存实例化完成,但是未初始化完成的对象
// Key:beanName
// Value: Bean实例
// 和一级缓存一样也是保存BeanName和创建bean实例之间的关系,
// 与singletonObjects不同之处在于,当一个单例bean被放在里面后,
// 那么bean还在创建过程中,就可以通过getBean方法获取到了,其目的是用来循环检测引用!(后面会分析)
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
// Spring三级缓存:用来保存一个对象工厂,提供一个匿名内部类,用于创建二级缓存中的对象
// Key:beanName
// Value: Bean的工厂
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

如图所示,除了三级缓存是一个HashMap,其他两个都是ConcurrentHashMap:

image.png

Spring之所以引入三级缓存,目的就是为了解决循环依赖问题!

除了上面三个Map集合,还有另一个集合这里也说一下:

// 用来保存当前所有已注册的Bean 
private final Set<String> registeredSingletons = new LinkedHashSet<>(256); 

3.2.2 循环依赖问题:

首先,Spring 解决循环依赖有两个前提条件:

  • Setter方式注入造成的循环依赖(构造器方式注入不可以)
  • 必须是单例

本质上解决循环依赖的问题就是依靠三级缓存,通过三级缓存提前拿到未初始化的对象。下面我们来看一个循环依赖的例子:

image.png

A 对象的创建过程:

  1. 创建对象A,实例化的时候把A对象工厂放入三级缓存
  2. image.png
  3. A 注入属性时,发现依赖 B,转而去实例化 B
  4. 同样创建对象 B,注入属性时发现依赖 A,一次从一级到三级缓存查询 A,从三级缓存通过对象工厂拿到 A,把 A 放入二级缓存,同时删除三级缓存中的 A,此时,B 已经实例化并且初始化完成,把 B 放入一级缓存。
  5. image.png
  6. 接着继续创建 A,顺利从一级缓存拿到实例化且初始化完成的 B 对象,A 对象创建也完成,删除二级缓存中的 A,同时把 A 放入一级缓存。
  7. 最后,一级缓存中保存着实例化、初始化都完成的A、B 对象。
  8. image.png
  9. 从上面5步骤的分析可以看出,三级缓存解决循环依赖是通过把实例化和初始化的流程分开了,所以如果都是用构造器的话,就没法分离这个操作(因为构造器注入实例化和初始是一起进行的)。因此构造器方式注入的话是无法解决循环依赖问题的。

解决循环依赖为什么必须要要三级缓存?二级不行吗?


答案:不可以!


使用三级缓存而非二级缓存并不是因为只有三级缓存才能解决循环引用问题,其实二级缓存同样也能很好解决循环引用问题。


使用三级而非二级缓存并非出于IOC的考虑,而是出于AOP的考虑,即若使用二级缓存,在AOP情形下,往二级缓存中放一个普通的Bean对象,BeanPostProcessor去生成代理对象之后,覆盖掉二级缓存中的普通Bean对象,那么多线程环境下可能取到的对象就不一致了。


一句话总结就是,在 AOP 代理增强 Bean 后,会对早期对象造成覆盖,如果多线程情况下可能造成取到的对象不一致~

了解完什么是循环依赖和三级缓存后,我们继续分析getSingleton(beanName)方法,代码如下:

/**
 * 根据真实beanName到缓存中获取Bean的共享单实例(单例):
 *
 * @param beanName Bean的真实beanName
 * @return
 */
@Override
@Nullable
public Object getSingleton(String beanName) {
     // 第二个参数为true表示允许早期依赖
  return getSingleton(beanName, true);
}
/**
 * 根据真实beanName到缓存中获取Bean的共享单实例(单例):
 *
 * @param beanName Bean的真实beanName
 * @param allowEarlyReference 是否应创建早期参考
 * @return 注册的单例对象;如果找不到,则返回null
 */
// 位于DefaultSingletonBeanRegistry类中
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
  // 首先到一级缓存中尝试获取beanName对应的单实例Bean对象: 
  Object singletonObject = this.singletonObjects.get(beanName);
  // 如果条件一成立(singletonObject == null):有几种可能呢?(2种)
  // 1.单实例确实尚未创建...
  // 2.单实例正在创建中,当前发生循环依赖了...
  // 条件一成立,条件二也会成立:
  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
    // 尝试从二级缓存中获取beanName对应的单实例Bean对象:
    singletonObject = this.earlySingletonObjects.get(beanName);
    // 条件成立:说明二级缓存没有找到~
         // 再尝试从三级缓存中获取beanName对应的单实例Bean对象:
    if (singletonObject == null && allowEarlyReference) {
      // 如果缓存中不存在,锁住一级缓存singletonObjects
      synchronized (this.singletonObjects) {
        // 这里再次从一级缓存中去检索beanName,是为了防止此时有其他线程给一级缓存中添加了该Bean对象(导致加锁对象singletonObjects被修改)
        singletonObject = this.singletonObjects.get(beanName);
        // 这里singletonObject == null 依然成立,说明没有其他线程修改singletonObjects中的缓存数据,则继续向下走!
        if (singletonObject == null) {
          // 同理:从二级缓存中去检索beanName
          singletonObject = this.earlySingletonObjects.get(beanName);
          // 同理:这里singletonObject == null 依然成立,说明没有其他线程修改singletonObjects中的缓存数据,则继续向下走!
          if (singletonObject == null) {
            // 从三级缓存中检索beanName
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            // 如果条件成立:说明3级缓存中有beanName对应的数据,这里涉及到缓存升级~
            if (singletonFactory != null) {
              singletonObject = singletonFactory.getObject();
                                // 记录在缓存中,earlySingletonObjects和singletonFactories互斥!
              // 向2级缓存中存入该数据
              this.earlySingletonObjects.put(beanName, singletonObject);
              // 将3级缓存的该数据干掉
              this.singletonFactories.remove(beanName);
            }
          }
        }
      }
    }
  }
  return singletonObject;
}

总结一下上面代码的执行过程:


首先尝试从缓存singletonObjects获取实例

如果获取不到再从earlySingletonObjects获取

如果还是获取不到,再从singletonFactories中获取beanName对应的ObjectFactory

然后调用这个ObjectFactory的getObject方法来创建Bean,放到earlySingletonObjects,并从singletonFactories移除掉这个ObjectFactory

总之这一块很繁琐,Spring源码阅读就是很烧脑子,真不知设计者是怎样的天才啊!不感叹了,我们接这这个方法结束后,接这回到doGetBean()方法继续往下阅读代码:


3.3 Bean的实例化

getSingleton(beanName)方法执行后,从缓存中得到了 Bean 的原始状态,接下来需要对该 Bean 进行实例化。


缓存中记录的只是最原始的 Bean 状态,并不是我们最终想要的 Bean!


getObjectForBeanInstance方法,其实就是检测获得 Bean 是不是 FactoryBean 类型的 Bean。如果是,那么需要调用该 Bean 对应的 FactoryBean 实例中的 getObject() 作为返回值。

无论是从缓存中获取到的 Bean 还是通过不同的 scope 策略加载的 Bean 都只是最原始的 Bean 状态,并不一定是我们最终想要的 Bean。

假如我们需要对工厂 Bean 进行处理,那么这里得到的其实是工厂 Bean 的初始状态,但我们真正想要的是工厂 Bean 中定义的 factory-method 方法中返回的 Bean,而getObjectForBeanInstance 方法就是完成这个操作。

/**
 * 真正去获取Bean对象的方法:
 * @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
 * @param requiredType 检索所需的Bean类型
 * @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
 * @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
 * @return 返回Bean的一个实例
 * @throws BeansException if the bean could not be created
 */
protected <T> T doGetBean(
    String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    throws BeansException {
    // 根据传入的name参数,获取真正的Bean对应的beanName
  String beanName = transformedBeanName(name);
    // 用于保留返回值(要返回的Bean实例对象)
  Object beanInstance;
    // 根据transformedBeanName方法转换后的真实beanName,直接尝试从缓存中获取Bean的共享单实例(单例):
    Object sharedInstance = getSingleton(beanName);
    // CASE1:
    // 如果缓存中有对应的数据,此时缓存数据可能是普通单实例,也可能是 FactoryBean(需要根据name来进行判断)
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            ... // 这里的代码只是打印日志,就不再加了
        }
        // 获取sharedInstance对应的Bean实例化对象,如果是FactoryBean类型,则可以是该实例本身或其创建的子Bean对象。
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    ...
}

getObjectForBeanInstance(sharedInstance, name, beanName, null);中为什么又要传入未处理过&的 name 呢?

因为,我们从 IOC 中拿到的对象,它可能是普通单实例,也可能是 FactoryBean 实例。如果是FactoryBean 实例,这个时候还要进行处理。主要是看 name 是否带&符号。


带&符号:则说明这次getBean()方法想要拿FactoryBean 对象。

不带&符号:则说明是要拿FactoryBean 内部管理的实例。

3.3.1 从工厂Bean的实例中获取对象

我们跟进getObjectForBeanInstance方法中分析详细一下:

/**
 * Get the object for the given bean instance, either the bean
 * instance itself or its created object in case of a FactoryBean.
 * (翻译:获取给定Bean实例的对象,如果是FactoryBean类型,则可以是该实例本身或其创建的子Bean对象)
 *
 * 获取给定Bean实例的对象,如果是FactoryBean类型,则可以获取该实例本身或其创建的子Bean对象。
 * @param beanInstance 缓存中拿到的单实例对象(初始化状态,尚未实例化)
 * @param name 未处转换处理过的name(可能带有&工厂符号)
 * @param beanName 经过转换处理过的真实beanName
 * @param mbd 合并过后的bd(BeanDefinition)对象
 * @return the object to expose for the bean
 */
protected Object getObjectForBeanInstance(
    Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
  // 如果条件成立:说明当前要获取FactoryBean对象,即name中带有&符号:
  if (BeanFactoryUtils.isFactoryDereference(name)) {
    // 如果beanInstance是null,则直接返回
    if (beanInstance instanceof NullBean) {
      // 直接返回
      return beanInstance;
    }
    // 如果条件成立:说明单实例对象不是 FactoryBean接口实现类,则验证不通过,直接抛异常!
         // (指定的name是工厂相关且beanInstance不是FactoryBean类型,则验证不通过)
    if (!(beanInstance instanceof FactoryBean)) {
      throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
    }
    // 给当前Bean实例对应的mbd打个标记,记录它表达的实例是一个FactoryBean.
    if (mbd != null) {
      mbd.isFactoryBean = true;
    }
    return beanInstance;
  }
  // 执行到这里,有几种情况呢?(2种)
  // 情况1.当前Bean实例就是普通单实例
  // 情况2.当前Bean实例是FactoryBean接口实现类,但是本次请求要拿的是FactoryBean实现类内部管理的实例。
     // 如果用户想要直接获取工厂实例而不是工厂的getObject方法对应的实例,那么传入的name应该加入&
  // 1.当前bean实例就是普通单实例
  if (!(beanInstance instanceof FactoryBean)) {
    // 直接返回该单实例
    return beanInstance;
  }
  // 2.当前Bean实例是FactoryBean接口实现类,但是本次请求要拿的是FactoryBean实现类内部管理的实例。
  // 用于保存FactoryBean实例调用getObject方法后得到的返回值.
  Object object = null;
  if (mbd != null) {
    mbd.isFactoryBean = true;
  }
  else {
    // 尝试从缓存中加载Bean,如果缓存中已经存在 beanName 对应的工厂 Bean 生成的对象,则直接返回
    // (即,尝试到缓存获取FactoryBean.getObject返回值)
    object = getCachedObjectForFactoryBean(beanName);
  }
     // 加载FactoryBean
  // 如果条件成立:说明缓存中没有,就需要到FactoryBean.getObject获取
  if (object == null) {
    // 到这里已经明确知道beanInstance一定是FactoryBean类型
    FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
    // 判断Spring中是否有当前beanName对应的BD信息:
    // containsBeanDefinition检测beanDefinitionMap中也就是在所有已经加载的类中检测是否定义beanName
    if (mbd == null && containsBeanDefinition(beanName)) {
      // 将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,
      // 如果指定BeanName是子Bean的话同时会合并父类的相关属性
      // 为什么是合并后的呢?因为咱们的BD是直接继承的。合并后的BD信息是包含继承回来的BD。
      mbd = getMergedLocalBeanDefinition(beanName);
    }
    // 是否是用户定义的而不是应用程序本身定义的:
    // synthetic 默认值是false 表示这是一个用户对象,如果是true 表示是系统对象。
    boolean synthetic = (mbd != null && mbd.isSynthetic());
    /**
     * getObjectFromFactoryBean是本方法的核心,从给定的FactoryBean中获取一个Bean对象:
     *
     * factory:FactoryBean实例
     * beanName:FactoryBean实例中的Bean对应的beanName
     * synthetic:如果是用户对象,这个值就是true,大部分情况下都是true。
     */
    object = getObjectFromFactoryBean(factory, beanName, !synthetic);
  }
  return object;
}

总的来说,getObjectForBeanInstance 中做了如下几件事情:


对 beanInstance 进行 FactoryBean 正确性的校验;

对非 FactoryBean 类型或者本身就需要返回 FactoryBean 类型对象的 beanInstance 不作处理;

如果缓存中已经存在 beanName 对应的工厂 Bean 生成的对象则直接返回;

如果缓存没有,则对 beanInstance 进行强转为 FactoryBean 类型,将从 Factory 中解析获取 Bean 的工作委托给 getObjectFromFactoryBean(factory, beanName, !synthetic) 方法。

下面我们继续跟进getObjectFromFactoryBean(factory, beanName, !synthetic) 方法:


3.3.2 从给定的FactoryBean中获取一个Bean对象的核心方法

/**
 * 从给定的FactoryBean中获取一个Bean对象:
 * @param factory FactoryBean实例
 * @param beanName FactoryBean实例中的Bean对应的beanName
 * @param shouldPostProcess 如果是用户对象,这个值就是true,大部分情况下都是true。
 * @return 返回从FactoryBean获得的Bean对象
 * @throws BeanCreationException if FactoryBean object creation failed
 * @see org.springframework.beans.factory.FactoryBean#getObject()
 */
// 位于FactoryBeanRegistrySupport中的方法:
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
  // 如果条件成立:说明factory是单例的同时,factory还得已经被实例化
  if (factory.isSingleton() && containsSingleton(beanName)) {
    synchronized (getSingletonMutex()) {
      // 再次尝试从缓存中获取bean
      Object object = this.factoryBeanObjectCache.get(beanName);
      if (object == null) {
        // 委托doGetObjectFromFactoryBean方法产生Bean
        object = doGetObjectFromFactoryBean(factory, beanName);
        // 再次判断缓存中是存在beanName对应的Bean
        Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
        if (alreadyThere != null) {
          object = alreadyThere;
        }
        else {
          if (shouldPostProcess) {
            if (isSingletonCurrentlyInCreation(beanName)) {
              // Temporarily return non-post-processed object, not storing it yet..
              return object;
            }
            beforeSingletonCreation(beanName);
            try {
              // 调用ObjectFactory的后处理器
              object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
              throw new BeanCreationException(beanName,
                  "Post-processing of FactoryBean's singleton object failed", ex);
            }
            finally {
              afterSingletonCreation(beanName);
            }
          }
          // 单例模式下:已经加载的bean要记录下来,便于下次复用
          if (containsSingleton(beanName)) {
            this.factoryBeanObjectCache.put(beanName, object);
          }
        }
      }
      return object;
    }
  }
  else {
    Object object = doGetObjectFromFactoryBean(factory, beanName);
    if (shouldPostProcess) {
      try {
        object = postProcessObjectFromFactoryBean(object, beanName);
      }
      catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
      }
    }
    return object;
  }
}

如果返回的 Bean 是单例的,就必须保证全局唯一,同时不可重复创建,可以使用缓存来提高性能,也就是说加载过就记录下来以便于下次复用。如果返回的 Bean 不是单例的,则直接创建 Bean。


Spring是怎么进行保证单例模式下 Bean 的全局唯一呢?在代码 16 和 19 行,分别在创建 Bean 前后进行双重判断缓存中是否能通过 beanName 获取到实例。


这里的缓存就是 this.factoryBeanObjectCache 这个 ConcurrentHashMap 。该方法在创建完成 Bean 后,最后会将其添加至缓存。


我们大致讲了 getObjectFromFactoryBean() 方法的前面和末尾的流程,其中创建 Bean 的工作实际交给了 doGetObjectFromFactoryBean(factory, beanName) 方法去完成。


3.3.3 真正从FactoryBean中获取一个Bean实例的方法

又是doXxx方法,我们前面说过,Spring框架中经常出现Xxx方法和doXxx方法,内外层配套使用,真正执行代码逻辑的是内层doXxx方法。

/**
 * 真正从FactoryBean中获取一个Bean实例的方法
 * @param factory FactoryBean实例
 * @param beanName FactoryBean实例中的Bean对应的beanName
 * @return 返回从FactoryBean获得的Bean对象
 * @throws BeanCreationException if FactoryBean object creation failed
 * @see org.springframework.beans.factory.FactoryBean#getObject()
 */
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
    // 作为要返回的结果
    Object object;
  try {
         // 需要权限认证(略)
    if (System.getSecurityManager() != null) {
      ...
    }
    else {
              //  factory.getObject()方法获取bean 
      object = factory.getObject();
    }
  }
  catch (FactoryBeanNotInitializedException ex) {// 略}
  catch (Throwable ex) {// 略}
  if (object == null) {
    if (isSingletonCurrentlyInCreation(beanName)) {
      ...
    }
    object = new NullBean();
  }
  return object;
}

从上面的代码中,我们可以清晰的看到调用了 FactoryBean 的 getObject() 方法获得 Bean实例。


我们再回到 getObjectFromFactoryBean() 方法,通过 doGetObjectFromFactoryBean(factory, beanName) 方法创建出 Bean 并没有直接返回,首先再次判断缓存中是否有,如果有那么用缓存的 Bean 返回,没问题。


如果确认缓存没有,那么理论上应该直接返回,但是并没有。Spring还调用 ObjectFactory 的后处理器对 Bean 进行处理。(即,子类 AbstractAutowireCapableBeanFactory 的 postProcessObjectFromFactoryBean(object, beanName) 方法)


下面回到doGetBean()方法继续往下跟进代码逻辑:

/**
 * 真正去获取Bean对象的方法:
 * @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
 * @param requiredType 检索所需的Bean类型
 * @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
 * @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
 * @return 返回Bean的一个实例
 * @throws BeansException if the bean could not be created
 */
protected <T> T doGetBean(
    String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    throws BeansException {
    // 1.根据传入的name参数,获取真正的Bean对应的beanName
  String beanName = transformedBeanName(name);
    // 用于保留返回值(要返回的Bean实例对象)
  Object beanInstance;
    // 2.根据transformedBeanName方法转换后的真实beanName,直接尝试从缓存中获取Bean的共享单实例(单例):
    Object sharedInstance = getSingleton(beanName);
    // CASE1:
    // 如果缓存中有对应的数据,此时缓存数据可能是普通单实例,也可能是 FactoryBean(需要根据name来进行判断)
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            ... // 这里的代码只是打印日志,就不再加了
        }
        // 3.Bean的实例化
        // 获取sharedInstance对应的Bean实例化对象,如果是FactoryBean类型,则可以是该实例本身或其创建的子Bean对象。
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    // CASE2:
    // 如果根据beanName从缓存中没有找到对应的数据 (那么我们就需要自己创建了...)
    else {
        // 4.原型模式(prototype)的依赖检查: Spring只能给解决单例模式下的循环依赖问题,
        // 如果原型模式(prototype)出现循环依赖,则直接抛异常~
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
        // 5.检测parentBeanFactory
        // 如果beanDefinitionMap中也就是已经加载的类中不包括beanName,则尝试从parentBeanFactory中检测
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
           ...
        }
        ...
    }                                             
}

3.4 原型模式(prototype)的依赖检查

在3.2.2小节,我们分析了什么循环依赖,以及Spring如何解决循环依赖问题。从中得出的结论是:


Spring解决循环依赖必须满足2个条件:

Setter方式注入造成的循环依赖(构造器方式注入不可以)

必须是单例

因此上面代码中原型模式(prototype)的依赖检查,目的就是检测原型模式下是否出现循环依赖问题,如果出现,则Spring直接抛出异常throw new BeanCurrentlyInCreationException(beanName);

// 3.原型模式(prototype)的依赖检查: Spring只能给解决单例模式下的循环依赖问题,
// 如果原型模式(prototype)出现循环依赖,则直接抛异常~
if (isPrototypeCurrentlyInCreation(beanName)) {
    throw new BeanCurrentlyInCreationException(beanName);
}

3.5 检测parentBeanFactory

如果第2步getSingleton(beanName);从缓存中没有检索到 beanName 对应的结果,那么就转向父工厂去检索。getParentBeanFactory();方法就是获取父工厂:

@Override
@Nullable
public BeanFactory getParentBeanFactory() {
  return this.parentBeanFactory;
}
/** Parent bean factory, for bean inheritance support. */
// 父Bean工厂,用于支持Bean的继承
@Nullable
private BeanFactory parentBeanFactory;

containsBeanDefinition(beanName)方法的作用是获取当前 BeanDefinitionMap (XML配置文件)中的 beanName 对应的配置。如果获取到则返回 true,否则返回false

// 如果条件成立,说明:父工厂不为空,且当前BeanDefinitionMap中无法获取beanName对应的Bean对象
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
    // 那么就只能到父工程parentBeanFactory中尝试去获取Bean了
    // parentBeanFactory.doGetBean() 相当于递归调用~
    ...
}

完整代码如下:

// 如果beanDefinitionMap中也就是已经加载的类中不包括beanName则尝试从parentBeanFactory中检测
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
  // Not found -> check parent.
  String nameToLookup = originalBeanName(name);
  if (parentBeanFactory instanceof AbstractBeanFactory) {
    return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
        nameToLookup, requiredType, args, typeCheckOnly);
  }
  else if (args != null) {
    // Delegation to parent with explicit args.
    // 递归到BeanFactory中寻找
    return (T) parentBeanFactory.getBean(nameToLookup, args);
  }
  else if (requiredType != null) {
    // No args -> delegate to standard getBean method.
    return parentBeanFactory.getBean(nameToLookup, requiredType);
  }
  else {
    return (T) parentBeanFactory.getBean(nameToLookup);
  }
}

下面继续回到doGetBean()方法往下跟进:

/**
 * 真正去获取Bean对象的方法:
 * @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
 * @param requiredType 检索所需的Bean类型
 * @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
 * @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
 * @return 返回Bean的一个实例
 * @throws BeansException if the bean could not be created
 */
protected <T> T doGetBean(
    String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    throws BeansException {
    // 1.根据传入的name参数,获取真正的Bean对应的beanName
  String beanName = transformedBeanName(name);
    // 用于保留返回值(要返回的Bean实例对象)
  Object beanInstance;
    // 2.根据transformedBeanName方法转换后的真实beanName,直接尝试从缓存中获取Bean的共享单实例(单例):
    Object sharedInstance = getSingleton(beanName);
    // CASE1:
    // 如果缓存中有对应的数据,此时缓存数据可能是普通单实例,也可能是 FactoryBean(需要根据name来进行判断)
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            ... // 这里的代码只是打印日志,就不再加了
        }
        // 3.Bean的实例化
        // 获取sharedInstance对应的Bean实例化对象,如果是FactoryBean类型,则可以是该实例本身或其创建的子Bean对象。
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    // CASE2:
    // 如果根据beanName从缓存中没有找到对应的数据 (那么我们就需要自己创建了...)
    else {
        // 4.原型模式(prototype)的依赖检查: Spring只能给解决单例模式下的循环依赖问题,
        // 如果原型模式(prototype)出现循环依赖,则直接抛异常~
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
        // 5.检测parentBeanFactory
        // 如果beanDefinitionMap中也就是已经加载的类中不包括beanName,则尝试从parentBeanFactory中检测
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
           ...
        }
        // 如果从缓存中获取不到, 同时当前的 beanDefinitionMap 中存在对应 beanName 的配置,
        // 我们就可以依据包含有XML配置文件信息的 beanDefinition 进行创建 bean 了。
        try {
            ...
            // 6.GenericBeanDefinition转为RootBeanDefinition:
            // 将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,
            // 如果指定BeanName是子Bean的话同时会合并父类的相关属性    
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // 判断当前BD是否为抽象BD,抽象BD不能创建实例,只能作为父BD让子BD去继承。
            checkMergedBeanDefinition(mbd, beanName, args);
            // 7.寻找依赖
            String[] dependsOn = mbd.getDependsOn();
            // 若存在依赖则需要递归实例化依赖的bean
            if (dependsOn != null) {
                ...
            }
            ...
        catch (BeansException ex) {
            ...
        }
        finally {
            beanCreation.end();
        }
    }                                             
}

3.6 GenericBeanDefinition转为RootBeanDefinition

如果从缓存中获取不到,且当前的 beanDefinitionMap 中存在对应 beanName 的配置,我们就可以依据包含有XML配置文件信息的 beanDefinition 进行创建 Bean !


从XML配置文件中读取到的 Bean 信息是利用 GenericBeanDefinition 存储的,但是后面Spring对所有 Bean 的后续处理都是针对于 RootBeanDefinition 的,所以需要进行转换,转换的同时如果父类 Bean 不为空,则会一并合并父类的属性。

// 将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,
// 如果指定BeanName是子Bean的话同时会合并父类的相关属性
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// 判断当前BD是否为抽象BD,抽象BD不能创建实例,只能作为父BD让子BD去继承。
checkMergedBeanDefinition(mbd, beanName, args);

3.7 寻找依赖(依赖注入)

因为 Bean 的初始化过程中很可能会用到某些属性,而某些属性很可能是动态配置的,并且配置的成依赖于其他的 Bean,那么此时应该先加载依赖的 Bean。所以在流程中,Spring初始化一个 Bean,会先初始化其依赖的所有的其他 Bean

// 7.寻找依赖
// eg: <bean name="A" depends-on="B" ... />
String[] dependsOn = mbd.getDependsOn();
// 若存在依赖则需要递归实例化依赖的Bean
if (dependsOn != null) {
   for (String dep : dependsOn) {
      // 判断循环依赖:\
      // <bean name="A" depends-on="B" ... />
   // <bean name="B" depends-on="A" .../>
      // Spring是处理不了这种情况的,需要抛异常...
      if (isDependent(beanName, dep)) {
         throw new BeanCreationException(mbd.getResourceDescription(), beanName,
               "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
      }
      // 缓存依赖调用
      registerDependentBean(dep, beanName);
      try {
         getBean(dep);
      }
      catch (NoSuchBeanDefinitionException ex) {
         throw new BeanCreationException(mbd.getResourceDescription(), beanName,
               "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
      }
   }
}

3.8 根据不同的scope作用域创建Bean

Spring中包含如下几个作用域:


singleton 作用域

prototype 作用域

request 作用域

session 作用域

global session 作用域

我们重点分析单例singleton 作用域创建Bean的逻辑,其他几种情况与其类似,不再重复分析:

// 8.根据不同的scope作用域创建Bean
// CASE-SINGLETON:创建单实例Bean的逻辑(singleton模式的创建)
if (mbd.isSingleton()) {
    // 第二个getSingleton方法,获取单例Bean实例,这个方法更倾向于创建实例并返回:
    sharedInstance = getSingleton(beanName, () -> {
        try {
            // 创建单例Bean的核心方法
            return createBean(beanName, mbd, args);
        }
        catch (BeansException ex) {
            // Explicitly remove instance from singleton cache: It might have been put ther
            // eagerly by the creation process, to allow for circular reference resolution.
            // Also remove any beans that received a temporary reference to the bean.
            destroySingleton(beanName);
            throw ex;
        }
    });
    //这 里为啥不直接返回,还调用getObjectForBeanInstance(...)?
    // 这里为什么又要套呢?为啥不直接拿回去用呢?
    // 其实,你从IOC中拿到的对象,它可能是普通单实例,也可能是FactoryBean实例。
    // 如果是FactoryBean实例,这个时候还要进行处理。主要是看name是带“&” 还是 不带“&”,
    // 带“&”说明这次getBean想要拿FactoryBean对象。
    // 否则是要拿FactoryBean内部管理的实例。
    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// CASE-PROTOTYPE: 创建多实例Bean的逻辑(prototype模式的创建)
else if (mbd.isPrototype()) {
   // prototype原型模式的创建(new)
   .....
}
// CASE-OTHER:其他scope作用域创建Bean的逻辑,例如request、session、global session
else {
   // 指定的scope上实例化bean
   .....
}

上面代码中,获取单例 Bean 的 getSingleton(String beanName, ObjectFactory singletonFactory)方法是从缓存种获取Bean实例的getSingleton(String beanName, boolean allowEarlyReference)方法的重载方法。我们跟进该方法查看:

/**
 * 缓存中没有需要新创建单例初始状态的bean
 * @param beanName Bean对象对应的beanName
 * @param singletonFactory Bean工厂
 * @return the registered singleton object
 */
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(beanName, "Bean name must not be null");
   // 全局变量需要同步
   synchronized (this.singletonObjects) {
      // 首先检查对应的bean是否已经加载过,因为是单例模式的,如果有直接返回
      Object singletonObject = this.singletonObjects.get(beanName);
      if (singletonObject == null) {
         // 单例bean的初始化
         if (this.singletonsCurrentlyInDestruction) {
            ... // 抛异常
         }
         if (logger.isDebugEnabled()) {
            ... // 日志打印
         }
         // 记录加载状态,通过this.singletonsCurrentlyInCreation.add(beanName)将当前正要创建的bean记录在缓存中,
         // 便于对循环依赖进行检测
         beforeSingletonCreation(beanName);
         // 创建单例bean是否成功的标识
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
         }
         try {
            // 初始化Bean
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         }
         catch (IllegalStateException ex) {
            // Has the singleton object implicitly appeared in the meantime ->
            // if yes, proceed with it since the exception indicates that state.
            singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
               throw ex;
            }
         }
         catch (BeanCreationException ex) {
            ... // 捕获异常
         }
         finally {
            if (recordSuppressedExceptions) {
               this.suppressedExceptions = null;
            }
            // 创建完Bean之后需要移除缓存中对该Bean正在加载状态的记录
            afterSingletonCreation(beanName);
         }
         // 加入缓存并删除加载Bean过程中所记录的各种辅助状态
         if (newSingleton) {
            addSingleton(beanName, singletonObject);
         }
      }
      // 返回处理结果
      return singletonObject;
   }
}

我们跟进看一下addSingleton(beanName, singletonObject);方法:

/**
 * 添加缓存并移除辅助变量
 * @param beanName the name of the bean
 * @param singletonObject the singleton object
 */
protected void addSingleton(String beanName, Object singletonObject) {
    synchronized (this.singletonObjects) {
        // 加入缓存
        this.singletonObjects.put(beanName, singletonObject);
        this.singletonFactories.remove(beanName);
        this.earlySingletonObjects.remove(beanName);
        this.registeredSingletons.add(beanName);
    }
}

分析完上面代码,接下来去研究一下createBean(beanName, mbd, args) 方法:


3.8.1 准备创建Bean的方法

按照Spring的方法命名风格,真正创建Bena的应该是doCreateBaen()方法,所以该部分主要研究Spring的createBean() 方法中在 doCreateBean() 之前先进行了哪些准备工作。

/**
 * 创建单例Bean的核心方法:(其实是准备创建Bean之前需要做的一些工作)
 * @see #doCreateBean
 */
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      throws BeanCreationException {
   // mbdToUse: 创建实例时使用mbd
   // 在XML配置文件中用 parent 属性可以定义父 <bean> 和子 <bean> ,父 <bean> 用 RootBeanDefinition表示
   RootBeanDefinition mbdToUse = mbd;
   // 锁定class,根据设置的class属性或者根据className来解析Class
   Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
   // 当解析出class同时mbd中beanClass属性设置为字符串对象而不是Class对象时,需要对mbd进行更新为Class类型:
   // 条件一 resolvedClass != null:说明拿到了mbd实例化对象时的真实Class对象。
   // 条件二 !mbd.hasBeanClass():条件成立,说明mbd在resolveBeanClass之前,是没有Class对象的。
   // 条件三 bd.getBeanClassName()!= null:成立,说明mbd有ClassName 
   if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
      mbdToUse = new RootBeanDefinition(mbd);
      mbdToUse.setBeanClass(resolvedClass);
   }
   // 验证override及准备覆盖的方法(预处理...直接跳过,不作为重点)
   try {
      mbdToUse.prepareMethodOverrides();
   }
   catch (BeanDefinitionValidationException ex) {
      ... // 异常抛出
   }
   try {
      // 对BeanDefinition中的属性做些前置处理,给BeanPostProcessors一个机会来返回代理来替代真正的实例
      // 注意,这里的代理对象不是Spring AOP逻辑实现的地方。!
      Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      // 如果前置处理返回的不为空,则直接返回(AOP功能就是这里判断的),否则需要进行常规bean的创建
      // bean != null:条件成立会形成一个短路操作,这里直接返回了.
      if (bean != null) {
         return bean;
      }
   }
   catch (Throwable ex) {
      ... // 异常抛出
   }
   try {
      // 核心方法(真正创建Bean的方法):创建Bean实例对象,并且生命周期的动作大部分都在这里 
      Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      if (logger.isDebugEnabled()) {
         ... // 日志打印
      }
      return beanInstance;
   }
   catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
      ... // 异常处理
   }
   catch (Throwable ex) {
      ... // 异常处理
   }
}

代码的处理流程大致如下:


设置 class 属性或者根据 className 来解析 class ;

对 MethodOverride 属性进行验证及标记;

应用初始化前的后处理器,解析指定 bean 是否存在应用初始化前的短路操作;

创建 bean。

3.8.2 真正创建Bean的方法

doCreateBean 方法,就是真正去创建Bean的方法,这一部分代码量很多,且非常复杂,所以单独抽出来一篇文章进行补充:Spring源码解析——Bean加载(doCreateBean方法补充)


3.9 类型转换

根据 scope 创建完 Bean 成功后,一般可以直接返回即可。但当传入 doGetBean 方法中的 requireType 参数不为空时,意味着我们对最后返回的 Bean 有着类型上的要求。Spring一般通过类型转换器将第⑧步创建完成的 Bean 转换为 requireType 指定的类型。Spring自身提供了一些转换器,用户也可以自己扩展转换器来满足需求。


我们先回到doGetBean方法再来你全局看一下:

/**
 * 真正去获取Bean对象的方法:
 * @param name Bean对象的名称 -> 根据name参数获取对应的Bean对象
 * @param requiredType 检索所需的Bean类型
 * @param args 使用显式参数创建bean实例时要使用的参数(仅在创建新实例而不是检索现有实例时才应用)
 * @param typeCheckOnly 是否获取实例用于类型检查而不是实际使用
 * @return 返回Bean的一个实例
 * @throws BeansException if the bean could not be created
 */
protected <T> T doGetBean(
    String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    throws BeansException {
    // 1.根据传入的name参数,获取真正的Bean对应的beanName
  String beanName = transformedBeanName(name);
    // 用于保留返回值(要返回的Bean实例对象)
  Object beanInstance;
    // 2.根据transformedBeanName方法转换后的真实beanName,直接尝试从缓存中获取Bean的共享单实例(单例):
    Object sharedInstance = getSingleton(beanName);
    // CASE1:
    // 如果缓存中有对应的数据,此时缓存数据可能是普通单实例,也可能是 FactoryBean(需要根据name来进行判断)
    if (sharedInstance != null && args == null) {
        if (logger.isTraceEnabled()) {
            ... // 这里的代码只是打印日志,就不再加了
        }
        // 3.Bean的实例化
        // 获取sharedInstance对应的Bean实例化对象,如果是FactoryBean类型,则可以是该实例本身或其创建的子Bean对象。
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    // CASE2:
    // 如果根据beanName从缓存中没有找到对应的数据 (那么我们就需要自己创建了...)
    else {
        // 4.原型模式(prototype)的依赖检查: Spring只能给解决单例模式下的循环依赖问题,
        // 如果原型模式(prototype)出现循环依赖,则直接抛异常~
        if (isPrototypeCurrentlyInCreation(beanName)) {
            throw new BeanCurrentlyInCreationException(beanName);
        }
        // 5.检测parentBeanFactory
        // 如果beanDefinitionMap中也就是已经加载的类中不包括beanName,则尝试从parentBeanFactory中检测
        BeanFactory parentBeanFactory = getParentBeanFactory();
        if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
           ...
        }
        // 如果从缓存中获取不到, 同时当前的 beanDefinitionMap 中存在对应 beanName 的配置,
        // 我们就可以依据包含有XML配置文件信息的 beanDefinition 进行创建 bean 了。
        try {
            ...
            // 6.GenericBeanDefinition转为RootBeanDefinition:
            // 将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,
            // 如果指定BeanName是子Bean的话同时会合并父类的相关属性    
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
            // 判断当前BD是否为抽象BD,抽象BD不能创建实例,只能作为父BD让子BD去继承。
            checkMergedBeanDefinition(mbd, beanName, args);
            // 7.寻找依赖
            String[] dependsOn = mbd.getDependsOn();
            // 若存在依赖则需要递归实例化依赖的bean
            if (dependsOn != null) {
                ...
            }
            // 8.根据不同的scope作用域创建Bean
            // CASE-SINGLETON:创建单实例Bean的逻辑(singleton模式的创建)
            if (mbd.isSingleton()) {
                ...
            }
            // CASE-PROTOTYPE: 创建多实例Bean的逻辑(prototype模式的创建)
            else if (mbd.isPrototype()) {
               // prototype原型模式的创建(new)
               .....
            }
            // CASE-OTHER:其他scope作用域创建Bean的逻辑,例如request、session、global session
            else {
               // 指定的scope上实例化bean
               .....
            }
        catch (BeansException ex) {
            ...
        }
        finally {
            beanCreation.end();
        }
        // 9.对最终得到的Bean实例进行类型转换
        return adaptBeanInstance(name, beanInstance, requiredType);
    }                                             
}

我们跟进adaptBeanInstance(name, beanInstance, requiredType);方法:

@SuppressWarnings("unchecked")
<T> T adaptBeanInstance(String name, Object bean, @Nullable Class<?> requiredType) {
  // 检查需要的类型是否符合Bean的实际类型
  if (requiredType != null && !requiredType.isInstance(bean)) {
    try {
             // 类型转换:
      Object convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
      if (convertedBean == null) {
        throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
      }
      return (T) convertedBean;
    }
    catch (TypeMismatchException ex) {
      if (logger.isTraceEnabled()) {
        logger.trace("Failed to convert bean '" + name + "' to required type '" +
            ClassUtils.getQualifiedName(requiredType) + "'", ex);
      }
      throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
    }
  }
  return (T) bean;
}

经过以上的九个步骤,Bean 就算创建完成了(即,完整的doGetBean方法的逻辑执行完毕)。其中第8步最为关键——针对不同的 scope 进行 Bean 的创建。


3.10 小节

我们来大致总结一下一个 Bean 的创建过程(即,一个doGetBean方法的逻辑):


① 根据传入的 name 参数,经过转换获取真实的 beanName

② 根据真实的 beanName 向缓存中获取Bean的单实例(这时候获取的是初始状态,尚未实例化)

③ 对 Bean 的单实例进行实例化

④ 对 Bean 的单实例进行依赖注入

⑤ 对 Bean 的单实例进行初始化

⑥ 对最终得到的对 Bean 的单实例进行类型转换

成功加载 Bean 对象!


4.参考文章

https://blog.csdn.net/firefile/article/details/90735264

https://blog.csdn.net/sinat_35663368/article/details/102524884

https://mp.weixin.qq.com/s/AHl52PZOU-jlRLIVps2_qw

https://wangguoping.blog.csdn.net/article/details/82859993

如果文章对大家有帮助,请三连支持一下!

image.png



相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
15天前
|
XML 安全 Java
|
29天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
67 2
|
1月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
13天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
30 2
|
1月前
|
缓存 Java Spring
实战指南:四种调整 Spring Bean 初始化顺序的方案
本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
实战指南:四种调整 Spring Bean 初始化顺序的方案
|
13天前
|
安全 Java 开发者
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
26 1
|
22天前
|
监控 IDE Java
如何在无需重新启动服务器的情况下在 Spring Boot 上重新加载我的更改?
如何在无需重新启动服务器的情况下在 Spring Boot 上重新加载我的更改?
42 8
|
1月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
59 9
|
2月前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
2月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
142 5