美文网首页编程学习笔记
Spring解析之IoC:bean的加载(三)

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

作者: 宝之家 | 来源:发表于2018-03-09 16:48 被阅读233次

    前言
    计划赶不上变化,原本年前准备将这个主题告一段落,但各种工作之外的事接踵而至,且剩下的工作量依然超过我的预期,只能拖到现在,前段时间接了商业化广告的活,源码看得多了自己写时总不自觉的按更高的标准要求自己,一遍遍的修改重构,这可能就是所谓的潜移默化吧
    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逻辑基本讲解完毕,阶段性胜利,哈哈

    相关文章

      网友评论

        本文标题:Spring解析之IoC:bean的加载(三)

        本文链接:https://www.haomeiwen.com/subject/qnwizxtx.html