内容均为自己理解,如有错误请指出,帮助作者成长,感谢!!!
提到Spring中的Bean,会出现很对相关联的问题。
例如:
1、Spring中的IOC是怎么实现的?
2、Spring是如何解决循环依赖的?
3、BeanFactory与FactoryBean的区别?
1.1 Spring-IOC
在Spring-IOC中我们绕不开三个名词:控制反转、依赖注入、容器。
控制反转:控制反转是一个理论思想。原来对象是由使用者来进行控制,有了Spring之后,可以吧对象交给Spring来帮助我们进行管理。
依赖注入:吧对应的属性值注入到具体对象中。例如@Autowired,populateBean完成属性值注入。
容器:容器顾名思义是用来存储对象的。Spring容器使用map结构来存储对象,Spring中一般存在三级缓存,一级缓存存放成品对象,二级缓存存放半成品对象,三级缓存比较特殊,是一个lambda表达式。整个bean的生命周期从创建到使用到销毁都是由Spring容器来进行管理。
图1.1 Spring-IOC流程图
Spring-IOC的流程,在图1.1中已经描述,此处做一些解述。
解述:
1、如上图所画,我们可以将Bean的状态分为概念态和定义态2种。概念态就是还没有进行Bean对象的创建流程,只是通过XML配置文件或者注解定义出这个Bean对象;定义态的Bean即为Spring开始创建Bean流程,并为Bean进行属性赋值等一系列操作。
2、beanFactory是最上层的根接口,没有提供对应的子类实现。我们在实际调用的过程中最常用的是DefaultListableBeanFactory。我们在使用时优先创建bean工厂,创建好bean工厂后优先向bean工厂中设置一些参数例如:beanPostprocrssor、Aware接口的子类等等属性。
2.1 通过creatBeanFactory()方法创建一个bean工厂(defaultListableBeanFactory)。
3、beanFactoryPostProcessor的处理,是通过PlaceHolderConfigurSupport、ConfigurationClassPostProcessor来修改bean的定义信息等内容。
Spring-IOC完整流程:
1、Spring读取XML配置文件或者注解,读取到Bean的定义信息,并将这些定义信息存储在BeanDefinitionMap对象中,此时的BeanDefinitionMap是一个半成品。(在2.1.1章节有具体描述)
2、通过增强器(BeanFactoryPostProcessor)对Bean对象进行赋值,具体赋值方式在图2.1中有描述。(在2.1.2章节描述PostProcessor)
3、经过BeanFactoryPostProcessor对BeanDefinitionMap赋值后,BeanDefinitionMap成为一个成品,通过反射机制来创建我们的Bean对象。
1.1.1 Spring读取XML配置文件或注解
如图1.1.1所画,Spring-IOC的第一步便是读取XML配置文件或注解。
图1.1.1 Spring读取XML/注解流程图
1.1.2 PostProcessor
PostProcessor可以称之为后置处理器或者增强器提供一些额外的扩展功能,比如从XML配置文件中读取到对象占属性值是占位符后,可以修改此属性值。
PostProcessor分为BeanPostProcessor和BeanFactoryPostProcessor两种,这两种都是增强器,针对的对象有所不同。BeanPostProcessor针对Bean对象,BeanFactoryPostProcessor针对BeanFactory。
如下图所示:
图1.1.2 PostProcessor
1.2 Bean的生命周期
Bean的生命周期包含了Bean从创建到使用到销毁的整体流程。话不多说,直接上图(图片有点大,截图后有点模糊)。
图1.2 Bean的生命周期
如小潘画的图所示,Bean的生命周期我们可以分为实例化bean、初始化bean、使用bean、销毁bean四个阶段,我们就通过这四个阶段来描述bean的生命周期。
bean的生命周期流程:
1、通过反射机制实例化对象。在getBean()和doGetBean()方法中进行反射实例化bean对象。
2、实例化bean对象之后就给此对象进行属性赋值。属性赋值分为容器对象属性赋值和自定义对象属性赋值。
2.1、通过populateBean()方法给自定义对象进行属性赋值。
2.2、检查容器对象是否实现了Aware接口,实现了Aware接口通过invokeAwareMethods()方法进行处理。主要完成BeanName、BeanFactory、BeanClassLoader对象的属性设置。
3、属性赋值后就可以调用BeanPostProcessor接口中的前置处理方法。使用比较多的有ApplicationContextPostProcessor,设置ApplicationContext、ResourceLoader等对象。
4、执行完前置处理方法之后,容器会判断bean对象有没有实现initializingBean接口,如果实现了就调用afterPropertiesSet方法,没有就不调用。
5、调用BeanPostProcessor接口中的后置处理方法。Spring-Aop就是在此步骤中实现。(AbstractAutoProxyCreator抽象类实现)。
6、后置处理方法结束后,我们就可以通过getBean()的方式来进行对象的获取。
7、销毁流程。
(1)判断对象是否实现了DispoableBean接口。
(2)调用destroyMethod方法。
1.3 BeanFactory与FactoryBean的区别
BeanFactory与FactoryBean都是用来创建Bean对象的,不过他们之间存在不同。
相同点:都是用来创建Bean对象的。
不同点:使用BeanFactory创建对象的时候,必须遵守严格的生命周期流程,比较复杂。如果想要简单的定义某个对象的创建,同时创建完成的对象交给Spring容器来管理,那么就需要实现FactoryBean接口。
FactoryBean接口有三个方法:
isSingleton():判断对象是否是单例对象。
getObjectType():返回获取对象的类型。
getObject():自定义创建对象的过程。(比如是通过new来获取对象、通过反射来获取对象、动态代理)
图1.3 BeanFactory与FactoryBean的区别
1.4 循环依赖
聊到Spring中Bean的时候就绕不开循环依赖,三级缓存问题。
如下图所示:
图1.4.1 循环依赖问题引入
先创建A对象,实例化A对象,给A对象赋值,从容器中查找B对象,如果从容器中找到了B对象,直接给A对象赋值,找不到就会创建B对象,实例化B对象,给B对象赋值。此时从容器中查找对象A,找到了赋值,找不到创建A对象。此时就形成了一个闭环,那我们看看Spring是如何解决这个死循环问题呢?如下图:
图1.4.2 三级缓存
上图三级缓存代表三个缓存!!!
图1.4.3 三级缓存
图上的数字则为步骤,图1.4.2上描述了过程,1.4.1章节做了详细描述。
说了这么久的三级缓存,我们看下Spring源码中三级缓存是什么样子。
在循环依赖这个问题有这几个点个人感觉比较重要:三级缓存、提前暴露对象、aop
想一下,发现A对象是已经被创建了,不过A对象是半成品状态,只完成了实例化没有完成初始化。在程序运行的过程中,我们有了对象的引用,我们可以在后期完成赋值操作。这样我们就可以先把半成品对象先赋值,后续再完成完整的赋值操作。这样就相当于提前暴漏了一个不完整对象。(提前暴漏对象)
在Spring容器中对象的实例化和初始化是分开操作的,这样就解决了循环依赖的问题。当对象都完成实例化和初始化之后,还要吧完整对象放到容器中,此时容器中存在半成品和完成品2个状态的对象。因为存在2中不通状态的对象,所以在容器中使用不同的map结构来进行存储,也就是我们常说的三级缓存。一级缓存存放的是成品对象,二级缓存存放的是半成品对象,三级缓存存放的是一个lambda表达式。如果一级缓存中存在此对象,那么二级缓存中就不会存在同名的对象。三级缓存的查询顺序是一级、二级、三级。
三级缓存的value类型是ObjectFactory,是一个函数式接口,三级缓存时为了保证在容器运行过程中同名的bean对象只有一个。当一个对象需要被代理,生成代理对象时,在后置处理器中来进行AOP操作。而Spring容器中普通对象和代理对象是不能同时存在的,因此当一个对象需要被代理的时候,就要使用代理对象替换掉之前的普通对象。然而在实际调用过程中,我们不能确认什么时候对象被使用,所以在对象被调用的时候,优先判断此对象是否需要被代理,因此传入lambda表达式的时候,可以通过lambda表达式来执行对象的覆盖过程。getEarlyBeanReference()方法中完成此操作。因此,所有的bean对象在创建的时候都要优先放到三级缓存中,在后续的过程中,如果需要被代理则返回代理对象,如果不需要被代理则返回普通对象。
1.4.1 循环依赖步骤
1.4.2 Bean放入三级缓存的时间与删除时间
三级缓存:Spring容器执行了doCreateBean()之后,放入三级缓存。 addSingletonFactory()此方法中将对象放入三级缓存中。如下图所示,在doCreateBean方法种调用了addSingletonFactory方法,而addSingletonFactory方法中if判断里面第一行代码就是将bean对象放入在三级缓存中。
二级缓存:第一次从三级缓存中确认此对象是代理对象还是普通对象的时候放入二级缓存中,同时删除三级缓存中的内容。getSingleton()此方法中将对象放入二级缓存中同时删除掉三级缓存中的对象。如下图所示:
一级缓存:生成完整对象之后,放入到一级缓存,同时删除掉二三级缓存。在执行getSingleton()方法时会调用addSingleton()方法,addSingleton()此方法中将对象放入一级缓存中同时删除掉二、三级缓存中的对象。(getSingleton()方法太长就不截图了!)