Spring解析之IoC:bean的加载(三)

前言
计划赶不上变化,原本年前准备将这个主题告一段落,但各种工作之外的事接踵而至,且剩下的工作量依然超过我的预期,只能拖到现在,前段时间接了商业化广告的活,源码看得多了自己写时总不自觉的按更高的标准要求自己,一遍遍的修改重构,这可能就是所谓的潜移默化吧
bean加载三部曲中的第一部按照doGetBean(String, Class<T>, Object[], boolean)自上而下的代码结构将加载分为if-else两段,前段在getBean显式调用时返回单例对象以及自定义FactoryBean生成bean时调用,其中的重点为部分解决循环依赖的“三层缓存”和getObjectForBeanInstance(Object, String, String, RootBeanDefinition),后段由初始化过程中隐式调用以及获得多例对象时调用,在第二部中介绍了隐式调用的整个流程,最后剩下createBean(String, RootBeanDefinition, Object[])核心创建bean尚未分析,本篇文章就与这部分内容相关

由于只剩下createBean这个核心方法了,那么我们还是像前面将核心方法分成几块讲解

图1. AbstractAutowireCapableBeanFactory的createBean(String, RootBeanDefinition, Object[])

标注1生成最终能实例化的Class类型,但并没有根据Class实例化出对象
图2. 解析出可实例化的Class

在分析解析XML并将解析后内容封装成GenericBeanDefinition过程中曾今说过<bean>class属性与GenericBeanDefinition父类AbstractBeanDefinitionObject beanClass对应,这里mbd.hasBeanClass()就是判断beanClass是否instanceof Class,如果是直接返回,因为我们要的就是Class类型,不是的话最终调用doResolveBeanClass(RootBeanDefinition, Class<?>),该方法依然是个中转方法,主要根据传入的typesToMatch生成特定的ClassLoader,之后还要调用RootBeanDefinition#resolveBeanClass,根据特定的加载器或者默认加载器加载出class属性对应的Class对象,其生成Class的动作又委派给工具类ClassUtils
图3. 使用ClassUtils创建出Class

我们将创建Class流程分成三部分:第一部分主要处理一些“原始”数据类型,举个简单的例子我在XML中配置如下

<bean id="myInt" class="java.lang.Integer">

我定义的对象就是“原始”数据类型,这种类型的Class处理就由第一部分负责,具体是怎么做的呢,实际上是两个Map,一个是resolvePrimitiveClassName(String)中隐含的primitiveTypeNameMap,另一个就是commonClassCache,对应的代码给大家看一下

图4. 原始Class元素的塞入

ClassUtils加载时就会将基本数据类型,基本数据类型对应的包装类型,基本数据类型数组和包装类型数组等Class放入两个Map中,如果我们配置的类属于其中的一个直接返回对应的Class即可。图3红框2内代码处理的类型为java.lang.String[][Ljava.lang.String;[[Ljava.lang.String;,第一个我可以尝试模拟出来,后两个实在没搞明白怎么模拟,有知道的读者麻烦告知。如果之前根据typesToMatch生成了特定的类加载器就用这个ClassLoader加载Class,否则使用默认加载器,默认类加载是首先使用当前线程类加载器,如果还是没有获取到使用当前类的类加载器。这一系列的操作后RootBeanDefinition中的beanClass属性除了异常情况就都是对应<bean>生成对象的Class
回到图1标注2代码是对<lookup-method><replaced-method>两种method injection方式的预处理,在Spring解析之IoC:<bean>解析及Bean的注册中我们没有详细阐述两个标签的解析存储过程,在这里大致了解一下,两个标签解析后分别封装在LookupOverrideReplaceOverride中,两个对象都继承自抽象父类MethodOverride,而多个该类对象又存放在AbstractBeanDefinition成员变量MethodOverrides methodOverrides中,下面我已<lookup-method>举个例子,说明下此类标签的用法已经解决的问题
<lookup-method>标签最重要的一个用途就是在单例对象内注入一个多例对象,我们知道构造器注入和setter注入是两种最常用的注入方式,但如果说到实现单例对象中注入多例对象需求,这两种注入方式都鞭长莫及,因为在Spring创建出单例对象时相关的依赖注入流程都已经完成,也就是说依赖的对象只能注入一次,为了解决这个问题,Spring提出了method injection方法注入,我们先来例子让大家有个直观的认识。首先定义一个单例对象SingletonService
图5. SingletonService

该类是一个抽象类,存在返回多例对象的抽象方法,再定义一个多例对象的抽象接口Prototype
图6. Prototype接口

有接口了必然有就有一个或多个实现,这里为了简便只提供了一个实现类
图7. Prototype接口的实现类MyPrototype

在XML中进行<bean>配置
图8. 相关XML配置

实现类scope = prototype否则就失去了动态注入的意义了,lookup-methodname获得多例对象的抽象方法名称,bean为返回bean的类型,比如这里就是任何实现了Prototype接口的实现类id/name,如果读者想跟深入了解的话,我给出一篇文章Method Injection。说了一堆做铺垫再来看图1标注2做了什么
图9. AbstractBeanDefinition的prepareMethodOverrides()

上面说过所有的方法注入标签都存放在methodOverrides,遍历所有的MethodOverride,根据<lookup-method><replaced-method>中抽象方法名(name属性的值)和抽象类中所有同名的抽象方法比较,算出符合name的抽象方法数量,为什么要这样呢?我试验了一下,结果是可能存在同名重载方法,最简单的例子就是同名抽象方法但参数不同,但<lookup-method><replaced-method>中有无法配置抽象方法的参数,所以需要全部计数。那为什么但只存在一个符合抽象方式时setOverloaded(false)呢?其实读者可以将这里的判断和下面的使用分为两部分考虑,如果存在多个重载方法,那么使用时必定要再次判断到底配置的是哪一个,如果只有一个符合要求这里加上一个标识,在下面使用的时候就省去了再次判断的开销
图1的标注3主要的目的在注释上写的还是比较清楚的,给用户一个机会可以返回目标对象的代理实例,如果返回不为null,即返回了代理对象就直接返回,在正式说这部分逻辑之前还得先补充一个知识,在前面说过Spring提供后处理器接口BeanPostProcessor,提供了在初始化对象后修改实例的能力,该接口有很多子接口,后面有机会会一一介绍,现在先说一种InstantiationAwareBeanPostProcessor,除了父类提供的两个待实现接口外又提供了另外三种,分别为Object postProcessBeforeInstantiation(Class<?>, String)boolean postProcessAfterInstantiation(Object, String)PropertyValues postProcessPropertyValues(PropertyValues, PropertyDescriptor[], Object, String),为了更好的说明还是举个例子
图10. InstantiationAwareBeanPostProcessorImpl

InstantiationAwareBeanPostProcessorImpl实现了接口所有的方法,并做了一些打印以便观看现象,在XML中也加上简单的配置

<bean id="student" class="com.xiaomi.bean.Student">
    <property name="name" value="zhangsan"></property>
</bean>
<bean id="instantiationAwareBeanPostProcessor" class="com.xiaomi.bean.InstantiationAwareBeanPostProcessorImpl"></bean>

运行并两次获得Student实例打印,结果如下

图11. InstantiationAwareBeanPostProcessor例子运行结果

很明显在生成Student实例前调用了postProcessBeforeInstantiation,那么我们可以在这个时候对目标对象进行一定的包装或者处理,达到类似AOP的目的,之后真正创建了Student实例,创建完成进行属性的注入,在注入之前会调用postProcessPropertyValues,在该方法中我们将原本注入的zhangsan替换成了lisi,“调包”后Spring调用了setter进行属性注入,最后才调用父接口BeanPostProcessor的两个方法,这个顺序之前已经说过,没什么意外的。我们获得了两次Student对象由打印语句发现实现InstantiationAwareBeanPostProcessor只会在创建对象前调用一次,当然,如果这里Student是多例,自然就会被第二次处理。讲到这里我们将postProcessBeforeInstantiation注释去除,在运行一遍看看结果有什么不同
图12. 去除postProcessBeforeInstantiation注释部分去除运行结果

我们发现只剩下postProcessBeforeInstantiationpostProcessAfterInitialization两个方法的调用(Student构造器也是在postProcessBeforeInstantiation中显式调用的,而不是Spring容器创建对象时默认调用),为什么会出现这种情况,其实我们回到源码看一下处理逻辑就都清楚了
图13. AbstractAutowireCapableBeanFactory的resolveBeforeInstantiation(String, RootBeanDefinition)

上图代码对应图1的标注3,在初始化时Spring会检测是否存在自定义类实现了InstantiationAwareBeanPostProcessor接口,如果存在会将DefaultListableBeanFactory中成员变量hasInstantiationAwareBeanPostProcessors置为true,这里的boolean hasInstantiationAwareBeanPostProcessors()判断通过调用第一个下划线方法
图14. AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsBeforeInstantiation(Class<?>, String)

逻辑很简单,首先获得所有后处理器,判断其中所有实现InstantiationAwareBeanPostProcessor的类,进而调用它的postProcessBeforeInstantiation,最后将我们自己实现方法的返回值返回。resolveBeforeInstantiation根据是否返回对象(代理)判断是否调用applyBeanPostProcessorsAfterInitialization(RootBeanDefinition, String)
图15. AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsAfterInitialization(Object, String)

方法中调用了BeanPostProcessorpostProcessAfterInitialization(Object, String),再看看图1的总流程,如果bean != null就直接返回,联想上面的例子bean != null实际上就是我们生成代理的情况,那就是说如果有代理直接返回代理对象,且只会调用InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiationBeanPostProcessor#postProcessAfterInitialization,所表现出来的结果正是例子图12的样子,既然调了包返回了代理那就没有必要再创建原始的目标类了,否则进入图1标注4创建目标类实例的代码,代码清单2

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
  // Instantiate the bean.
  BeanWrapper instanceWrapper = null;
  if (mbd.isSingleton()) {
    //    (1)
    instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  }
  if (instanceWrapper == null) {
    //    (2)
    instanceWrapper = createBeanInstance(beanName, mbd, args);
  }
  final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
  Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);

  // Allow post-processors to modify the merged bean definition.
  synchronized (mbd.postProcessingLock) {
    if (!mbd.postProcessed) {
      //    (3)
      applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
      mbd.postProcessed = true;
    }
  }

  // Eagerly cache singletons to be able to resolve circular references
  // even when triggered by lifecycle interfaces like BeanFactoryAware.
  //    (4)
  boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
      isSingletonCurrentlyInCreation(beanName));
  if (earlySingletonExposure) {
    if (logger.isDebugEnabled()) {
      logger.debug("Eagerly caching bean '" + beanName +
          "' to allow for resolving potential circular references");
    }
    //    (5)
    addSingletonFactory(beanName, new ObjectFactory<Object>() {
      public Object getObject() throws BeansException {
        return getEarlyBeanReference(beanName, mbd, bean);
      }
    });
  }

  // Initialize the bean instance.
  Object exposedObject = bean;
  try {
    //    (6)
    populateBean(beanName, mbd, instanceWrapper);
    if (exposedObject != null) {
      //    (7)
      exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
  }
  catch (Throwable ex) {
    if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
      throw (BeanCreationException) ex;
    }
    else {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
    }
  }
  //    (8)
  if (earlySingletonExposure) {
    Object earlySingletonReference = getSingleton(beanName, false);
    if (earlySingletonReference != null) {
      if (exposedObject == bean) {
        exposedObject = earlySingletonReference;
      }
      else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
        String[] dependentBeans = getDependentBeans(beanName);
        Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
        for (String dependentBean : dependentBeans) {
          if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
            actualDependentBeans.add(dependentBean);
          }
        }
        if (!actualDependentBeans.isEmpty()) {
          throw new BeanCurrentlyInCreationException(beanName,
              "Bean with name '" + beanName + "' has been injected into other beans [" +
              StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
              "] in its raw version as part of a circular reference, but has eventually been " +
              "wrapped. This means that said other beans do not use the final version of the " +
              "bean. This is often the result of over-eager type matching - consider using " +
              "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
        }
      }
    }
  }

  // Register bean as disposable.
  try {
    //    (9)
    registerDisposableBeanIfNecessary(beanName, bean, mbd);
  }
  catch (BeanDefinitionValidationException ex) {
    throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
  }

  return exposedObject;
}

Spring首先创建了一个bean的包装类,按照官方文档上的说法提供该类的好处有:1.提供了设置单个和成块属性值的功能,可以获得属性的descriptors,查询属性是否可读可写;2.支持属性的无限嵌套;2.具有不改变目标类代码情况下,添加PropertyChangeListenersVetoableChangeListeners功能等。还想深入了解该类的读者可以看Bean manipulation and the BeanWrapper。如果缓存中已经存在我们后续要操作的BeanWrapper,直接移除取出做后续处理如标注1,如果暂时不存在该对象标注2处代码进行创建,此处是getBean核心逻辑之一,Spring根据创建对象方式的不同又在该方法内部分为三部分:1.根据factory-method工厂方法进行创建;2.匹配一个或多个参数的有参构造器创建;3.默认无参进行实例化。后续会用专门的文章对这三种方式的创建流程进行分析。
到目前为止我们已经遇过两个后处理器相关的接口,一个是BeanPostProcessor,另一个是它的子接口InstantiationAwareBeanPostProcessor,标注3又会引出一个BeanPostProcessor的子接口MergedBeanDefinitionPostProcessor,除了父接口的两个方法外还需实现该接口的类给出postProcessMergedBeanDefinition(RootBeanDefinition, Class<?>, String),前面我们说过如果<bean>存在父子关系,Spring在解析之后会将父子关系的<bean>映射成为RootBeanDefinition,那么该后处理器及待实现方法就是在做RootBeanDefinition的后处理工作
标注4根据:1.是否单例;2.是否允许循环引用标志;3.beanName对应的bean是否正在创建中三个条件决定是否允许“早期单例暴露”。同样的,我们说过Spring中部分解决循环依赖的第三级缓存正是标注5的代码,如果判断允许“早期单例暴露”就将创建该单例对象的ObjectFactory存入三级缓存singletonFactories。当要获得单例对象时Spring会调用自定义ObjectFactorygetObject(),其实就是这里的getEarlyBeanReference(String, RootBeanDefinition, Object)

图16. 获得早期单例对象引用getEarlyBeanReference(String, RootBeanDefinition, Object)

这里又出现一个新的后处理器SmartInstantiationAwareBeanPostProcessor,它继承自InstantiationAwareBeanPostProcessor,新提供了三个待实现方法:1.Class<?> predictBeanType(Class, String)用于预测返回beanClass类型,一般在隐式初始化时调用;2.Constructor<?>[] determineCandidateConstructors(Class<?>, String)存在多个构造器时用于决定使用哪个进行类的初始化,在上面说过的通过有参构造器创建对象时调用;3.Object getEarlyBeanReference(Object, String)用于获得早期暴露的单例对象引用,和MergedBeanDefinitionPostProcessor类似,一般不需要我们人为扩展,而是Spring内部自己使用。标注6主要是对BeanWrapper进行属性的填充,代码清单3

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
        PropertyValues pvs = mbd.getPropertyValues();
                //    (1)
        if (bw == null) {
            if (!pvs.isEmpty()) {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
            }
            else {
                // Skip property population phase for null instance.
                return;
            }
        }

        // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
        // state of the bean before properties are set. This can be used, for example,
        // to support styles of field injection.
        boolean continueWithPropertyPopulation = true;
                //      (2)
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                        continueWithPropertyPopulation = false;
                        break;
                    }
                }
            }
        }

        if (!continueWithPropertyPopulation) {
            return;
        }
                //      (3)
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
                mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

            // Add property values based on autowire by name if applicable.
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }

            // Add property values based on autowire by type if applicable.
            if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }

            pvs = newPvs;
        }

        boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
        boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
                //      (4)
        if (hasInstAwareBpps || needsDepCheck) {
            PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            if (hasInstAwareBpps) {
                for (BeanPostProcessor bp : getBeanPostProcessors()) {
                    if (bp instanceof InstantiationAwareBeanPostProcessor) {
                        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                        pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                        if (pvs == null) {
                            return;
                        }
                    }
                }
            }
            if (needsDepCheck) {
                checkDependencies(beanName, mbd, filteredPds, pvs);
            }
        }
                //      (5)
        applyPropertyValues(beanName, mbd, bw, pvs);
    }

Spring初始化解析XML配置文件时,所有的<property>会放入PropertyValue的集合中,集合又被封装入MutablePropertyValues对象中。标注1的判断逻辑是,如果不存在是<bean>对应实体BeanWrapper但还存在解析出的属性,肯定出现问题了,抛出异常;如果不存在实体也不存在属性,那也没有继续往下解析的必要了,直接返回。标注2又一次用到了上面讲过的InstantiationAwareBeanPostProcessor,如果存在实现该接口后处理器,则调用boolean postProcessAfterInstantiation(Object, String),进行对象实例创建后的处理,这里依然存在一个短路操作,如果方法返回false,continueWithPropertyPopulation为假,如果该值非真,也无需进行后处理器对于属性处理等后续操作了。标注3很明显涉及按名称、按类型两种属性注入方式,后续会有专门文章分析注入原理,这里不做阐述。标注4根据是否有InstantiationAwareBeanPostProcessor或依赖检查标识判断是否进行属性的“调包”处理,处理的过程就是InstantiationAwareBeanPostProcessor自有方法的最后一个PropertyValues postProcessPropertyValues(PropertyValues, PropertyDescriptor[], Object, String)。到这里Spring得到了所有需要填充的属性(两种方式注入的、后处理器调包的),最后调用标注5处代码真正将属性设置进BeanWrapper对象中,代码清单4

    protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
        if (pvs == null || pvs.isEmpty()) {
            return;
        }

        MutablePropertyValues mpvs = null;
        List<PropertyValue> original;

        if (System.getSecurityManager() != null) {
            if (bw instanceof BeanWrapperImpl) {
                ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
            }
        }
                //    (1)
        if (pvs instanceof MutablePropertyValues) {
            mpvs = (MutablePropertyValues) pvs;
            if (mpvs.isConverted()) {
                // Shortcut: use the pre-converted values as-is.
                try {
                    bw.setPropertyValues(mpvs);
                    return;
                }
                catch (BeansException ex) {
                    throw new BeanCreationException(
                            mbd.getResourceDescription(), beanName, "Error setting property values", ex);
                }
            }
            original = mpvs.getPropertyValueList();
        }
        else {
            original = Arrays.asList(pvs.getPropertyValues());
        }
                //      (2)
        TypeConverter converter = getCustomTypeConverter();
        if (converter == null) {
            converter = bw;
        }
        BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
                //      (3)
        // Create a deep copy, resolving any references for values.
        List<PropertyValue> deepCopy = new ArrayList<PropertyValue>(original.size());
        boolean resolveNecessary = false;
        for (PropertyValue pv : original) {
            if (pv.isConverted()) {
                deepCopy.add(pv);
            }
            else {
                String propertyName = pv.getName();
                Object originalValue = pv.getValue();
                                //    (4)
                Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
                Object convertedValue = resolvedValue;
                boolean convertible = bw.isWritableProperty(propertyName) &&
                        !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
                if (convertible) {
                                        //    (5)
                    convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
                }
                //     (6)
                if (resolvedValue == originalValue) {
                    if (convertible) {
                        pv.setConvertedValue(convertedValue);
                    }
                    deepCopy.add(pv);
                }
                //    (7)
                else if (convertible && originalValue instanceof TypedStringValue &&
                        !((TypedStringValue) originalValue).isDynamic() &&
                        !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
                    pv.setConvertedValue(convertedValue);
                    deepCopy.add(pv);
                }
                else {
                    resolveNecessary = true;
                    deepCopy.add(new PropertyValue(pv, convertedValue));
                }
            }
        }
        //    (8)
        if (mpvs != null && !resolveNecessary) {
            mpvs.setConverted();
        }

        // Set our (possibly massaged) deep copy.
        try {
            //    (9)
            bw.setPropertyValues(new MutablePropertyValues(deepCopy));
        }
        catch (BeansException ex) {
            throw new BeanCreationException(
                    mbd.getResourceDescription(), beanName, "Error setting property values", ex);
        }
    }

标注1首先判断所有属性整体是否都被转换,如果是就不需要下面的步骤,直接赋值返回;如果并不是所有属性已经转换过,就会首先按标注2获取用户自定义的类型转换器。标注3创建了新集合用于存放转换后的结果,之后遍历每一个属性并依次判断转换与否,没有转换则标注4根据上面创建的解析器valueResolver对该值进行解析,我们知道除了最普通的String外Spring支持多种形式的属性配置,该方法中就包含了几乎所有类型属性的解析流程,这里给出一张表格涉及所有解析的类型已经该类型在读取XML时对应的封装类型

真实类型 封装类型
Array ManagedArray
List ManagedList
Set ManagedSet
Map ManagedMap
Bean BeanDefinitionHolder
ref引用Bean RuntimeBeanReference
idref RuntimeBeanNameReference
Properties ManagedProperties
String TypedStringValue
SPEL表达式 TypedStringValue

如果判断该属性是可转换的convertible = true,就会开始标注5的自定义转换器的转换,如果原始值originalValue与经过Spring内置解析器解析后的值resolvedValue相同,且可转换标识convertible为真说明必定经过了自定义转换的步骤(不管是否真的转换过),此时用最新的convertedValue替换老值,如果标识6所示。实际上我觉得标注6和7具有相同的目的,只要convertible为真就说明最新的值必然为convertedValue,那也必然要更新属性封装对象对应字段了。标注8将属性对象设为整体已转化,与方法一开始根据此判断直接返回还是开始转换相呼应。标注9终于将解析后数组封装入MutablePropertyValues,再塞入BeanWrapper中,至此创建对象中的属性填充流程分析完毕
接着代码清单2分析看标注7,“初始化”Bean

图17. AbstractAutowireCapableBeanFactory的initializeBean(String, Object, RootBeanDefinition)

该方法严格意义上来说并不是做了Bean的初始化,因为此时参数的bean已经是实例化完成的对象了,只不过该对象还在Spring内部,并没有暴露给用户,方法内主要做了三件事:1.感知类对象相关属性的设置;2.后处理器方法的处理;3.init-method属性的处理。我们一个个来看,如果bean实现了BeanNameAwareBeanClassLoaderAwareBeanFactoryAware三种Aware接口中的一个或多个,则调用对应的实现方法,虽然大家可能对这三者不太熟悉,另一个Aware接口ApplicatonContextAware相信大家都用过,该接口的setApplicationContext(ApplicationContext)能让我们得到Spring的上下文对象,ApplicatonContextAware和这里的三个接口处理方式如出一辙,只是触发时机不同,前者是在bean后处理阶段触发,后者则是在这里的Spring容器生命周期初始阶段
红线2又碰到bean后处理器了,后处理器有多种变体,上面提到过InstantiationAwareBeanPostProcessorSmartInstantiationAwareBeanPostProcessor,并且在Spring解析之IoC:bean的加载(一)中也用例子的形式详解了BeanPostProcessor的用法,有了前面的基础这里后处理器也就非常好理解了,标注2和4分别对应了BeanPostProcessorObject postProcessBeforeInitialization(Object, String)Object postProcessAfterInitialization(Object, String),方法内会遍历所有实现BeanPostProcessor接口的类调用每个类的上述两个方法。同样的在Spring解析之IoC:bean的加载(一)BeanPostProcessor例子中我们曾经说过后处理器的before/after是相对于实现了InitializingBean接口afterPropertiesSet()方法的执行时刻来说的,那么看图中标注3不是正处在前后方法中间吗?我们有理由猜测invokeInitMethods(String, Object, RootBeanDefinition)就是对于afterPropertiesSet()的处理
图18. AbstractAutowireCapableBeanFactory的invokeInitMethods(String, Object, RootBeanDefinition)

横线代码验证了我们的猜测,但是我们发现除了调用afterPropertiesSet()外还有一处红框内的逻辑没有想到。之前说过Spring提供了三对在bean初始化和销毁时进行自定义操作的手段:1.@PostConstruct/@PreDestory;2.<bean>init-method/destroy-method属性;3.实现InitializingBean/DisposableBean接口,这段代码就是针对第二种情况进行的处理,方法invokeCustomInitMethod(String, Object, RootBeanDefinition)内部做了一些权限控制后得到配置init-methodMethod对象,最终通过反射调用该方法
初始化bean方法分析完毕,我们回到图17从宏观看看各个流程的步骤,其实这就是Spring容器中bean生命周期那张图的执行流程
图19. Spring中Bean的生命周期

  1. Instantiate对应代码清单2标注2instanceWrapper = createBean(beanName, mbd, args);第一次创建bean
  2. Popluate properties对应代码清单3整个属性填充过程
  3. BeanNameAware's setBeanName()对应图17invokeAwareMethods(String, Object)处理第一个BeanNameAware流程
  4. BeanFactoryAware's setBeanFactory()对应图17invokeAwareMethods(String, Object)处理第三个BeanFactoryAware流程
  5. Pre-initializatioin beanPostProcessors对应图17标注2代码applyBeanPostProcessorsBeforeInitialization(Object, String)
  6. InitializingBean's afterPropertiesSet()对应图18红线处逻辑
  7. Call custom init-method对应图18红框内代码
  8. Post-initialization beanPostProcessors对应图17标注4代码applyBeanPostProcessorsAfterInitialization(Object, String)

生命周期中的最后两个状态虽然现在没有遇到,但大家看名字就很明显9与6InitializingBean接口对应,一个初始化一个销毁;10destroy-method属性和7init-method对应。记得刚学习Spring时,这个生命周期是永远记不住的知识点,但整个源码分析下来就清清爽爽了
回到主线代码清单2,标注8处代码看上去一大堆,实际这段被执行到的可能性很小,earlySingleton = true会调用我们曾今说的Object getSingleton(String, boolean),我们将方法放在现在的“语境”中再分析一下

图20. 特定场景下的Object getSingleton(String, boolean)执行流程图

读者需要知道的是,这段代码在new ClassPathXmlApplicationContext()隐式调用和getBean多例对象时才有可能调用,对于多例来说earlySingletonExposure = false,意味着代码不会执行;对于隐式调用allowEarlyReference = false说明红框处内的代码不会执行,再结合代码清单2标注5addSingletonFactory(String, ObjectFactory)可以推得只存在singletonObjects中已有beanName对应对象这一种可能,整个逻辑代码才会往下执行,介于此该流程就不继续往下分析了。最后看下代码清单2标注9
图21. AbstractBeanFactory的registerDisposableBeanIfNecessary(String, Object, RootBeanDefinition)

该方法的主要作用是对实现Disposable接口的对象进行注册,注意,这里只是注册,因为销毁必定是在容器关闭时调用的,而这里所有的一切都是对象初始化流程,所以只能是注册,并且只有单例对象且实现Disposable接口的destroy()或者实现DestructionAwareBeanPostProcessor接口的postProcessBeforeDestruction(Object, String)才会真正注册。注册分为两大块,单例的和自定义scope的。单例对象销毁注册调用registerDisposableBean(String, DisposableBean),将beanName-bean映射放入Map<String, Object> disposableBeans即结束;自定义scope由于需要实现Scope接口,其中就包含这里的registerDestructionCallback(String, Runnable)。至此Spring获取bean逻辑基本讲解完毕,阶段性胜利,哈哈

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容