• Bean的加载


    前言

    经过前面的文章分析,我们结束了对XML配置文件的解析,这篇文章开始对bean加载进行分析,话不多说,开始。

    Bean加载入口

    下面有很简单的一段代码可以作为Spring代码加载的入口:

     1 ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
     2 ac.getBean(XXX.class);

     ClassPathXmlApplicationContext用于加载CLASSPATH下的Spring配置文件,可以看到,第二行就已经可以获取到Bean的实例了。所以先来分析一下第一句代码,还是对照着源码来分析:

    public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
                throws BeansException {
    
            return doGetBean(name, requiredType, args, false);
        }
      1 protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      2             @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
      3         //提取对应的beanName
      4         final String beanName = transformedBeanName(name);
      5         Object bean;
      6 
      7         // 检查缓存中或者实例工厂中是否有对应的实例:尝试直接从缓存中获取或者singletonFactories中的objectFactory中获取
      8         Object sharedInstance = getSingleton(beanName);
      9         if (sharedInstance != null && args == null) {
     10             if (logger.isTraceEnabled()) {
     11                 if (isSingletonCurrentlyInCreation(beanName)) {
     12                     logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
     13                             "' that is not fully initialized yet - a consequence of a circular reference");
     14                 }
     15                 else {
     16                     logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
     17                 }
     18             }
     19             //返回对应的实例,有时候诸如BeanFactory的情况下并不是直接返回实例本身而是返回指定方法返回的实例
     20             bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
     21         }
     22 
     23         else {
     24             /**
     25              * 只有在单例情况下才会尝试解决循环依赖,原型模式情况下,如果存在A中有B的属性,B中有A的属性,
     26              * 那么当依赖注入的时候,就会产生在当A还未创建完成的时候,因为对于B的创建会再次返回创建A,造成循环依赖,也就是下面的情况,
     27              * isPrototypeCurrentlyInCreationException(beanName)为true。
     28              */
     29             if (isPrototypeCurrentlyInCreation(beanName)) {
     30                 throw new BeanCurrentlyInCreationException(beanName);
     31             }
     32 
     33             //确认是否存在父类工厂
     34             BeanFactory parentBeanFactory = getParentBeanFactory();
     35             //若父类不为空并且BeanDefinitionMap中所有加载的类不包括beanName,则尝试在父类工厂去检测
     36             if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
     37                 // Not found -> check parent.
     38                 String nameToLookup = originalBeanName(name);
     39                 if (parentBeanFactory instanceof AbstractBeanFactory) {
     40                     return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
     41                             nameToLookup, requiredType, args, typeCheckOnly);
     42                 }
     43                 else if (args != null) {
     44                     // 委托给具有显示参数args的父工厂
     45                     return (T) parentBeanFactory.getBean(nameToLookup, args);
     46                 }
     47                 else if (requiredType != null) {
     48                     //委托给标准的父工厂getBean方法
     49                     return parentBeanFactory.getBean(nameToLookup, requiredType);
     50                 }
     51                 else {
     52                     return (T) parentBeanFactory.getBean(nameToLookup);
     53                 }
     54             }
     55 
     56             if (!typeCheckOnly) {
     57                 //如果不是仅仅做类型检查则是创建Bean,这里需要进行记录
     58                 markBeanAsCreated(beanName);
     59             }
     60 
     61             try {
     62                 //将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,如果指定beanName是子bean的话同时会合并父类的相关属性
     63                 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
     64                 checkMergedBeanDefinition(mbd, beanName, args);
     65 
     66                 //得到该bean所依赖的相关bean
     67                 String[] dependsOn = mbd.getDependsOn();
     68                 //循环实例化依赖的bean
     69                 if (dependsOn != null) {
     70                     for (String dep : dependsOn) {
     71                         if (isDependent(beanName, dep)) {
     72                             throw new BeanCreationException(mbd.getResourceDescription(), beanName,
     73                                     "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
     74                         }
     75                         //缓存依赖调用
     76                         registerDependentBean(dep, beanName);
     77                         try {
     78                             getBean(dep);
     79                         }
     80                         catch (NoSuchBeanDefinitionException ex) {
     81                             throw new BeanCreationException(mbd.getResourceDescription(), beanName,
     82                                     "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
     83                         }
     84                     }
     85                 }
     86                 //到这里已经实例化玩依赖的bean了,所以开始实例化mbd本身了----------------------------
     87                 //singleton模式的创建
     88                 if (mbd.isSingleton()) {
     89                     sharedInstance = getSingleton(beanName, () -> {
     90                         try {
     91                             return createBean(beanName, mbd, args);
     92                         }
     93                         catch (BeansException ex) {
     94                             // Explicitly remove instance from singleton cache: It might have been put there
     95                             // eagerly by the creation process, to allow for circular reference resolution.
     96                             // Also remove any beans that received a temporary reference to the bean.
     97                             destroySingleton(beanName);
     98                             throw ex;
     99                         }
    100                     });
    101                     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    102                 }
    103 
    104                 else if (mbd.isPrototype()) {
    105                     // prototype模式的创建
    106                     Object prototypeInstance = null;
    107                     try {
    108                         beforePrototypeCreation(beanName);
    109                         prototypeInstance = createBean(beanName, mbd, args);
    110                     }
    111                     finally {
    112                         afterPrototypeCreation(beanName);
    113                     }
    114                     bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    115                 }
    116 
    117                 else {
    118                     String scopeName = mbd.getScope();
    119                     final Scope scope = this.scopes.get(scopeName);
    120                     if (scope == null) {
    121                         throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
    122                     }
    123                     try {
    124                         Object scopedInstance = scope.get(beanName, () -> {
    125                             beforePrototypeCreation(beanName);
    126                             try {
    127                                 return createBean(beanName, mbd, args);
    128                             }
    129                             finally {
    130                                 afterPrototypeCreation(beanName);
    131                             }
    132                         });
    133                         bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
    134                     }
    135                     catch (IllegalStateException ex) {
    136                         throw new BeanCreationException(beanName,
    137                                 "Scope '" + scopeName + "' is not active for the current thread; consider " +
    138                                 "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
    139                                 ex);
    140                     }
    141                 }
    142             }
    143             catch (BeansException ex) {
    144                 cleanupAfterBeanCreationFailure(beanName);
    145                 throw ex;
    146             }
    147         }
    148 
    149         // 检查需要的类型是否符合bean的实际类型
    150         if (requiredType != null && !requiredType.isInstance(bean)) {
    151             try {
    152                 T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
    153                 if (convertedBean == null) {
    154                     throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
    155                 }
    156                 return convertedBean;
    157             }
    158             catch (TypeMismatchException ex) {
    159                 if (logger.isTraceEnabled()) {
    160                     logger.trace("Failed to convert bean '" + name + "' to required type '" +
    161                             ClassUtils.getQualifiedName(requiredType) + "'", ex);
    162                 }
    163                 throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
    164             }
    165         }
    166         return (T) bean;
    167     }

    仅仅从上述代码就能看出来bean的加载经历了一个相当复杂的过程,其中涉及到了各种各样的考虑。对于加载过程中所涉及的步骤大致如下:

    (1)转换对应的beanName;

      这里需要说明一下转换对应的beanName是什么意思,很多人会认为传入的name不就是beanName吗?其实不是,这里传入的参数可能是别名,也可能是FactoryBean,所以需要进行一系列的解析,这些解析的内容包括如下内容:

        ❤ 去除FactoryBean的修饰符,比如说name="&aa",那么首先就会去除&而使得name="aa"。

        ❤ 取指定alias所表示的最终beanName,例如别名A指向名称为B的bean则返回B;别名为A指向别名B,别名B又指向名称为C的bean则返回C。

    (2)尝试从缓存中加载单例;

      单例在Spring的同一个容器内只会被创建一次,后续再获取bean,就直接从单例缓存中获取了。当然这里只是尝试加载,首先创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,在Spring中创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建时候需要依赖上一个bean则直接使用ObjectFactory。

    (3)bean的实例化;

      如果从缓存中得到了bean的原始状态,则需要对bean进行实例化。这里需要强调一下,缓存中记录的只是最原始的bean状态,并不一定是我们最终相要的bean。举例说明:假如我们需要对工厂bean进行处理,那么这里得到的其实是工厂bean的初始状态,但是我们真正需要的是工厂bean中定义的factory-method方法中返回的bean,而getObjectForBeanInstance()就是完成这个工作的。(后面的文章会详细的讲解)。

    (4)原型模式的依赖检查;

      只有在单例情况下才会尝试解决循环依赖,如果A中有B的属性,B中有A的属性,那么当依赖注入的时候,就会产生当A还未创建完成的时候因为对B的创建再次返回创建A,造成了循环依赖,也就是这种情况:isPrototypeCurrentlyInCreation(beanName)判断为true。

    (5)检测parentBeanFactory;

      从代码上看,如果缓存中没有数据的话直接转到父类工厂上去加载,这是为什么呢?

        其实不仅仅只是判定缓存中没有数据,这里的判断条件是:parentBeanFactory != null && !containsBeanDefinition(beanName)。parentBeanFactory != null,parentBeanFactory如果为空,则其他一切都是浮云,这就没有什么说的了。但是!containBeanDefinition(beanName)就比较重要了,这句是检测如果当前加载的XML配置文件中不包含beanName所对应的配置,就只能到parentBeanFactory去尝试下了,然后再去递归的调用getBean方法。

    (6)将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition;

      因为从XML配置文件中读取到的bean信息是存储到GenericBeanDefinition上的,但是所有的bean后续处理都是针对RootBeanDefinition的,所以这里需要进行一个转换,转换的同时如果父类bean不为空的话,则合并父类的属性。

    (7)寻找依赖;

      因为bean的初始化过程很可能会用到某些属性,而某些属性很可能是动态配置的,并且配置成依赖于其他的bean,那么这个时候就有必要先加载依赖的bean,所以,在Spring的加载顺序中,在初始化某一个bean的时候首先会初始化这个bean所对应的依赖。

    (8)针对不同的scope进行bean的创建;

      在Spring中存在着不同的scope,其中默认的是singleton,但是还有些其他的配置如prototype、request之类的。在这个步骤中,Spring会根据不同的配置进行不同的初始化策略。

    (9)类型转换;

      程序到这里返回bean后已经基本结束了,通常对该方法的调用参数requiredType是为空的,但是可能会存在这样的情况,返回的bean其实是个String,但是requiredType却传入Integer类型,那么这时候这个步骤就会起作用了,它的功能是将返回的bean转换为requiredType所指定的类型,在Spring中提供了各种各样的转换器,用户也可以自己扩展转换器来满足需求。

    经过上面的步骤后,bean的加载就已经结束了,这个时候返回的就是我们需要的bean了。

    接下来看一下bean加载的时序图,加深对流程的了解:

    至此,本篇文章只是大致的对bean加载的流程以及一些特别的点进行说明,接下来的文章会继续围绕着这篇文章进行讲解,使得bean加载过程通透明了。

    参考:《Spring源码深度解析》 郝佳 编著:

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    CWinApp类 功能
    CreateCompatibleBitmap
    CreateCompatibleDC CreateCompatibleBitmap SelectObject详解
    Linux学习_菜鸟教程_4
    Linux学习_菜鸟教程_1
    Linux学习_菜鸟教程_2
    Linux学习_菜鸟教程_3
    MATLAB生成正弦波
    03补件处理流程
    对数据库中所有的表进行操作
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/10140274.html
Copyright © 2020-2023  润新知