200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > Spring 源码解析 - Bean创建过程 以及 解决循环依赖

Spring 源码解析 - Bean创建过程 以及 解决循环依赖

时间:2019-08-02 22:27:23

相关推荐

Spring 源码解析 - Bean创建过程 以及 解决循环依赖

一、Spring Bean创建过程以及循环依赖

上篇文章对Spring Bean资源的加载注册过程进行了源码梳理和解析,我们可以得到结论,资源文件中的bean定义信息,被组装成了BeanDefinition存放进了beanDefinitionMap容器中,那Bean是怎样创建和依赖注入的还没有进行分析,而且这里还有个经典的循环依赖问题,本篇文章将带领大家一起继续上篇文章进行Spring源码的分析,梳理下Bean创建过程以及循环依赖问题的解决。

下面是上篇文章的地址:

Spring 源码解析 - Bean资源加载注册过程

在上篇文章,我们主要分析的AbstractApplicationContextrefresh()方法中的obtainFreshBeanFactory()方法的作用,通过该方法我们了解到如何创建的BeanFactory工厂,以及如何生成BeanDefinition并注册到容器,本篇文章我们还是从refresh()方法中找突破点,从finishBeanFactoryInitialization(beanFactory)入手分析,该方法主要做了对所有的单例类型bean进行初始化以及依赖注入:

二、finishBeanFactoryInitialization(beanFactory)

点击到该方法中,直接看到方法最后的beanFactory.preInstantiateSingletons()方法中:

preInstantiateSingletons()方法中,可以明显看到拿到了beanDefinitionNames列表,而这个列表则是存储所有注册的beanDefinition定义信息。

下面接着判断是否为单例模式,并且不是懒加载,这种情况下在项目启动时才需要进行实例化。

下面会判断当前的beanName是否为FactoryBean,如果不是的话则使用getBean(beanName),从这命名上我们也可以猜出getBean(beanName)肯定是做了对bean的创建,下面主要分析该方法,其实该方法调用的是AbstractBeanFactory下的getBean(beanName)

这里又调用了当前类的doGetBean方法,到这之后,我们先不着急继续向下分析。

当我们在使用Spring时,需要使用bean时,则一般都是通过ApplicationContextgetBean(String name)方法获取一个指定的对象,使用方式如下图所示:

那这个getBean(String name)方法和上面我们分析得到的getBean(beanName)是否为一个呢?下面我们可以先看下ApplicationContext类的getBean(String name)方法最终使用的哪个方法来获取bean

三、ApplicationContext 下的 getBean(String name) 方法

ApplicationContext中并没有重写getBean(String name)方法,实际的getBean(String name)其实是调用的AbstractApplicationContext下的getBean(String name)

在该方法中又使用了当前的BeanFactory中的getBean(String name, Class<T> requiredType)方法,那此时的BeanFactory是具体哪个子类呢?

在上篇文章中我们得出结论中,其中BeanFactory默认是使用DefaultListableBeanFactory,而DefaultListableBeanFactory并没有对getBean(String name)方法进行重写,那继续去父类AbstractAutowireCapableBeanFactory中找,也没有对getBean(String name)方法重写,继续向父类AbstractBeanFactory找,发现有getBean(String name)的重写。

看到这里是不是很有印象,和前面初始化单例分析时调用了相同的方法进行初始化bean, 那此时我们就可以得出结论不管是单例还是原型或者其他,最终都是使用的AbstractBeanFactory中的getBean(String name)方法初始并获取bean

下面我们重点分析下,其中的doGetBean方法。

四、doGetBean 以及 循环依赖

doGetBean方法中,首先对传入的name进行转换,因为name可能是别名,需要转为beanName

接着使用getSingleton(String beanName)方法获取该beanName的实例,如果实例不为空的话则使用该bean

这里的getSingleton(String beanName)其实是在DefaultSingletonBeanRegistry类下,主要是从单例缓存中获取已经创建的对象,这里就使用到了经典的三级缓存。

在该方法中又调用了当前类的getSingleton(String beanName, boolean allowEarlyReference)方法,继续进到方法下:

从该方法中可以明显看出,首先去singletonObjects容器中去获取对象,如果不存在的话则再去earlySingletonObjects容器中获取对象,如果还不存在的话,则从singletonFactories容器中获取一个ObjectFactory,如果存在则可以通过ObjectFactory创建对象,最后将对象存放至earlySingletonObjects容器中,然后从singletonFactories容器中移除。

这里如果不了解循环依赖的话,是不是看着一脸懵。

什么是循环依赖呢,如下图所示的这种都属于循环依赖:

由于Spring在实例化对象时,会先去尝试创建其依赖的对象,这样就会出现A在创建中,发现依赖于B,此时再去创建B,而B又依赖于A再去尝试创建A,这种情况下不就陷入了死循环中,因此为了解决这种场景增加了多级缓存的概念。

多级缓存是哪几级呢,其实就是上面的singletonObjects、earlySingletonObjects、singletonFactories三个容器,分别对应着缓存的一二三级,这三个容器在DefaultSingletonBeanRegistry类中:

三级缓存具体是怎么使用的呢:

一级缓存:singletonObjects,存储所有的单例bean

二级缓存:earlySingletonObjects,存储提前暴露的bean,里面的bean都是没创建完的

三级缓存:singletonFactories,存储单例beanObjectFactory(也就是创建该bean的工厂)

创建对象时首先会将创建beanObjectFactory暴露至三级缓存中,后面如果有依赖于该bean的则会先从一二三级缓存中挨个尝试取数据,直接拿到,如果是在三级缓存中存在,则通过ObjectFactory获取一个早期的bean对象,并且放入二级缓存中,三级缓存中的移除,后面使用该早期的bean对象进行依赖注入。再后面当该早期bean的依赖也被注入完成后,此时就是一个完整的对象了,需要从二级缓存中移除,放入一级缓存中,供上层使用。

了解这些之后,就不难理解getSingleton(String beanName, boolean allowEarlyReference)方法中的逻辑了,下面在回到AbstractBeanFactory类的doGetBean方法下:

如果从getSingleton(String beanName)中获取的实例为空,则缓存中不存在,那就要看如果进行实例化的了。

这里首先会判断是否原型模式下形成了循环依赖,如果是则抛出异常,因此这里可以得出Spring原型模式下不允许出现循环依赖。

这里可以看下isPrototypeCurrentlyInCreation方法。

可以看到这里的prototypesCurrentlyInCreation其实就是一个ThreadLocal,判断的依据就是当前ThreadLoad等于或者包含该beanName,即认为是出现了循环依赖,那该ThreadLocal什么时候写数据的呢,再回到doGetBean方法中,继续向下分析。

接着会获取到上级的BeanFactory,并且如果当前的beanName不在beanDefinitionMap容器中,则尝试用上级的BeanFactory进行获取bean,一般对于我们声明好的bean肯定都会存在于beanDefinitionMap容器中。

再接着会根据beanDefinitionMap容器中的BeanDefinition创建一个RootBeanDefinition,接着会遍历全部的依赖,首先判断是否已经注册依赖了,主要判断dependentBeanMap容器中是否存在,如果不存在的话,则进行注册将该依赖加入到dependentBeanMap中,最后递归的方式调用getBean(String name)创建依赖的对象。

在接着就要进行当前bean的创建工作了,首先如果是单例模式下,则使用getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法获取实例对象,并传入一个ObjectFactory函数,其中getSingleton方法其实在DefaultSingletonBeanRegistry下,进入到该方法中:

在该方法中首先对一级缓存进行上锁,然后再次尝试从一级缓存中获取对象,如果还是不存在的话,并且单例对象正在销毁的话则抛出异常,否则的话则执行创建bean的前方法,主要写入singletonsCurrentlyInCreation容器中,表示当前bean正在创建中。这个容器名称可以记一下会在后面进行判断是否是创建中的bean

下面接着使用newSingleton作为创建对象的标记,可以看到当调用singletonFactory.getObject()成功后,则置为成功的标志,而singletonFactory则就是前面传递进来的ObjectFactory,这里的getObject(),其实就是执行的AbstractAutowireCapableBeanFactory下的createBean方法,该方法可以放在后面分析,因为原型模式也是使用的createBean方法创建实例对象,后面可以一起分析,现在我们知道它是创建实例对象的就可以。

在接着该方法的最后,如果newSingleton标记创建成功,则此时对象属于一个完整的对象,其中依赖注入已经在createBean方法中实现过了,这个时候需要写入到一级缓存中曝光出来。

在写入一级缓存时,首先对一级缓存加锁,然后写入一级缓存,并从二三级缓存移除,同时记录到已注册的单例列表中。

到此getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法就走完了,在回到AbstractBeanFactorydoGetBean继续向下看。

上面如果不是单例模式,再继续判断否为原型模式,如果是原型的,则先调用beforePrototypeCreation(String beanName)beanName加入到prototypesCurrentlyInCreationThreadLocal中,进入到该类中逻辑如下:

这里prototypesCurrentlyInCreationThreadLocal正对应上前面原型模式下判断循环依赖,下面在回到上一步方法中,可以看到和单例模式一样使用了AbstractAutowireCapableBeanFactory下的createBean方法创建实例,同样该方法还是放在后面分析,继续向下看afterPrototypeCreation(String beanName)方法,此时当前bean已经实例化好了,依赖注入同样在createBean方法中已经完成了,下面就可以将ThreadLoad中记录的beanName移除了,逻辑如下:

在回到上一步方法中,继续向下看:

接着在else中则表示,既不是单例模式也不是原型模式下,这里就不做过多的介绍了,但明显的可以看到也是使用createBean方法创建实例对象的。

看到最后的话,三种情况下创建的bean不为空的话,则直接返回给上层,也就是给到业务层可以正常使用该bean了。

五、createBean

从上面的分析中发现,实际创建对象都使用的AbstractAutowireCapableBeanFactory下的createBean方法,现在我们对该方法分析下是如何操作的,进入到该方法中:

这里首先拿到前面创建的RootBeanDefinition,下面根据RootBeanDefinition获取到当前beanClass,主要判断需要创建的bean是否可以被实例化,是否可以通过当前的类加载器加载。 如果resolvedClass不为空,并且mbdbeanClass不是resolvedClass话,则创建一个新的RootBeanDefinition

接着会尝试创建代理对象,如果是AOP的话则尝试使用BeanPostProcessors来替代真正的实例,对于AOP的分析这里不做过多的介绍了,这里主要关注下最后的doCreateBean方法,进入到该方法中:

doCreateBean方法中,如果是单例模式的话,会尝试从factoryBeanInstanceCache缓存中获取一个BeanWrapper压缩对象,如果不存在的话则通过createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args)方法创建一个新的BeanWrapper对象。

这里的BeanWrapper对象是对bean的包装,可以使用统一的方式来访问bean的属性,从下面的操作可以看出,通过BeanWrapper对象获取到了当前bean的实例对象,因此在createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args)方法中肯定对当前bean进行了实例化操作,下面进入到该方法中:

createBeanInstance方法中,首先通过BeanDefinition获得beanClass,下面如果RootBeanDefinition的工厂方法不为空则使用工厂方法进行初始化策略。

如果RootBeanDefinition的工厂方法为空,接着向下看,其中resolved表示是否已经确定好构造方法,autowireNecessary表示构造方式是否需要自动注入,如果传递过来的参数为空的话,那这个时候就对RootBeanDefinitionconstructorArgumentLock进行上锁,接着会判断RootBeanDefinitonresolverConstructorOrFactoryMethod是否为空,这里其实是一个缓存,后面在实例对象时会对其进行赋值,这里如果存在缓存,则直接使用。

下面如果构造方法已经确定的话,那就是mbd.resolvedConstructorOrFactoryMethod缓存存在,则根据mbd中的构造类型选择用有参的构造函数创建bean,还是使用无参的构造函数创建bean

这里我们假设缓存为空的话,继续向下执行。

如果没有缓存的话,则就要找beanName对应的beanClass的所有的有参构造器,如果找到了则尝试进行创建bean,否则的话直接使用无参的构造函数进行初始化,这里由于篇幅我们直接分析下instantiateBean无参构造函数的实例化。

进入instantiateBean方法中。

在该方法中没如果是否开启了安全管理,如果是则使用AccessController.doPrivileged生成bean实例,可以看到最终都是由getInstantiationStrategy().instantiate方法生成实例,该方法其实在SimpleInstantiationStrategy类中,下面进入到该方法中:

在该方法中,首先如果有覆盖方法的话,同样对RootBeanDefinitionconstructorArgumentLock进行上锁,接着也还是获取RootBeanDefinitonresolverConstructorOrFactoryMethod是否为空,就是判断是否存在缓存,如果不存在的话则获取到beanClass,如果Class是接口类型的话则抛出异常。

这一步的处理比较明了,如果是安全模式下,则使用AccessController.doPrivileged,同时这里使用clazz.getDeclaredConstructor反射获取class的构造方法,并且将反射得到的结果存入到resolvedConstructorOrFactoryMethod中进行缓存,对应了前面取缓存的动作,最后直接通过BeanUtils工具类反射实例化出bean对象。

下面如果没有覆盖方法的话,则使用cglib进行实例化对象:

看到这里之后,我们应该了解到了bean在哪里被创建的了,下面在回到原来的AbstractAutowireCapableBeanFactory下的doCreateBean方法中,其中createBeanInstance已经创建出了bean实例,下面继续向下看:

这里判断是否为单例模式,并且允许循环依赖,并且该beanName正在创建中的话,判断是否正在创建中就是去singletonsCurrentlyInCreation容器中判断有无,这个容器在前面有提到。

如果条件都符合的话,则首先曝光至三级缓存中,可以看下addSingletonFactory方法的过程:

这里首先对一级缓存进行上锁,并且一级缓存中不存在,则写入到三级缓存中,并从二级缓存中移除,最后加入到注册单例列表中。

下面回到上一步的方法中。

接着会有一个核心的方法populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw)方法,主要进行了依赖的注入,依赖注入完后接着调用beaninit-method方法,进行自定义的初始化操作,这里我们主要看下populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw)方法。

populateBean方法中,先校验是否需要进行依赖注入,接着向下看,可以看到两个非常显眼的标识:

这里进行了两种类型的解析byNamebyType就是配置的autowire属性 ,选择依赖注入的方式是根据名称还是根据类型,这里我们主要看下autowireByName的逻辑:

这里首先获取到所有依赖的beanName,然后如果存在容器中的话,则递归的方式获取到该bean,下面记录到上一层的newPvs中。

接着再回到上一层的方法中,接着向下看,此时依赖的属性已经都被存放在了pvs中,可以看到最后是使用applyPropertyValues方法进行属性的赋值,下面来看下该方法的逻辑:

applyPropertyValues方法中同样判断了是否开启安全管理,继续向下看:

这里首先判断了下属性是否被转化,如果转化了就直接设置属性值,否则的话就记录下,在后面的逻辑中对其进行转化后再进行设置属性值。

这个转换其实就是依赖的类型有可能多种多样,比如可能是引用类型、集合类型、字符串、Properties属性等等,对于不同的依赖需要由不同的操作方式,因此在下面的逻辑中生成了一个BeanDefinitionValueResolver这个是bean定义属性值解析器,主要就是将bean定义中的属性值解析为bean实例对象的实际值:

这里上面没有被转化的属性,这里会进行转化,下面主要看是如何进行转化的,进入到resolveValueIfNecessary方法中:

可以明显看出针对不同的类型进行了不同的解析方式,这里以引用类型为例看下resolveReference(Object argName, RuntimeBeanReference ref)方法是如何处理的:

这里的逻辑也非常直观,首先获取到beanName,如果不是引用父类容器中的bean的话,则使用getBean递归获取依赖的对象,最后对其注册,并返回给上层。

下面再回到applyPropertyValues方法中就好理解了,最后将转化后的值通过PropertyValue中的setConvertedValue方法注入到bean的属性字段中。

到这里就大概看完bean的依赖注入,下面我们再回到AbstractAutowireCapableBeanFactory中的doCreateBean方法中,继续向下走的话

这里又从缓存中获取一遍bean,但这里的allowEarlyReference属性为false,也就是最多只会走一二级缓存,如果取不到的话,直接将前面的实例对象返回,否则如果是被代理的话,再次检查一遍依赖是否都加载完。

最后将处理的bean返回出去。

看到这里应该会对Bean的创建过程以及循环依赖有了一定的理解了吧。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。