往期文章:
上一篇文章讲到了ConfigurationClassPostProcessor的具体逻辑,至此所有的配置信息已经被解析成BeanDefinition,注册到容器中了。
下一步就是根据BeanDefinition进行非懒加载的单例bean的实例化。因此,本篇文件就要介绍非懒加载单例bean的实例化过程。
要研究bean的实例化,首先就要找到bean的实例化的入口。
这个createBeanInstance方法,就是我们这篇文件研究的主体。
bean的实例化有两种方式,一种是工厂方法实例化,一种是构造器实例化。
createBeanInstance方法,首先会检查BeanDefinition中的factoryMethodName属性是否不为空,如果不为空,就会使用工厂方法进行实例化。
我们上一篇文章分析的@Bean注解修饰的方法,生成的BeanDefinition就带有factoryMethodName属性。
如果BeanDefinition中的factoryMethodName属性为空,就要通过构造器进行实例化。
构造器实例化,先要进行构造器的挑选。
如果要通过构造器进行实例化的话,就要先挑选一个合适的构造器,因为一个类里面构造器可能有多个。
推断构造:
初次筛选的条件如下,符合条件的构造器,会放到一个数组里面返回。
二次筛选就是从初次筛选的结果中,再筛出一个最合适的。
这里主要就是筛选出要使用的构造器,以及构造器要使用的参数,筛选出的构造器会保存到constructorToUse变量,而构造器要使用的参数则保存到argsToUse变量。
上面这张图就是推断构造二次筛选的流程,比较复杂,这里看不懂的可以忽略,非重点。
经过上面的步骤,现在已经挑选出合适的构造器,或者使用无参构造器。接下来就是要通过构造器,进行反射实例化。
Spring通过InstantiationStrategy(实例化策略),进行bean实例化的。
也就是说,不是直接通过构造器进行反射实例化的,而是再包了一层,提供了一定的可扩展性。我们可以实现自己的InstantiationStrategy,在bean实例化的时候做一些特殊处理。
如果我们没有做特殊处理,那么Spring就会使用SimpleInstantiationStrategy(简单实例化策略),里面才是通过构造器进行反射实例化。
然后会把实例化的bean包装在一个BeanWrapper中返回。
上面已经对bean实例化的整个过程介绍完毕,下面是通过代码走读对上面的原理分析进行验证。
首先是在代码中找到bean实例化的入口
验证上面的路径一路跟进去,就到了实例化bean的入口,createBeanInstance方法。
进入createBeanInstance方法内部,可以看到推断构造和实例化的流程。
判断BeanDefinition的factoryMethodName属性是否不为空,如果不为空,则使用工厂方法进行实例化。
如果我们没有主动传参,并且BeanDefinition中的resolvedConstructorOrFactoryMethod为true,resolved变量置为true,表示之前已经处理过,下面不需要再进行推断构造的处理。autowireNecessary变量表示是否需要进行构造器的自动注入,也就是是否是一个有参构造。
之前已经处理过,就直接进行实例化,不需要再次进行构造器的推断。上面是带参构造的实例化,下面是无参构造的实例化。
如果之前没有处理过,就要进行推断构造。determineConstructorsFromBeanPostProcessors方法就是推断构造的初次筛选,返回一个构造器数组。
初次筛选返回构造器数组后,如果构造器数组不为空,或者当前BeanDefinition的自动注入模型是构造器注入,或者我们在配置中指定了构造器参数,或者我们主动传递了参数,只有满足这四个条件中的一个,就会调用autowireConstructor方法,里面就包含了二次筛选的逻辑。
四个条件没一个满足,就走最简单的逻辑,使用无参构造器进行实例化。
再来看一下推断构造的代码。
determineConstructorsFromBeanPostProcessors方法,就是初次筛选的逻辑。
推断构造的初次筛选,是通过bean后置处理器进行的。如果我们打断点的话,会发现进入到AutowiredAnnotationBeanPostProcessor里面。
如果构造器带有@Autowired注解,但是之前已经有一个@Autowired修饰的构造器,那么直接报错。
如果构造器上的@Autowired注解的required属性是true(默认就是true),然后又有其他构造器,也直接报错。
都没有报错,代表目前为止只有为一个并且是@Autowired注解修饰的构造器,赋值给requiredConstructor遍历,并且添加到candidates中。
如果构造器没有@Autowired注解,并且是无参构造器,赋值给defaultConstructor。
然后所有构造器都遍历完毕后,进入下面的逻辑。
如果candidates集合不为空,requiredConstructor为空,defaultConstructor不为空,把defaultConstructor添加到candidates集合。也就是没有@Autowired或者@Autowired(required = true)注解修饰的构造器,只有不带@Autowired注解的构造器或者@Autowired(required = false)注解修饰的构造器,那么把无参构造器添加到candidates集合中,作为返回集。
下面两个else if分支必须primaryConstructor不为null才会进去,但是primaryConstructor是不可能不为null的,所以直接忽略。
autowireConstructor方法里面,包含了二次筛选的逻辑,也就是从构造器数组中,挑出一个最合适的构造器。
然后里面会new一个ConstructorResolver并调用它的autowireConstructor方法。
两个重要变量,constructorToUse(准备使用的构造器)和argsToUse(准备使用的参数)。
如果我们传递了参数,argsToUse赋值为我们传递的参数。
如果缓存中没有,看是否只有一个构造器,并且我们没有传递参数,而且配置中也没有指定构造参数。如果是,那么把无参构造和空参缓存到BeanDefinition中,然后通过无参构造器进行实例化。
minNrOfArgs,最少需要的参数个数。如果我们传递了参数,就是我们传递的参数个数,如果我们没有传递参数,那么看我们是否在配置中指定了构造器参数,如果指定了,那么就是我们指定的构造器参数个数,否则就是0。
遍历构造器,如果constructorToUse(准备使用的构造器)和argsToUse(准备使用的参数)不为空,而且当前遍历到的构造器参数个数parameterCount少于argsToUse的长度,那么直接break,后面的都不用再遍历了。
如果当前构造器参数个数parameterCount少于minNrOfArgs(最少需要的参数个数),那么continue忽略当前构造器。
计算取得的参数个数和构造器所需要的参数个数的差值typeDiffWeight,如果差值比之前的还小,就选择当前的构造器,更新constructorToUse和argsToUse,以及minTypeDiffWeight(目前找到的最小参数个数差值)。
如果我们没有传递参数,则把处理结果缓存到BeanDefinition中。然后使用推断出的构造器constructorToUse和构造参数argsToUse进行反射实例化。
坚持就是胜利,剩下的就是实例化bean的逻辑!
instantiate(beanName, mbd, constructorToUse, argsToUse),使用推断出的构造器constructorToUse和构造参数argsToUse进行反射实例化。
strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse),调用了InstantiationStrategy(实例化策略)的instantiate方法。
调用了BeanUtils工具类的instantiateClass方法。
getInstantiationStrategy().instantiate(mbd, beanName, parent)也是通过InstantiationStrategy(实例化策略)的instantiate方法进行实例化。
然后可以看到包装成一个BeanWrapper返回。
getInstantiationStrategy().instantiate(mbd, beanName, parent)里面,也是通过BeanUtils工具类的instantiateClass方法进行实例化。
以上就是推断构造与bean的实例化源码解析的所有内容,下面做一个总结。