本文主要是分析的方法是 AbstractAutowireCapableBeanFactory#createBeanInstance,功能是 Spring 具体创建bean的过程。调用如下:

二、createBeanInstance 概述
createBeanInstance 根据方法名就知道,是创建bean的实例,也就注定了这个方法的不平凡。下面就来一步一步的剖析他。
整个方法的源码如下:
我们可以看到:
如果RootBeanDefinition 中存在 Supplier 供应商接口,则使用 Supplier 的回调来创建bean。 Supplier是用来替代声明式指定的工厂。
如果RootBeanDefinition 中存在 factoryMethodName 属性,或者在配置文件中配置了factory-method,Spring会尝试使用 instantiateUsingFactoryMethod 方法,根据RootBeanDefinition 中的配置生成bean实例。需要注意的是,如果一个类中中的方法被 @Bean注解修饰,那么Spring则会将其封装成一个 ConfigurationClassBeanDefinition。此时 factoryMethodName 也被赋值。所以也会调用instantiateUsingFactoryMethod 方法通过反射完成方法的调用,并将结果注入Spring容器中。
当以上两种都没有配置时,Spring则打算通过bean的构造函数来创建bean。首先会判断是否有缓存,即构造函数是否已经被解析过了, 因为一个bean可能会存在多个构造函数,这时候Spring会根据参数列表的来判断使用哪个构造函数进行实例化。但是判断过程比较消耗性能,所以Spring将判断好的构造函数缓存到RootBeanDefinition 中的 resolvedConstructorOrFactoryMethod 属性中。
如果存在缓存,则不需要再次解析筛选构造函数,直接调用 autowireConstructor 或者 instantiateBean 方法创建bean。有参构造调用 autowireConstructor 方法,无参构造调用 instantiateBean 方法。
如果不存在缓存则需要进行解析,这里通过 determineConstructorsFromBeanPostProcessors 方法调用了 SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors 的后处理器方法来进行解析,Spring 默认的实现在AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors 方法中。通过determineCandidateConstructors 方法获取到了候选的构造函数(因为满足条件的构造函数可能不止一个,需要进行进一步的选择)。
获取解析后的候选的构造函数列表 ctors 后(最终的构造函数就从这个列表中选取),开始调用 autowireConstructor 或者 instantiateBean 方法创建bean。在autowireConstructor 中,进行了候选构造函数的选举,选择最合适的构造函数来构建bean,如果缓存已解析的构造函数,则不用选举,直接使用解析好的构造来进行bean的创建。
三、createBeanInstance 详解
需要注意的是,本文分析顺序由于是从代码编写的顺序分析,所以跟实际执行时候的顺序可能并不完全一致。正常的代码流程在第一遍创建bean 时是没有缓存存在的,所以bean在第一遍创建时的执行顺序是 1->2->3->6,当存在缓存的时候则会执行 1->2->3->4->5。需要区别两种执行逻辑。这里建议阅读顺序是 1->2->3->6->4->5

下面我们来分段解析代码。
1. 调用 Supplier 接口 - obtainFromSupplier
这部分的功能 : 若 RootBeanDefinition 中设置了 Supplier 则使用 Supplier 提供的bean替代Spring要生成的bean
关于其应用场景 : 指定一个用于创建bean实例的回调,以替代声明式指定的工厂方法。主要是考虑反射调用目标方法不如直接调用目标方法效率高。
详参 : https://blog.youkuaiyun.com/duxd185120/article/details/109224025
2. 使用 factory-method 属性 - instantiateUsingFactoryMethod
如果RootBeanDefinition 中存在 factoryMethodName 属性,或者在配置文件中配置了factory-method,Spring会尝试使用 instantiateUsingFactoryMethod 方法,根据RootBeanDefinition 中的配置生成bean实例。简单来说,就是如果BeanDefinition 指定了工厂方法,则使用其指定的工厂方法来初始化bean
这个源码太长,也并不重要,就不在这里展示了。简单来说,这里可以分为两种情况 :
在 xml配置中,可以使用 factory-bean 和 factory-method 两个标签可以指定一个类中的方法,Spring会将这个指定的方法的返回值作为bean返回(如果方法是静态方法,则可以不创建factorybean就直接调用,否则需要先将factorybean注入到Spring中)。
对@Bean 注解的解析。在 ConfigurationClassPostProcessor 后处理器中,会对被 @Bean 注解修饰的方法进行解析,生成一个 ConfigurationClassBeanDefinition的 BeanDefinition。此时BeanDefinition 的 factoryMethodName 正是 @Bean修饰的方法本身。所以这里会调用 instantiateUsingFactoryMethod 方法。通过回调的方式调用 @Bean修饰的方法。并将返回结果注入到Spring容器中。
3. 构造函数的缓存判断
到达这一步,Spring则打算通过bean的构造函数来创建bean。但是一个bean可能会存在多个构造函数,这时候Spring会根据参数列表的来判断使用哪个构造函数进行实例化。但是判断过程比较消耗性能,所以Spring将判断好的构造函数缓存到RootBeanDefinition 中,如果不存在缓存则进行构造函数的筛选并缓存解析结果。RootBeanDefinition 中具体的缓存属性有如下几个:
RootBeanDefinition 中主要使用如下几个属性缓存 :
resolvedConstructorOrFactoryMethod : 用于缓存已解析的构造函数或工厂方法
constructorArgumentsResolved :这个字段有两层含义: 一,标记构造函数是否已经完成解析。二,标志这个bean的加载是否 需要通过构造注入(autowireConstructor) 的方式加载。因为只有在 autowireConstructor 方法中才会将其置为 true。
resolvedConstructorArguments : 缓存解析好的构造函数的入参
下面我们来看详细代码:
4. 带有参数的构造函数实例化 - autowireConstructor
上面的代码我们可以看到,Bean的的创建,分为有参构造函数和无参构造函数两种方式进行创建,对于有参构造函数,使用的就是该方法进行处理。这个代码量非常巨大,实现的功能实现上比较复杂,功能上却可以一句话讲清,简单来说,就是根据传入的参数列表,来匹配到合适的构造函数进行bean 的创建。
autowireConstructor 的代码比较复杂,详细代码如下:
简单理一下上面的逻辑:
首先判断 explicitArgs 是否为空,如果不为空,则就直接使用 explicitArgs 作为构造函数的参数。
explicitArgs 所代表的意思是 调用getBean方法是的传参,代码中可以看到,如果 explicitArgs 不为空,那么并未从缓存中获取构造函数(个人猜测 getBean 方法调用参数并不一致,可能缓存并不适用) 如下:

如果 explicitArgs 为空,则尝试从之缓存中获取,也即是从 RootBeanDefinition 的resolvedConstructorArguments 属性或 preparedConstructorArguments 属性中获取。resolvedConstructorArguments 代表完全解析好的参数, preparedConstructorArguments 代表尚未完全解析的参数,如果 获取到 preparedConstructorArguments ,则需要在进一步的解析。
如果缓存中也没有获取到,则只能自己开始分析来获取候选构造函数列表,关于候选构造函数的信息,在调用该方法时就已经传递了过来,即Constructor<?>[] chosenCtors,如果 Constructor<?>[] chosenCtors 为null,则通过反射获取候选构造函数列表 candidates
获取到候选构造函数列表 candidates后,则会优先判断获取到的 candidates 是否只有一个构造函数,如果只要一个,则不需要解析,直接保存相关信息即解析完毕。
否则则进行候选构造函数列表candidates的选举,寻找最合适的构造函数,对 candidates 按照 public 构造函数优先参数数量降序,非public构造函数参数数量降序 规则排序,目的是为了后面检索的时候可以更快速判断是否有合适的构造函数。
排序结束后 ,开始遍历构造函数,按照 构造函数的参数类型和数量与构造函数一一匹配,寻找差异性最小的构造函数作为最终的构造函数并通过 cglib 或者 反射来 创建bean。
按照功能划分,整个 autowireConstructor 方法可以划分为四步:
解析构造函数参数
获取候选的构造函数列表
解析构造函数参数个数
寻找最匹配的构造函数
下面分段解析:
4.1 解析构造函数参数
上面的逻辑还是很清楚的
如果有传入参数 explicitArgs,则直接使用 explicitArgs
如果没有传入,尝试从缓存中获取
如果参数完全解析了,则直接使用,如果没有则调用 resolvePreparedArguments 进行解析
这里解释一下 resolvePreparedArguments 方法的作用。
我们声明的构造函数的可能是这样的
但是我们在配置的时候xml配置文件却是这样的。
这时候,Spring就需要有一个过程,从Spring 的 “666” 到 Integer 的 666 的转变,这个方法就是做类型转化的工作。但需要注意调用这个方法的前提条件是 argsToResolve != null。
4.2 获取候选的构造函数列表
这个逻辑也是比较清楚的 chosenCtors 是传入的构造函数列表
外部是否传入了候选构造函数列表( chosenCtors == null)
如果没传入(chosenCtors 为null),通过反射获取构造函数列表
如果构造函数只有一个 & getBean 没有传参 & 构造参数无参,则直接使用这唯一一个构造函数并返回
这里需要注意点是 传入的 chosenCtors ,在不同的调用场景下可能会传入null,或者 调用 SmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors 方法返回的值。Spring 默认的实现是在 AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors 方法中。
4.3 解析构造函数参数个数
在 Spring 中指定的构造函数会保存在 RootBeanDefinition.constructorArgumentValues 中,类型为 ConstructorArgumentValues,如下。可以看到 ConstructorArgumentValues 分为两部分保存参数。
如下的定义中,
constructorDemoB、666就被保存到 indexedArgumentValues 中; 999 就被保存到genericArgumentValues ,如下图所示:

但是需要注意的是: 这里面保存的是ValueHolder 类型,里面保存的也并不是 实际类型,而是未经转换的类型,即constructorDemoB 保存的并不是 ConstructorDemoB类 实例,而是保存了一个 beanName 为 constructorDemoB。这里的 666 保存的也是字符串形式(而实际的构造函数需要的是Integer形式)。总的来说就是 mbd.getConstructorArgumentValues(); 中的构造函数值并不一定是真正可以使用的类型,还需要进行一个解析进行类型的匹配。
而这个解析过程就发生在 resolveConstructorArguments 方法中。如下:
4.4 寻找最匹配的构造函数
到这里,我们可以知道 minNrOfArgs 为最终构造函数入参数量;candidates 是供选择的构造函数列表。
代码比较长,上面已经贴出了完整版,这里就简化一下。
这一步的目的就是根据参数数量和参数列表来选择最合适的构造函数,并且调用 instantiate(beanName, mbd, constructorToUse, argsToUse) 方法来创建bean实例。
下面提三点:
由于在配置文件中声明bean不仅仅可以使用参数位置索引的方式创建,也支持通过参数名称设定参数值的情况,如下:
所以这时候,就需要首先确定构造函数中的参数名称。而获取参数名的方式有两种,一种是通过注解直接获取(@ConstructorProperties 注解获取),即上面代码中对应的 ConstructorPropertiesChecker.evaluate(candidate, parameterCount);,另一种是通过Spring同构的工具类 ParameterNameDiscoverer ,这个在代码中也有使用。
完成这一步的时候,构造函数、参数名称、参数类型、参数值都确定后就可以锁定构造函数以及转换对应的参数类型了。
instantiate 方法也很简单,根据 beanFactory 中的 bean实例化策略来实例化对象
注:关于 实例化策略,主要两种 SimpleInstantiationStrategy 和 CglibSubclassingInstantiationStrategy,简单实例化策略(直接反射) 和 Cglib 动态代理策略(通过cglib 代理),默认第二种。
在这里将解析后的内容添加到缓存中的代码如下:
5. 无参构造函数实例化 - instantiateBean
相较于上面的有参构造函数,无参构造函数的解析显的那么简单
可以看到,最关键的调用代码是在InstantiationStrategy#instantiate(org.springframework.beans.factory.support.RootBeanDefinition, java.lang.String, org.springframework.beans.factory.BeanFactory) 中,其细代码如下:
getDeclaredConstructor(Class<?>... parameterTypes) : 这种方法会返回制定參数类型的全部构造器,包含public的和非public的,当然也包含private的。
getDeclaredConstructors() : 的返回结果就没有參数类型的过滤了。
getConstructor(Class<?>... parameterTypes) : 这种方法返回的是上面那个方法返回结果的子集。仅仅返回制定參数类型訪问权限是public的构造器。
getConstructors() : 的返回结果相同也没有參数类型的过滤。
这里的逻辑非常简单, 甚至可以用一句话概括 : 是否有方法被覆盖(是否使用replace 或 lookup 进行配置),有则使用cglib动态代理,增强方法,否则直接通过反射创建。这一块判断是否方法被重写,不是为了事务或者aop,因为解析还没到那一步,这里是为了 lookup-method 和 replaced-method
6. 构造函数的筛选
在bean第一次创建的时候,并不存在构造缓存,所以会执行下面的代码。也就是说,这里是Bean第一次创建所调用的代码。
这里可以看到,如果没有进行任何额外的配置的话,会使用默认的方式(instantiateBean(beanName, mbd))创建bean。
我们这里整理一下调用 autowireConstructor 方法的条件(以下条件满足其一即可):
ctors != null : determineConstructorsFromBeanPostProcessors 方法筛选出的候选构造函数不为null。对于默认的Spring来说,实际返回的是 AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors 方法的返回值。而其作用简单来说就是返回被 @Autowired 注解修饰的构造函数(实际要更为复杂,这里可以简单这样理解),如下:
mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR :这里的配置是通过xml 来配置bean时指定的装配方式,如果指定了构造装配,则调用 autowireConstructor 方法。如下:
mbd.hasConstructorArgumentValues() :这里表示是否给当前bean定义了构造函数入参。通过xml配置bean的时候可以通过 <constructor-arg> 标签指定构造函数入参。如下:

!ObjectUtils.isEmpty(args) : 在通过getBean方法以编程方式传递的参数值args。如果有,使用其作为bean创建时构造函数的参数。
mbd.getPreferredConstructors() != null : RootBeanDefinition 的实现是 null 。这里仅在 GenericApplicationContext.ClassDerivedBeanDefinition 中有过解析。
关于determineConstructorsFromBeanPostProcessors 方法,实际返回的是 AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors 方法的返回值。关于 AutowiredAnnotationBeanPostProcessor 的解析详参:Spring源码分析衍生篇五:AutowiredAnnotationBeanPostProcessor
四、 总结
AbstractAutowireCapableBeanFactory#createBeanInstance方法处于Spring 创建bean 的入口阶段,完成了bean 的初步创建,调用各种扩展接口来尝试完成bean的创建(Supplier、factory-method),失败了则根据传入参数和和构造函数列表来选择合适的构造函数来创建bean。
但是并未完成属性注入、接口特性实现(如 Aware)、标签设置(如inti-method)的设置。在后续的 AbstractAutowireCapableBeanFactory#populateBean 方法中完成了属性的注入。
需要注意的是,当一个bean第一次被解析时判断调用 autowireConstructor 方法来进行创建的时候,那么后面再次解析该bean仍会通过 autowireConstructor 方法进行解析。因为 autowireConstructor 方法中在添加缓存的时候将 constructorArgumentsResolved 置为true来确保下一次解析时仍调用 autowireConstructor 方法

被折叠的 条评论
为什么被折叠?



