创建Bean

介绍了循环依赖以及Spring中的循环依赖的处理方式后,我们继续“准备创建bean”章节的继续学习。当经过resolveBeforeInstantiation方法后,程序有两个选择,如果创建了代理或者说重写了InstantiationAwareBeanPostProcessor的postProcessBeforeInitialization方法并在postProcessBeforeInitialization方法中改变了bean,则直接返回就可以了,否则需要进行常规Bean的创建。而这常规bean的创建就是在doCreateBean中完成的。

尽管日志与异常的内容非常重要,但是在阅读源码的时候似乎大部分人都会忽略,这个不是一个好习惯,我们看看这个doCreateBean的逻辑流程:

1. 如果是单例则需要首先清楚缓存
2. 实例化bean,将BeanDefinition转换为BeanWrapper。

转换是一个复杂的过程,但是我们可以尝试概括大致的功能:

  1. 如果存在工厂方法,则使用工厂方法进行初始化。
  2. 一个类有多个构造函数,每个构造函数都有不同的参数,所以需要根据参数锁定构造函数并进行初始化。
  3. 如果既不存在工厂方法也不存在带有参数的构造函数,则使用默认的构造函数进行bean的实例化。
3. MergedBeanDefinitionPostProcessor的应用。

bean合并后的处理,Autowired注解正是通过此方法实现诸如类型的预解析。

4. 依赖处理

在Spring中会有循环依赖的情况,例如A->B&B->A。如果此时A与B都是单例的,那么在Spring中的处理方式就是当创建B的时候,涉及自动注入A步骤时,并不是直接去再次创建A,而是通过放入缓存的ObjectFactory来创建实例,这样就解决了循环依赖的问题。

5. 属性填充

将所有属性填充至bean的实例中。

6. 循环依赖检查

之前提到过,在Spring中解决循环依赖只对单例有效,而对于prototype的bean,Spring没有好的解决办法,唯一要做的就是抛出异常。在这个步骤中会检测已经加载的Bean是否已经出现了依赖循环,并判断是否需要抛出异常。

7. 注册DisposableBean。

如果配置了destory-method,这里需要注册以便在销毁的时候调用。

8. 完成创建并返回

可以看到上面的步骤非常繁琐,每一步骤都使用了大量的代码来完成其功能,最复杂也是最难以理解的当属循环依赖的处理,在真正进入doCreateBean前我们是有必要先了解这个循环依赖的。

创建Bean的实例

先来看一下createBeanInstance这个函数:

  1. 如果在RootBeanDefinition中存在factoryMethodName属性,或者说在配置文件中配置了factory-method,那么Spring会尝试使用instantiateUsingFactoryMethod方法根据RootBeanDefinition中的配置生成bean的实例。
  2. 解析构造函数并进行构造函数的实例化。因为一个bean对应的类中可能有多个构造函数,而每个构造函数的参数不同,Spring在根据参数及类型去判断最终会使用哪个构造函数进行实例化。但是,判断的过程是个比较消耗性能的步骤,所以采用缓存机制,如果已经解析过就不需要重复解析,直接从BeanDefinition中的resolvedConstructorOrFactoryMethod的缓存去取,否则需要再次解析,并将解析结果添加至这个属性中。

    2.1 autowireConstructor

对于实例的创建spring中分成了两种情况,一种是通用的实例化,另一种是带有参数的实例化。带有参数的实例化过程相当复杂,因为存在者不确定性,所以在判断对应参数上做了大量工作。

看一下ConstructorResolver#autowireConstructor方法,逻辑很复杂,Spring一惯的做法是将复杂的逻辑分解,分成N个小函数的嵌套,每一层都是对下一层逻辑的总结及概要,这样使得每一层的逻辑会变得简单容易理解。在上面的函数中,包含着很多的逻辑实现,不符合这一规范。来理解一下代码:

2.1.1 构造函数参数的确定

2.1.1.1 根据explicitArgs参数判断

如果传入的explicitArgs不为空,那边可以直接确定参数,因为explicitArgs参数是在调用Bean的时候用户指定的,在BeanFactory类中存在这样的方法:getBean。在获取bean的时候,用户不但可以指定bean的名称还可以指定bean所对应的类的构造函数或者工厂方法的方法参数,主要用于静态工厂方法的调用,而这里是需要给定完全匹配的参数的,所以,便可以判断,如果传入的explicitArgs参数不为空,则可以确定构造函数参数就是它。

2.1.1.2 缓存中获取

除此之外,确定参数的办法如果之前已经分析过,也就是说构造函数参数已经记录在缓存中就返回。否则就重新分析,涉及到类型转换的问题。

2.1.1.3 配置文件获取

如果不能根据传入的参数explicitArgs确定构造函数的参数也无法在缓存中得到相关信息,那么只能开始新一轮的分析了。

分析从获取配置文件中配置的构造函数信息开始,经过之前的分析,我们知道,Spring中配置文件中的信息经过转换都会通过BeanDefinition实例承载,也就是参数mbd中包含,那么可以通过mbd.getConstructorArgumentValues来获取配置的构造函数信息。有了配置中的信息便可以获取对应的参数值信息了,获取参数值的信息包括直接指定值,如:直接指定构造函数中某个值为原始类型String类型,或者是一个对其他bean的引用,而这一处理委托给resolveConstructorArguments方法,并返回能够解析到的参数的个数。

2.1.2 构造函数的确定

经过第一步后已经确定了构造函数的参数,接下来的任务就是根据构造函数参数在所有构造函数中锁定对应的构造函数,而匹配的方法就是根据参数的个数匹配,所以在匹配之前需要先对构造函数按照public构造函数优先参数数量降序、非public构造函数参数数量降序,这样可以在遍历的情况下迅速判断排在后面的构造函数参数个数是否符合条件。

由于在配置文件中并不是唯一限制使用参数位置索引的方式去创建,同样还支持指定参数名称进行设定参数值的情况,如, 那么这种情况就需要首先确定构造函数中的参数名称。

获取参数名称可以有两种方式,一种是通过注解的方式直接获取,另一种就是使用Spring中提供的工具类ParameterNameDiscoverer来获取。构造函数、参数名称、参数类型、参数值都确定后就可以锁定构造函数以及转换对应的参数类型了。

2.1.3 根据确定的构造函数转换对应的参数类型

主要是使用Spring中提供的类型转化器或者用户提供的自定义的类型转换器进行转换。

2.1.4 构造函数不确定性的验证

当然,有时候即使构造函数、参数名称、参数类型、参数值都确定后也不一定直接锁定构造函数,不同的构造函数的参数为父子关系,所以Spring在最后又做了一次验证。

2.1.5 根据实例化策略以及得到的构造函数及构造函数参数实例化bean。

2.2 instantiateBean

经历了带有参数的构造函数的实例构造,相信你会非常愉快地理解不带参数的构造函数的实例化过程。

这个方法没有什么实质性的逻辑,带有参数的实例构造中,Spring把精力都放在了构造函数以及参数的配置上,所以如果没有参数的话那将是非常简单的一件事,直接调用实例化策略就好了。

2.3 实例化策略

实例化过程中反复提到过实例化策略,那这有是做什么用的呢?其实经过前面的分析,我们已经得到足以实例化的所有相关信息,完全可以使用最简单的反射方法直接反射来构造实例对象,但是Spring却没有这么做。

我们看一下instantiate这个方法。我们已经感受到了Spring的良苦用心以及为了能更方便地使用Spring而做了大量的工作。程序中,首先判断如果beanDefinition.getMethodOverrides()为空也就是用户没有使用replace或者lookup的配置方法,那么直接使用反射的方式,简单快捷,但是如果使用了这两个特性,在直接使用反射的 方式创建实例就不妥了,因为需要将这两个配置提供的功能切入进去,所以就必须要使用动态代理的方式将包含两个特性所对应的逻辑的拦截增强器设置进去,这样才可以保证在调用方法的时候会被相应的拦截器增强,返回值为包含拦截器的代理实例。

对于拦截器的处理方法非常简单,不再详细介绍。可以查看后面AOP的知识。

记录创建bean的ObjectFactory

AbstractAutowireCapableBeanFactory#doCreateBean

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");
    }
    // 为避免后期循环依赖,可以在Bean初始化完成前将创建实例的ObjectFactory加入工厂。
    addSingletonFactory(beanName, new ObjectFactory<Object>() {
        @Override
        public Object getObject() throws BeansException {
            // 对bean再一次依赖引用,主要应用SmartInstantiationAware BeanPostProcessor,
            // 其中我们熟知的AOP就是在这里将advice动态织入bean中,若没有则直接返回bean,不做任何处理。
            return getEarlyBeanReference(beanName, mbd, bean);
        }
    });
}

这段代码看起来并不复杂,但是很多人不是太理解这段代码的作用,而且,这段代码仅从此函数中去理解也很难弄懂其中的含义,我们需要从全局的角度去思考Spring的依赖解决办法。

  1. earlySingletonExposure:从字面的意思理解就是提早曝光的单例,我们暂不定义它的学名,我们感兴趣的是有哪些条件影响这个值。
  2. mbd.isSingleton:是否是单例
  3. this.allowCircularReferences:是否允许循环依赖,很抱歉,并没有找到在配置文件中如何配置,但是在AbstractRefreshableApplicationContext中提供了设置函数,可以通过硬编码的方式进行设置或者通过自定义的命名空间进行配置。 ClassPathXmlApplicationContext bf = new ClassPathXmlApplicationContext("config.xml"); bf.setAllowBeanDefinitionOverriding(false);
  4. isSingletonCurrentlyInCreation(beanName):该bean是否在创建中,在Spring中,会有个专门的属性默认为DefaultSingletonBeanRegistry的singletonsCurrentlyInCreation来记录bean的加载状态,在bean开始创建前会将beanName记录在属性中,在bean创建结束后,会将beanName从属性中移除。那么我们跟随代码一路走来可是对于这个属性的记录并没有多少印象,这个状态是在哪里记录的呢?不同scope的记录位置并不一样,我们以singleton为例,在singleton下记录属性的函数是在DefaultSingletonBeanRegistry类的public Object getSingleton函数的beforeSingletonCreation和afterSingletonCreation中,在这两段函数中分别使用 this.singletonCurrentlyInCreation.add(beanName)和this.singletonCurrentlyInCreation.remove(beanName)来进行状态的记录和移除。

经过上面的分析我们了解变量earlySingletonExposure是否是单例、是否允许循环依赖、是否对应的bean正在创建的条件的综合。当这3个条件都满足时会执行addSingletonFactory操作,那么加入SingletonFactory的作用是什么呢?又是在什么时候调用呢?

我们还是以最简单AB循环依赖为例,类A中含有类B的属性,而B中又含有属性A。那么在创建A的时候首先会记录A所对应的beanName,并将beanA的创建工厂加入缓存中,而在对A的属性填充也就是调用populate方法的时候又再一次的对B进行递归创建。同样,因为B中存在A属性,因此在实例化B的populate方法又会再次初始化B。而这里再去创建的时候使用的就是ObjectFactory而不是再去实例化。实例化好的放缓存,没有好的可创建ObjectFactory。

// 为避免后期循环依赖,可以在Bean初始化完成前将创建实例的ObjectFactory加入工厂。
addSingletonFactory(beanName, new ObjectFactory<Object>() {
    @Override
    public Object getObject() throws BeansException {
        // 对bean再一次依赖引用,主要应用SmartInstantiationAware BeanPostProcessor,
        // 其中我们熟知的AOP就是在这里将advice动态织入bean中,若没有则直接返回bean,不做任何处理。
        return getEarlyBeanReference(beanName, mbd, bean);
    }
});

在getEarlyBeanReference函数中并没有太多的逻辑处理,或者说除了后处理器的调用外没有别的处理工作,根据以上分析,基本可以理清Spring处理循环依赖的解决办法,在B中创建依赖A时通过ObjectFactory提供的实例化方法来中断A中的属性填充,使B中持有的A仅仅是刚初始化并没有填充任何属性的A,而这正初始化A的步骤还是在最开始创建A的时候进行的,但是因为A与B中的A所表示的属性地址是一样的,所以在A中创建好的属性填充自然可以通过B中的A获取,这样就解决了循环依赖的问题。

属性注入

在了解循环依赖的时候,我们曾经反复提到了populateBean这个函数,也多少了解了这个函数的主要功能就是属性填充,下面我们看看是如何实现填充的。

  1. InstantiationAwareBeanPostProcessor处理器的postProcessAfterInstantiation函数的应用,此函数可以控制程序是否继续进行属性填充。
  2. 根据注入类型(byName或byType),提取依赖的bean,并统一存入PropertyValues中。
  3. 应用InstantiationAwareBeanPostProcessor处理器的postProcessPropertyVaules函数,对属性获取完毕填充前对属性的再次处理,典型应用是RequiredAnnotationBeanPostProcessor类中对属性的验证。
  4. 将所有PropertyValues中的属性填充至BeanWrapper中。

在上面的步骤中有几个地方是我们比较感兴趣的,它们分别是依赖注入以及属性填充,那么,接下来进一步分析这几个功能的实现细节。

autowireByName

上文提到根据注入类型,提取依赖的bean,并统一存入PropertyValues中,那么我们首先了解下byName是怎么实现的!

看一下autowireByName这个方法。如果读者之前了解autowire的是呀方法,相信理解这个函数的功能不会太难,无非是在传入的参数pvs中找出已经加载的bean,并递归实例化,进而加入到pvs中。

autowireByType

autowireByType和autowireByName对于我们理解与使用来说复杂程度都很相似,但是其实实现功能的复杂度却完全不一样。

实现根据名称自动匹配的第一步就是寻找bw中需要依赖注入的属性,同样对于根据类型自动匹配的实现来讲第一步也是寻找bw中需要依赖注入的属性,然后遍历这些属性并寻找类型匹配的bean,其中最复杂的就是寻找类型匹配的bean。同时,Spring中提供了对集合的类型注入的支持,如使用注解的方式:@Autowired private List aList;

Spring将会把所有与Test匹配的类型找出来并注入到tests属性中,正是由于这一因素,所以在autoWireByType函数中,新建了局部遍历autowiredBeanNames,用于存储所有依赖的bean,如果只是对非集合类型的属性注入来说,此属性并无用处。

对于寻找类型匹配的逻辑实现封装在了resolveDependency函数中。

寻找类型匹配的执行顺序时,首先尝试使用解析器进行解析,如果解析器没有成功解析,那么可能是使用了默认的解析器没有做任何处理,或者是使用了自定义的解析器,但是对于集合等类型来说并不在解析的范围内,所以再次对不同类型进行不同情况的处理,虽说对于不同类型处理方式不一致,但是大致的思路还是很相似的,所以函数中只对数组类型进行了详细地注释。

applyPropertyValues

程序运行到这里,已经完成了对所有注入属性的获取,但是获取的属性是以PropertyValues形式存在的,还并没有应用到已经实例化的bean中,这一工作是在applyPropertyValues中。

AbstractAutowireCapableBeanFactory#applyPropertyValues

初始化Bean

大家应该记得在bean配置时bean中有一个init-method的属性,这个属性的作用是在bean实例化之前调用init-method方法来根据用户业务进行相应的实例化。Spring中已经进行了bean的实例化,并且进行了属性的填充,而就在这时将会调用用户设定的初始化方法。

AbstractAutowireCapableBeanFactory#initializeBean

虽说此函数主要目的是进行客户设定的初始化方法的调用,但是除此之外还有些其他必要的工作。

激活Aware方法。

在分析其原理之前,我们先了解一下Aware的使用。Spring中提供了一些Aware相关的接口,比如BeanFactoryAware、ApplicationContextAware、ResourceLoaderAware、ServletContextAware等。实现这些接口的bean在被初始化后,可以取得一些相应的资源,例如实现BeanFactoryAware的bean在初始化之后,Spring容器将会注入BeanFactory的实例,而实现ApplicationContextAware的bean,在被初始化之后,将会被注入ApplicationContext实例等。

处理器的应用

BeanPostProcessor相信大家都不陌生,这是Spring中开放式架构中一个必不可少的亮点,给用户充足的权限去更改或者扩展Spring,而除了BeanPostProcessor外还有很多其他的PostProcessor,当然大部分都是以此为基础,继承自BeanPostProcessor。BeanPostProcessor的使用位置就是这里,在调用客户自定义初始化方法之前以及调用客户自定义初始化方法之后分别会调用BeanPostProcessor的postProcessBeforeInitialization和postProcessAfterInitialization方法,使用户可以根据自己的业务需要进行相应的处理。

激活自定义的init方法

用户自定义的初始化方法除了我们熟知的使用配置init-method之外,还可以使自定义的bean实现InitializingBean接口,并在afterPropertySet中实现自己的初始化业务逻辑。

init-method和afterPropertySet都是在初始化bean时执行,执行顺序是afterPropertySet先执行,init-method后执行。

在AbstractAutowireCapableBeanFactory#invokeInitMethods方法中就实现了这两个步骤的初始化方法的调用。

注册DisposableBean

Spring中不但提供了对于初始化方法的扩展入口,同样也提供了销毁方法的入口,对于销毁方法的扩展,除了我们熟知的配置属性destory-method方法外,用户还可以注册后处理器DestructionAwareBeanPostProcessor来统一处理bean的销毁方法。

在AbstractAutowireCapableBeanFactory#registerDisposableBeanIfNecessary方法中。

results matching ""

    No results matching ""