从根儿上学习spring 十 之run方法启动第四段(4)

我们接着上一节已经准备开始分析AbstractAutowireCapableBeanFactory#doCreateBean方法,该方法是spring真正开始创建bean实例并初始化bean的入口方法,属于核心逻辑,所以我们新开一节开始分析。

图12

图12-530到536行

这几行的主要就是创建bean实例,也就是new或者反射创建一个对象出来。beanWrapper大家可以理解成刚创建的bean实例的存放容器,就像刚出生的人类宝宝需要放到一个专业容器中一样,后面用到我们再细说。这几行没什么逻辑,有些逻辑的就是535行的createBeanInstance(beanName, mbd, args)方法。我们来看下该方法的逻辑

图13-AbstractAutowireCapableBeanFactory#createBeanInstance方法

从该方法的注释可知,该方法可以使用合适的策略来创建bean实例,如工厂方法,构造器自动注入,和简单的实例化。

图13-1090到1093行

这几行的意思是如果该beanDefinition中提供了创建bean的Supplier接口,则调用该接口的get方法来创建bean对象。那么什么时候可以向beanDefinition中注册该接口呢?我们还没讲过ConfigurationClassPostProcessor类的逻辑,该类是扫描获取我们需要被spring管理的所有beanDefinition的入口,但是这个阶段spring并没有提供给我们扩展点去修改beanDefinition,似乎只有在我们前面讲beanFactoryProcessor接口的子接口BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法才可以通过registry参数获取到需要操作的beanDefiniction去注册Supplier。

1902行很简单就是调用Supplier接口get方法创建对象,不再多说。

图13-1095到1097行

1095行先判断beanDefinition的factoryMethodName属性是否为空,那么factoryMethodName属性是怎么来的呢?又是干嘛用的呢?

factoryMethodName是方法上加了@Bean注解的方法名。通过该方法spring会通过反射来调用该方法创建bean实例。instantiateUsingFactoryMethod(beanName, mbd, args);方法的具体逻辑就不带大家看了,就是通过beanDefiniton里的factoryBeanName(拥有factoryMethodName的bean名称)获取对应的bean实例,然后获取factoryMethodName方法参数,最终调用反射创建bean实例对象。

图14-AbstractAutowireCapableBeanFactory#createBeanInstance方法

图14-1100到1109行

这几行主要作用是针对prototype类型的bean才有作用,因为这几行是针对bean的多次创建场景,校验之前是否已经解析出需要用的构造器进行创建bean对象。这样就可以把解析出来的构造器缓存起来,不需要每次创建bean实例时都去解析一遍所要用的构造器。

通过1102行可以看出能走缓存是有前提的,就是参数args必须为空,这个args是getBean(String name, Object... args)方法的参数,也就是spring对用户传构造器参数的情况下每次都会走一遍构造器解析,而没有传构造器参数的情况下spring会根据构造器的参数类型自动解析并注入。这种情况下,下次创建时才会走构造器缓存。至于这么设计的原因,我猜测是因为走缓存的情况下spring也会把构造器的参数缓存起来,而用户动态传的参数是没法缓存的毕竟可能每次都不一样,所以针对这种情况才没走构造器缓存逻辑吧。

这里的resolved变量代表的就是能不能走构造器缓存逻辑,如果mbd.resolvedConstructorOrFactoryMethod不为空说明构造器之前已经解析过。这里的autowireNecessary表示是否可以走构造器的自动注入,如果mbd.constructorArgumentsResolved为true表示构造器的参数已经解析过了可以使用自动注入,否则就会调用1115行的instantiateBean(beanName, mbd)方法使用默认构造器创建bean实例。

图14-1120到1125行

这几行就是spring找合适的构造器并且自动为我们自动注入构造器参数,从if判断语句可知,至于能找到构造器且args不为空都会走自动注入逻辑

1125行--前面都不满足的情况下使用默认构造器创建bean对象

图12-537到541行

通过前面我们对createBeanInstance(beanName, mbd, args)方法spring已经为我们把bean的实例给创建出来了,537--541这几行逻辑很简单就是将bean的class类型设置到beanDefinition的resolvedTargetType属性中

图12-544到555行

这几行的核心就是调用了applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName)方法,点进这个方法可知也就是对当前的beanDefinition执行所有的MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition方法。MergedBeanDefinitionPostProcessor接口作为一个扩展接口,允许我们在postProcessMergedBeanDefinition方法中对beanDefinition进行操作。

或许大家会疑惑,这时候bean的实例已经创建出来了还能对beanDefinition做什么操作呢?这就得结合bean的初始化生命周期来看,这时候的bean实例是创建出来了,相当于new出来一个完全新的对象,而对象的属性还没设值,所以这时候对beanDefinition的操作也最多是影响bean的属性初始化。spring内部提供的MergedBeanDefinitionPostProcessor也确实大多都是跟bean的属性设值有关,如果大家有需求在bean实例化后操作beanDefinition也可以实现MergedBeanDefinitionPostProcessor接口。

图15-AbstractAutowireCapableBeanFactory#doCreateBean方法

接下来我们继续看AbstractAutowireCapableBeanFactory#doCreateBean方法的剩余部分。

图15-559到583行

这几行比较核心,是用来解决spring的循环依赖的。相信大家对spring循环依赖并不陌生,我就不赘述了。只是希望大家看完全部文章之后自己可以看源码真正了解哪些情况下spring才会有循环依赖问题,并加以总结。

559到560行:主要是判断当前的bean实例是否允许提前暴露,所谓的提前暴露就是这个bean在没有完全初始化好之后被其它bean引用。大家一定要对实例化和初始化有个基本了解,实例化是创建对象,而初始化对对创建好的对象就行设置属性等一些其它的初始化动作。从这里的判断条件我们可知允许提前暴露必须满足3个条件。1.当前bean是单例 2.spring容器被配置为允许循环依赖 3.当前bean正处于创建中时。

第2点很好理解,如果容器都不支持循环依赖都没必要提前暴露,因为提前暴露的前提就是出现了循环依赖。

第3点如果大家还记得调用链路前面的DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory)方法逻辑的话也很容易理解,这个方法会调用DefaultSingletonBeanRegistry#beforeSingletonCreation方法将beanNamt添加到singletonsCurrentlyInCreation容器,所以这里的第3点肯定是true。

第1点也不难理解,只有单例的bean才支持循环依赖,因为单例的bean一旦被引用就不会变,提前暴露出去才有意义。如果被引用的bean会变会生成不同的对象,那提前暴露出去后后面又生成新的对象,之前的对象不再被使用,那暴露出去就没有任何意义。

561到567行:在满足提前暴露的情况下,调用DefaultSingletonBeanRegistry#addSingletonFactory方法添加实例工厂,这个方法的核心逻辑就是向单例工厂map容器singletonFactories中以beanName作为key添加一个由lambda表达式构成的单例工厂singletonFactory,最终会调用AbstractAutowireCapableBeanFactory#getEarlyBeanReference方法,等后面调用时我们再说。

之所以说这里是为了解决循环依赖问题,我这里提前透露下,因为接下来要执行下面的populateBean(beanName, mbd, instanceWrapper)方法就是给我们的bean属性进行设值,这时候有循环依赖的话会再次走到当前bean的初始化过程(比如A依赖B,B依赖A。当前bean是A,在初始化属性B的时候又会尝试去获取A,所以会再次走到A的初始化过程)

为了故事的完整我们假设当前的beanName叫beanA吧,暂先不看下面的populateBean(beanName, mbd, instanceWrapper)且假设有循环依赖的情况,这时beanA又将被spring调用getBean(beanName)方法尝试获取beanA实例,这时再次走到AbstractBeanFactory#doGetBean调用getSingleton(beanName)方法获取单例对象时就可以从单例工厂singletonFactories中获取beanA的单例工厂并调用getObject方法获取实例,获取到的实例会放到earlySingletonObjects容器中以便后面有其它循环依赖时无需再次调用工厂获取beanA,而是直接通过earlySingletonObjects容器返回可以提前暴露的beanA实例。大家这时候一定要配合着源码理解。

上面已经说到spring的所谓三级缓存在循环依赖中使用场景,大概总结下就是,spring在没有循环依赖时bean的初始化过程完完整整且不会被打断的话,初始化好完整的bean实例会放进singletonObjects容器中(一级缓存);在实例化好对象初始化bean属性前为了防止有循环依赖,会提前向单例工厂容器(二级缓存)中添加单例工厂,以便在发生循环依赖时可以通过单例工厂提前暴露bean实例;在发生循环依赖使用单例工厂获取提前暴露的bean实例后,会将bean实例放入earlySingletonObjects容器中以便后面有其它循环依赖时直接从该容器中获取已经创建好的提前暴露对象。

上面我们已经了解了三级缓存的使用场景,那么大家想下为什么需要使用三级缓存,只保留一级缓存或者两级缓存不行吗?比如为了防止发生循环依赖,我们不向单例工厂容器中添加单例工厂而是直接向singletonObjects容器中添加未初始化好的bean,后面有了循环依赖也直接从singletonObjects容器里拿未初始化好的bean不就行了吗?

我说些自己的看法,首先如果只使用一个缓存,那么势必将出现这个缓存里既有提前暴露的bean实例也有初始化好的bean实例情况,从单一原则设计原则考虑这是不合适的。那单例工厂SingletonFactory缓存是否有可以省略呢?在出现循环依赖时直接从缓存中获取提前暴露的bean实例不就好了吗?毕竟在添加单例工厂后初始化bean前已经实例化好bean了,如果出现循环依赖把这个实例化好的bean暴露出去不就好了,为什么还要从单例工厂获取呢?这其实主要是为了防止这个bean存在动态代理的情况,如果存在动态代理那这个bean的引用就会更改,从普通的bean变成动态代理的bean,如果这时候简单的把刚实例化好的普通bean暴露出去那就会有问题。而通过单例工厂可以对这个bean进行检查和包装,如果这个bean被动态代理了就会暴露动态代理的bean。

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值