spring 源码解析之 bean 的获取

49 阅读8分钟

前言

经过了前一章的分析(spring 源码之核心类介绍与bean 的加载),我们终于结束了对 xml 配置文件的解析,接下来将会面临更大的挑战,就是对 bean 的加载的探索。bean 加载功能实现远比 bean 的解析要复杂得多。卷起来。

快速体验一下bean 的创建

测试代码

回到我们一开始的测试代码

 public static void main(String[] args) {
     // 创建Spring上下文(容器)
     ClassPathXmlApplicationContext context =
             new ClassPathXmlApplicationContext("ApplicationContext.xml");
     // 从容器中获取bean,假设我们有一个名为 'helloWorld' 的bean
     HelloWorld helloWorld = context.getBean("helloWorld", HelloWorld.class);
     // 使用bean
     helloWorld.sayHello();
     // 关闭上下文
     context.close();
 }

HelloWorld helloWorld = context.getBean("helloWorld", HelloWorld.class);

这句代码实现了什么样的功能呢?我们可以先快速体验一下 spring 中代码是如何实现的。

快速体验spring中如何获取bean的

根据上面的 main 方法,一路跟踪到AbstractBeanFactory#doGetBean

 /**
    * Return an instance, which may be shared or independent, of the specified bean.
    * 返回指定bean的实例,该实例可以是共享的,也可以是独立的
    * @param name the name of the bean to retrieve
    * @param requiredType the required type of the bean to retrieve
    * @param args arguments to use when creating a bean instance using explicit arguments
    * (only applied when creating a new instance as opposed to retrieving an existing one)
    * @param typeCheckOnly whether the instance is obtained for a type check,not for actual use
    * @return an instance of the bean
    * @throws BeansException if the bean could not be created
    * args 使用显式参数创建bean实例时要使用的参数
    * name 要检索的bean的名称
    * typeCheckOnly 是否获取实例是为了进行类型检查,而不是为了实际使用
    */
   @SuppressWarnings("unchecked")
   protected <T> T doGetBean(
       String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
       throws BeansException {
 ​
     // 获取 beanName 去除一些特殊字符 如&
     // 这个 name 可能是普通的 bean,也可能是 factoryBean这个特殊的bean,factoryBean的名称是带有&的
     String beanName = transformedBeanName(name);
     Object beanInstance;
 ​
     // Eagerly check singleton cache for manually registered singletons.
     // 检查单例缓存中是否有手动注册的单例
     // 为什么首先会使用这段代码呢?
     // 因为在创建单例 bean 的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,
     // spring 创建 bean 的原则就是不等 bean 创建完成就会将 bean 的 ObjectFactory
     // 也就是将ObjectFactory加入到缓存中,一旦下一个 bean 创建时需要依赖上一 bean 则直接使用 ObjectFactory
     Object sharedInstance = getSingleton(beanName);
     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 + "'");
         }
       }
       // 获取bean实例,有时候存在如 BeanFactory 的情况并不是返回实例本身,而是返回指定的实例,这个会在这个方法里面详细说
       beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
     }
 ​
     else {
       // Fail if we're already creating this bean instance:
       // We're assumably within a circular reference.
       // 如果是原型类型的 bean 并且已经在创建这个bean实例,则直接抛异常,因为只有单例类型的bean才能被循环引用
       if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
       }
 ​
       // Check if bean definition exists in this factory.
       // 检查此工厂中是否存在bean定义 获取父级工厂
       BeanFactory parentBeanFactory = getParentBeanFactory();
       // 如果存在父级工厂,并当前的BeanDefinition不存在,则尝试去父级工厂获取
       if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
         // Not found -> check parent.
         // 递归到BeanFactory中检测
         String nameToLookup = originalBeanName(name);
         if (parentBeanFactory instanceof AbstractBeanFactory abf) {
           return abf.doGetBean(nameToLookup, requiredType, args, typeCheckOnly);
         }
         else if (args != null) {
           // Delegation to parent with explicit args.
           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);
         }
         // 将当前的BeanDefinition与父级的BeanDefinition 合并成一个BeanDefinition,RootBeanDefinition
         RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);
 ​
         // Guarantee initialization of beans that the current bean depends on.
         // 确保初始化当前bean所依赖的bean。
         String[] dependsOn = mbd.getDependsOn();
         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
             registerDependentBean(dep, beanName);
             try {
               // 获取并实例化依赖 bean
               getBean(dep);
             }
             catch (NoSuchBeanDefinitionException ex) {
               throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                   "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
             }
           }
         }
 ​
         // Create bean instance.
         // 实例化依赖的 bean 后便可以实例化 mbd本身了
         if (mbd.isSingleton()) {
           sharedInstance = getSingleton(beanName, () -> {
             try {
               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;
             }
           });
           beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }
 ​
         else if (mbd.isPrototype()) {
           // It's a prototype -> create a new instance.
           Object prototypeInstance = null;
           try {
             beforePrototypeCreation(beanName);
             prototypeInstance = createBean(beanName, mbd, args);
           }
           finally {
             afterPrototypeCreation(beanName);
           }
           beanInstance = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
         }
 ​
         else {
           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的加载经历了一个相当复杂的过程,其中涉及各种各样的考虑。不过跟着上面的代码,并参照部分代码注释,是可以粗略地了解整个 Spring 加载bean 的过程。对于加载过程中所涉及的步骤大致如下。

  1. 转换对应 beanName 或许很多人不理解转换对应 beanNane 是什么意思,传入的参数 name 不就是 beanName吗?其实不是,这里传入的参数可能是别名,也可能是FactoryBean,所以需要进行一系列的解析,这些解析内容包括如下内容(后面解析方法的时候会详细解释)。
  • 去除 FactoryBean的修饰符,也就是如果 name="&aa”,那么会首先去除&而使 name="aa"
  • 取指定 alias所表示的最终 beanName,例如别名A指向名称为B的bean则返回 B
  • 若别名A指向别名B,别名B又指向名称为C的bean则返回C
  1. 尝试从缓存中加载单例 单例在 Spring 的同一个容器内只会被创建一次,后续再获取 bean,就直接从单例缓存中获取了。当然这里也只是尝试加载,首先尝试从缓存中加载,如果加载不成功则再次尝试从singletonFactories 中加载。因次在创建单例bean 的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,在Spring 中创建 bean 的原则是不等bean创建完成就会将创建 bean的 ObjectFactory 提早曝光加入到缓存中,一旦下一个 bean 创建时候需要依赖上一个 bean 则直接使用 ObjectFactory(后面章节会对循环依赖重点讲解)。
  2. bean 的实例化 如果从缓存中得到了 bean 的原始状态,则需要对 bean 进行实例化。这里有必要强调一下,缓存中记录的只是最原始的 bean状态,并不一定是我们最终想要的bean。举个例子,假如我们需要对工厂bean 进行处理,那么这里得到的其实是工厂 bean 的初始状态,但是我们真正需要的是工厂 bean 中定义的 factory-method 方法中返回的 bean,而getObjectForBeanlnstance 就是完成这个工作的,后续会详细讲解。
  3. 原型模式的依赖检查 只有在单例情况下才会尝试解决循环依赖,如果存在A中有B的属性,B中有A的属性那么当依赖注人的时候,就会产生当A还未创建完的时候因为对于B的创建再次返回创建A,造成循环依赖,也就是情况:isPrototypeCurrentlyInCreation(beanName)判断true
  4. 检測parentBeanFactory 从代码上看,如果缓存没有数据的话直接转到父类工厂上去加载了,这是为什么呢? 这里有一个很重要的判断条件:parentBeanFactory != null && !containsBeanDefinition(beanName),parentBeanFactory != null。parentBeanFactory如果为空,则其他一切都是浮云,这个没什么说的,但是!containsBeanDefinition(beanName)就比较重要了,它是在检测如果当前加载的 XML 配置文件中不包含 beanName 所对应的配置,就只能到 parentBeanFactory 去尝试下了,然后再去递归的调用 getBean方法。
  5. 将存储XML 配置文件的 GernericBeanDefinition 转换为 RootBeanDefinition 因为从 XML配置文件中读取到的bean 信息是存储在 GernericBeanDefinition 中的,但是所有的bean 后续处理都是针对RootBeanDefinition的,所以这里需要进行一个转换,转换的同时如果父类bean 不为空的话,则会一并合并父类的属性。
  6. 寻找依赖 因为 bean 的初始化过程中很可能会用到某些属性,而某些属性很可能是动态配置的,并且配置成依赖于其他的bean,那么这个时候就有必要先加载依赖的 bean,所以,在 Spring的加载循序中,在初始化某一个 bean 的时候首先会初始化这个 bean 所对应的依赖
  7. 针对不同的 scope 进行 bean 的创建 我们都知道,在Spring 中存在着不同的 scope,其中默认的是 singleton,但是还有些其他的配置诸如 prototype、request 之类的。在这个步骤中,Spring 会根据不同的配置进行不同的初始化策略。
  8. 类型转换 程序到这里返回bean 后已经基本结束了,通常对该方法的调用参数 requiredType是为空的,但是可能会存在这样的情况,返回的 bean 其实是个 String,但是 requiredType 却传入 Integer类型,那么这时候本步骤就会起作用了,它的功能是将返回的 bean 转换为 requiredType 所指定的类型。当然,String 转换为 Integer 是最简单的一种转换,在Spring中提供了各种各样的转换器,用户也可以自己扩展转换器来满足需求。
  9. 经过上面的步骤后 bean 的加载就结束了,这个时候就可以返回我们所需要的 bean了