spring源码学习笔记

目录

一,看源码的方法:

二,BeanDefinition接口:

三,Bean的定义信息,XML和注解,如何解析:

3.1,XML的配置文件如何解析:

3.2,注解如何解析:

3.3,总结,殊途同归:

四,对象具体创建的方式是什么? 当然是反射。

五,Spring特点;

5.1,Spring具有非常好的扩展性;

六,正确认识BeanFactory:

6.1 ,正确理解BeanFactory;

6.2,ApplicationContext与BeanFactory;

七,PostProcessor,增强处理器:

7.1,BeanFactoryPostProcessor和BeanPostProcessor;

7.2,BeanFactoryPostProcessor的postProcessBeanFactory()方法;

八,很重要的refresh()方法:

九,创建对象包含几个步骤

1)实例化:

2)初始化:

十,Bean的生命周期:

1)不要死记硬背Bean的生命周期:

2)实例化:

3)初始化是干什么的?

3.1)如何理解Aware接口,什么是Aware接口?

3.2)现在还有Aware吗,不是都用@Autowired注解吗?

4)执行初始化调用方法:

5)使用和销毁:

十一)Bean的扩展实现;

11.1,针对Bean对象的一系列扩展实现;

11.2,BeanPostProcessor:

11.3,AOP是IOC的一个扩展实现;

总结:

十二,循环依赖问题与三级缓存:

1,什么叫循环依赖?

2,如何解决循环依赖问题:

3,详解三级缓存:

3.1,根本在于将实例化和初始化分开来考虑:

3.2,源码-三级缓存在哪里:

3.3,源码-三级缓存解决循环依赖的过程:

4,问题

4.1,问题一,

4.2,问题二,

4.3,问题三,

4.4,问题四,

4.5,问题五,

4.6,问题六



一,看源码的方法:

先以应用入手,再到理解原理,然后

再理解源码。

以应用入手,比如说,@Controller注解、@Service注解。

二,BeanDefinition接口:

不管是注解,还是xml配置文件,都是用来描述各种Bean的定义信息的一种方式

在Spring源码里边,有一个十分重要且核心的接口,叫BeanDefinition。当然,这个接口里边会有一些具体的实现子类。

不管是xml也好,还是注解也好,定义好这些Bean的定义信息之后,最终都会转成一个BeanDefinition,将其交给Spring容器,然后Spring容器在识别到这些BeanDefinition之后,才能接着根据BeanDefinition里边的一些相关信息,来把我需要的Bean对象给创建出来。是这么一个过程。

如图所示:

三,Bean的定义信息,XML和注解,如何解析:

3.1,XML的配置文件如何解析:

首先,通过IO流的方式,把这些配置文件给读取过来,读取过来之后,就是一堆的字符串。

字符串本身是没意义的,需要把这些字符串进行一个解析,如何解析呢,由于XML文件本身是自带格式的,所以说有一些现成的工具比如说document4j、SAS等等,这些工具就可以把这一堆的字符串来转成一系列的document对象。

然后下一步就可以将document对象转成一堆具有父子关系的Node节点,然后循环遍历Node节点,就可以取出这些Node节点里边的数据值。

当取到这些具体的数据值之后,就可以把这些具体的数据值来赋到我们具体的BeanDefinition对象中,然后就可以进行相应对象的创建了。

举例:

经过一系列的解析之后,就可以把这些具体的属性值,全部都赋值到具体的BeanDefinition对象中去,然后就可以去进行相应对象的创建了。 

3.2,注解如何解析:

注解形式也是一样的,首先是先获取到类上边的注解信息。

当获取到这些注解信息之后,就可以获取到具体的class信息。

有了具体的class信息,比如说name属性、value属性等等之后,就可以把这些具体的属性信息赋值到进行具体的BeanDefinition对象中,然后就可以进行相应对象的创建了。

3.3,总结,殊途同归:

不管是XML还是注解,只是解析的方式不同而已,最终殊途同归,还是要将具有的数据值来赋值到BeanDefinition对象中,然后再去进行相应对象的创建。

四,对象具体创建的方式是什么? 当然是反射。

当然是反射,几乎所有的框架创建对象的方式都是反射。

有人疑问:反射的效率不是很低吗?

其实实际情况是:

一,反射即使效率低,为什么还是要用反射呢,因为反射足够灵活!比如说当我获取到一个完全限定名(包名+类名)的时候,我们就可以根据这一个字符串来创建出我们需要的一个具体对象;

二,关于反射效率低这件事,当一下子创建十万个以上的对象的时候,反射才可能会出现效率低的问题,当实际上只是创建少量对象的时候,是不会出现任何的效率问题的。

所以说,关于怀疑反射是否效率低这件事,根本不需要去考虑这件事情。

五,Spring特点;

5.1,Spring具有非常好的扩展性;

Spring的生态完整最根本的一个原因是,Spring具有非常好的扩展性,扩展性这一点也应该是学习Spring源码的时候最应该学习的知识。

因为在看源码的时候就会发现,源码里边很多地方都是可扩展的、留了很多的口子,让我们能够随意的对它进行扩展和优化。

六,正确认识BeanFactory:

6.1 ,正确理解BeanFactory;

有人把BeanFactory翻译成Bean工厂,其实这是不准确的。

如图所示,其实在源码中,对于BeanFactory真正的描述是,BeanFactory它是一个用来访问Spring容器的一个根接口。

因为Spring它本身就是一个容器,容器里边可以存放很多的各种各样的Bean对象,而实际上BeanFactory更多的是用来访问Spring容器的一个入口

如图所示:

 为什么把BeanFactory称之为一个入口呢?

因为BeanFactory比较单一,它只提供了一些getBean()的方法,但是仅仅有getBean()方法在实际开发中可能不太够用,所以ApplicationContext对BeanFactory做了一些最基本的扩展功能,提供了一些更加完善的方法。

那怎么理解BeanFactory呢,你可以把它理解为是一个访问Spring容器的一个入口,也可以直接认为它就等同于容器对象,我们平时可以就把它的某一个实现子类就当作是我们整体的Spring容器。

比如说这个DefaultListableBeanFactory,这个类对象就是我们所创建的Spring容器,也就是说我们的所有的Bean对象都在这个DefaultListableBeanFactory类对象里边进行存放的。

而这个DefaultListableBeanFactory就是BeanFactory接口下边的一个实现子类。所以我们通常可以直接把BeanFactory就等同于Spring容器,当我们需要访问Sping容器的时候,可以直接通过BeanFactory来访问容器里边的一些Bean对象或者是属性值。

6.2,ApplicationContext与BeanFactory;

首先,通过Idea中的show diagram查看一下ApplicationContext的继承关系:

 所以,ApplicationContext,应用上下文,继承BeanFactory接口,它是Spring的一各更高级的容器,提供了更多的有用的功能;

1) 国际化(MessageSource)

2) 访问资源,如URL和文件(ResourceLoader)

3) 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层  

4) 消息发送、响应机制(ApplicationEventPublisher)

5) AOP(拦截器)

BeanFactory是Spring里面最低层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能;

七,PostProcessor,增强处理器:

7.1,BeanFactoryPostProcessor和BeanPostProcessor;

BeanPostProcessor,有人把它称之为Bean的后置处理器,也有人把它称之为Bean的增强器,其实从功能上讲,称之为后处理器是不太准确的,应该叫做增强器更为合适。增强器是什么意思呢,它就是具体用来完成某些具体的扩展功能和实现的。

首先看这俩:

BeanFactoryPostProcessor和BeanPostProcessor有什么区别呢?

很明显,从类名就可以看出来,一个针对的是对BeanFactory对象进行的某些功能的增强,一个针对的是Bean对象进行的某些功能的增强

7.2,BeanFactoryPostProcessor的postProcessBeanFactory()方法;

 在BeanFactoryPostProcessor的postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法中,可以通过这里传进来的BeanFactory对象,来获取到BeanDefinition对象,继而对BeanDefinition对象去进行一些修改。

这个xml文件中的这个bean标签中的数据值最终会进入一个BeanDefinition对象里边。可以看到bean标签里边有一个property标签,property标签里边有一个value值,value值里边的占位符的实际值的最终替换环节,就是在BeanFactoryPostProcessor这个增强处理器里边进行完成的。 

 从最初的BeanDefinition保留原始值,到成为最终的BeanDefinition对象的过程中,可以有多个BeanFactoryPostProcessor的实现类来进行增强处理。

如图所示:

八,很重要的refresh()方法:

org.springframework.context.support.AbstractApplicationContext # refresh()

 这个refresh()方法很重要,里边包含了13个方法,如果想把Spring源码看明白,那么这13个方法是必须要搞懂的。如果这13个方法没搞懂,那说明Spring源码没看明白。


九,创建对象包含几个步骤

普及一个基本知识点:

创建对象包含几个步骤:

一般情况下包含两个环节,第一个环节叫实例化,第二个环节叫初始化

1)实例化:

实例化干什么事情,——分配内存空间。
当分配好内存空间之后,此时这个对象里边的属性值是什么值?当然是默认值

2)初始化:

那么很明显,初始化干的是什么事情,—— 属性赋值。
但是,在Spring中,初始化其实干的事情可不仅仅是一个属性赋值操作,除了属性赋值之外,还有其他非常重要的核心操作,比如说执行init()方法,比如说扩展实现,虽然有些麻烦,但是这才是Spring完整的一套流程。


十,Bean的生命周期:

1)不要死记硬背Bean的生命周期:

到底什么是Bean的生命周期?

死记硬背Bean的生命周期,是没有意义的。这东西,不需要背。

首先,写Spring源码的家伙,首先他也是个人,他只要是个人,他就一定会有自己的逻辑思路。如果我们可以把这一套逻辑思路理解记忆、背下来之后,那再回过来看Bean的生命周期,那还会有什么问题吗。

加载、验证、准备、解析、初始化,是属于某一个Bean对象的一个创建,这些东西是属于JVM里边的内容,这里暂不做讨论。

我们只说Spring里边是怎么用的。


十一,Spring中创建Bean对象的步骤:

2)实例化:

第一个,实例化,就是用来给对象分配内存空间,以及注意此时的属性是默认值。

在实例化完成之后,下边有很大一部分时间要来完成初始化这一过程。

3)初始化是干什么的?

首先,对象创建好了之后,紧接着下一步该干什么,给对象的属性赋值。

赋值完了下一步该干嘛,看图上所说,检查Aware相关接口并设置相关依赖。


3.1)如何理解Aware接口,什么是Aware接口?

首先,Spring本身是一个容器,是一个框架,Spring里边主动帮我们定义了很多个不需要我们用户自己去定义一遍的那些对象,比如说,BeanFactory、ApplicationContext、Environment、ResourceLoader,这些全部都是容器帮我们创建好的对象。

比如说,我如果想在当前对象中注入这两个属性的话,就必须有相应的get、set方法。

那么此时问题来了,

1,谁在什么时候调用了此方法来进行属性的注入?

首先,谁去调用这个set方法,我们用户,还是Spring容器,肯定是Spring容器。

因为对象创建都交给容器了,这种set方法进行属性的注入肯定也是由Spring容器来完成。

2,其次,怎么调用,怎么确定调用的是这个set方法而不是其他的set方法。

答案是,通过定义统一的规范接口,来实现此功能。

在Spring中有一个Aware接口,该接口是一个空接口,它里边没有任何一个方法,但是它有一些子类实现。

比如说BeanFactoryAware,在这个子接口里边,发现了setBeanFactory(BeanFactory beanFactory)方法;

比如说ApplicationContextAware接口,在里边发现了setApplicationContext(ApplicationContext applicationContext)方法。

这样的话,我只要让Student类实现这个BeanFactoryAware和ApplicationContextAware接口之后,就直接拥有了这两个set方法

从上面可以看出,其实Bean对象的属性赋值分为两个部分,一个是用户自定义的属性的赋值,一个是对容器对象的赋值

3.2)现在还有Aware吗,不是都用@Autowired注解吗?

有疑问:现在在实际写项目的时候是不是不需要Aware了,直接用@Autowired不就行了吗。

答:对,加一个@Autowired也行,没有问题。但是如果就是碰到了一个xml写的呢,人家没有用注解,怎么办。所以这个时候使用Aware接口就发挥作用了。

所以说这个Aware接口是一个标准的流程,如果使用@Autowired注解也没问题,但是如果没用@Autowired注解的时候,我们需要容器创建的对象了,比如说BeanFactory、ApplicationContext、Environment、ResourceLoader这些。这个时候这个Aware接口(子接口比如:BeanFactoryAware、ApplicationContextAware等等)就发挥作用了。 

4)执行初始化调用方法:

刚才说到,在属性赋值的步骤完成之后,紧跟着,会进行一个叫做“执行前置处理方法”——“初始化”——“执行后置处理方法”的步骤。

这里的“初始化”和整个大步骤中的“初始化”不是一个东西。这里的初始化确切的说是——“执行初始化调用方法”。

什么是“执行初始化调用方法”?

比如说,我们在xml文件中定义Bean的时候,会有这么一个属性叫做init-method,这个属性一般我们是不写的。

“执行初始化调用方法”这一个步骤在Java语言里边其实是没有实际意义的

因为在其他编程语言里边比如说Python里边会在这一个步骤中进行属性赋值工作,但是在Java里边,刚才已经刚刚完成了属性赋值的工作了。

即使如此,其实“执行初始化调用方法”这一个步骤还是有用的,在源码里边,“执行初始化调用方法”的名字叫做invokeInitMethods,在这个方法里边有一段判断逻辑——判断当前这个Bean对象是否实现了InitializingBean接口

InitializingBean这个接口是做什么的?

在这个接口里边有一个方法,叫做afterPropertiesSet(),这个方法是干嘛的?在这个方法里边可以做一件事——给用户最后一次机会来手动进行属性赋值或者方法调用操作

举例:实现这个afterPropertiesSet方法,进行属性赋值:

如果用户实现了InitializingBean这个接口,那么就实现afterPropertiesSet方法,如果没有实现InitializingBean这个接口,那么就继续往下执行原来这个invokeInitMethods方法剩余的部分。

5)使用和销毁:

当“执行前置处理方法”——“初始化”——“执行后置处理方法”这些步骤都完成了之后,即整个初始化步骤完成了。

那么接下来就是使用对象了,使用完对象,就是销毁对象了。

十一)Bean的扩展实现;

11.1,针对Bean对象的一系列扩展实现;

当完成了Bean对象的实例化以及属性赋值之后,此时是否已经可以获取到一个完整的Bean对象?

答:如果不考虑Spring,如果是一个正常的对象的创建过程,经过了对象的实例化创建,属性的赋值,此时该对象就可以用了。

但是对于Spring的Bean对象来说,还没有完,还有一个重要的步骤,就是针对该Bean对象进行一堆的增强实现,所以下边的步骤就是Bean的扩展实现。

11.2,BeanPostProcessor:

BeanPostProcessor,针对Bean对象的增强处理器;

顾名思义,它也是个增强处理器,只不过针对的是Bean的增强。

BeanPostProcessor有什么用处?

11.3,AOP是IOC的一个扩展实现;

首先,记住一句话:AOP是IOC的一个扩展实现。

其次,AOP的实现原理是什么?动态代理,动态代理的实现方式有两种,分别是JDK和cglib,这些属于最基本的知识,大家都知道。

在Bean对象的实例化以及属性赋值之后,我们现在已经获取到了一个Bean对象了,当我们有了一个Bean对象之后,我们想生成它的代理对象,我们需要做的是使用该Bean对象把它作为参数给BeanPostProcessor类里边的方法传进去,让BeanPostProcessor这个增强处理器来给我们生成具体的代理对象了。

看源码:

1,BeanPostProcessor接口的AbstractAutoProxyCreator实现子类,来给Bean对象生成它具体的代理对象。

2,在AbstractAutoProxyCreator实现子类中,找到BeanPostProcessor接口里边仅有的两个方法:postProcessBeforeInitialization()和postProcessAfterInitialization()。

3,可以看到:postProcessBeforeInitialization()方法这里,只是原封不动的返回了一个Bean,没有做其他的额外工作。

4,点开这个postProcessAfterInitialization()方法之后,发现里边是有处理逻辑的,抛开那些if条件,可以看到里边有一个wrapIfNecessary()方法,该方法的意思是,如果有需要则对Bean对象进行包装。

5,在这个wrapIfNecessary()方法里边可以找到一个叫做createProxy()的方法,意思是创建代理。

6,在createProxy()的方法里边,翻到最后一行,可以看到一个:proxyFactory.getProxy(classLoader);

可以看到,这里运用了工厂模式,通过代理工厂来生成代理对象。

7,再点proxyFactory.getProxy()方法,进去之后发现代码:createAopProxy().getProxy(classLoader);

createAopProxy()方法的意思是,用来创建我们的代理工厂。

8,再点getProxy()方法进取之后,可以发现这里是一个接口的抽象方法,该方法肯定有相应的实现,点了实现之后发现,这两个实现子类,JdkDynamicAopProxy和CglibAopProxy。

至此,这段逻辑就已经明了:在Bean对象的增强处理器中,就是通过对Bean对象进行一些条件判断,来选择一种代理模式,来进行Bean对象的代理

总结:

所以此时你会发现,其实AOP,就是在对Bean对象进行增强处理的某一个环节中,去做了动态代理的这么一件事。

所以AOP有没有出现在Bean的生命周期里边,答案肯定是有的。

所以此时再去回味那句话,AOP其实就是IOC的一个扩展实现,就很容易理解了。


十二,循环依赖问题与三级缓存:

1,什么叫循环依赖?

在spring中,对象的创建是交给Spring容器去执行的,Spring创建的Bean默认是单例的,也就是说,在整个Spring容器中,每一个对象都是有且只有一个。

那么这个时候就可能存在一种情况:

比如说,有一个A对象,它有一个b属性,还有一个B对象,它有一个a属性。当在对这两个对象进行属性赋值的时候,就会产生循环依赖问题。

假设先创建A对象,首先对A对象进行实例化,对A对象实例化完成之后,紧接着要对A对象进行初始化步骤中的属性赋值操作,那么在对b属性赋值的时候,此时是没有B对象的,所以接着要开始创建B对象了,当创建了B对象,并对B对象进行实例化完成之后,接着要对B对象进行初始化步骤里边的属性赋值操作——对a属性进行赋值了,而这个时候A对象也还没有创建完成。

所以这个时候,就形成了一个闭环,形成了一个循环依赖,也就是出现了循环引用的问题,这个时候程序就会报错,提示程序无法继续执行下去了。

这是循环依赖最直接、最简单的一个例子,不管循环依赖是两个对象之间,还是三个对象、四个对象、五个对象等等,最后产生了循环依赖的原因都是如此。

循环依赖问题详解-图解:

当前这种情况形成了一个闭环,即产生了循环依赖问题。

2,如何解决循环依赖问题:

问题的症结在于红叉这个地方,如果这里从容器中找到了A对象了,或者即使没找到也想办法将其阻断掉,那么形不成闭环之后,就可以解决循环依赖的问题了。

解决循环依赖问题:

如上图所示,初始化B对象要对a属性赋值的时候,要从容器中查找A对象的时候,这个时候其实Spring容器中有没有A对象?

其实是有的。只不过这个时候的A对象和我们传统意义上的A对象有些不一样。

在对象的创建过程中,我们可以将对象分为两种状态:

一种是完成实例化以及初始化的对象,这种我们将其叫做“成品对象”

另外一种是完成了实例化、但是还没有完成初始化的对象,这一种对象我们将其叫做“半成品对象”

那么我们刚才从容器中查找A对象的时候,这个时候的A对象是一种什么对象?毫无疑问,是一种“半成品对象”。

那么接着有一个至关重要的问题:如果当前对象持有了某一个对象的引用,那么能否在后续的步骤中给当前对象的相应的属性去进行赋值操作?

答案是可以的,因为有了某一个对象的引用之后,就意味着有了那个对象的地址,那么就一定能够对当前对象的相应属性进行一个赋值操作。

重新回到刚才从容器中查找A对象的步骤,此时虽然获取不到成品A对象,但是能够获取到半成品的A对象,所以其实从容器中找不到A对象的情况是不存在的:

那么如何从容器中获取半成品的A对象呢,答案就是用到了缓存这个东西,即接下来的三级缓存。

在红框的步骤中,当我们实例化完成A对象之后,可以将A对象先放入到一个缓存结构中,比如说Map。

紧接着,按照图中所示,初始化A对象、给b属性进行赋值操作,然后从容器中查找B对象,这个时候如果从容器中没有找到B对象,那么再从缓存中来查找一下是否存在B对象,如果还是找不到,那么紧接着就开始创建B对象,创建完成B对象之后,然后是实例化B对象,实例化B对象之后,我们将此时的半成品B对象也放入到缓存中去!

然后接着是初始化B对象、给a属性进行赋值操作,然后会先从容器中查找是否存在A对象,如果容器中没有A对象,那么再从缓存中去查找是否存在A对象,此时发现是有A对象的,那么就可以完成了对B对象的a属性的赋值工作了,那么B对象就可以初始化完成了,那么这时的B对象就是一个成品了。【注意此时B对象虽然是个成品了,但是它里边的a属性对应的A对象此时仍然是一个半成品】。

当B对象已经是一个成品了,那么将成品B对象放入缓存中,此时A对象的b属性就可以顺利进行赋值操作了,A对象的b属性进行赋值操作之后,A对象就可以顺利的初始化完成,然后A对象也成为了一个成品对象了。

因为刚才创建B对象是因为要对A对象的b属性进行赋值才进行的, 所以接下来继续走创建B对象的代码,然后会先从Spring容器中以及三级缓存中查找是否已经存在B对象,这个时候缓存中已经存在B对象了,所以直接获取B对象即可。

以上,就是三级缓存解决循环依赖问题的粗略过程。

3,详解三级缓存:

3.1,根本在于将实例化和初始化分开来考虑:

一般来说,Spring解决循环依赖问题的办法就是三级缓存以及提前暴露对象。但是通过上边的分析,我们可以发现,其实解决的根本就是在创建对象的过程中,将Bean对象的实例化和初始化分开来进行考虑

3.2,源码-三级缓存在哪里:

在Spring源码中有一个非常重要的类,叫做——DefaultSingletonBeanRegistry。在这个类里边,有三个缓存结构,这就是Spring里边的三级缓存。

如图所示:

 

解释:

一级缓存:singletonObjects;

二级缓存:earlySingletonObjects;

三级缓存:singletonFactories;

其实不管是一级缓存也好、还是二级缓存也好、还是三级缓存也好,都是我们人为的给它下的一个定义。源码的作者在写的时候并没有说哪个是一级、哪个是二级、哪个是三级。

这三个缓存有什么样的区别:

首先,从表面看,一级缓存和二级缓存是ConcurrentHashMap,三级缓存是HashMap结构。其次一级缓存的容量是256,二级缓存和三级缓存的容量是16。

其次,它们的泛型不一样:一级缓存和二级缓存的里边放的全部是Object类型的对象,而三级缓存里边放的是ObjectFactory类型。这也才是最根本、最重要的区别。

这个ObjectFactory是什么?

其实它是一个函数式接口。函数式接口的主要作用就是可以把匿名内部类和lambda表达式当作参数传递进方法里边去。

比如说,这里有一个函数式接口:{()->createBean()},那么此时将其作为参数传递到方法里边去了,那么当调用getObject()方法的时候,实际上调用的就是createBean()方法。

想理解函数式接口的详情可以去Java8新特性里边去学习一下。

3.3,源码-三级缓存解决循环依赖的过程:

模拟展现一下三级缓存是如何解决循环依赖的场景:

先记住六个方法:

首先,创建对象是从哪一个步骤开始的,在创建对象的时候有一个非常重要的方法,叫做——refresh()方法,在ClassPathXmlApplicationContext类里边

在refresh()方法里边,包含了十三个创建对象的非常重要的步骤。

在【finishBeanFactoryInitialization(beanFactory);】这个步骤的时候,注意一下,正是在这一个步骤里边,才真正开始了对象的实例化操作。

进入finishBeanFactoryInitialization()方法,里边有一个步骤:

beanFactory.preInstantiateSingletons();

在这个步骤里边,最后有一个步骤叫做:getBean(beanName);

这个getBean()方法的作用就是从Spring容器中先获取一下该对象,来判断该对象是否已经存在于Spring容器中。

点进去这个getBean()方法之后,里边是doGetBean()方法。

在doGetBean()方法里边,又可以看到一个方法叫做getSingleton(),就是从Spring容器中获取该单例对象,如果Spring容器中此时没有该对象,那么就会走下边的if (sharedInstance != null && args == null) 相反的else方法。

在这个else里边,可以找到一段代码: 

在这里,我们又发现了一个getSingleton()方法,只不过这时这里的参数多了一个lambda表达式。但是该lambda表达式现在不会被调用执行,因为只有在调用getObject方法的时候才会调用执行该lambda表达式

现在这个lambda表达式只是作为一个参数,传递到了getSingleton()方法里边去。

点击进入这个getSingleton()方法,可以看到一段代码:

 这个singletonFactory参数是什么,就是上一段的lambda表达式:

那么此时执行了该lambda表达式的getObject()方法时,实际山执行就是该lambda表达式里边的内容——createBean()方法

点进去createBean()方法之后,又可以找到一个叫做doCreateBean()的方法。

这里提个醒,在Spring源码里边,有很多以do开头的方法,当你看到这些以do开头的方法时,应该意识到,在这个方法里边,往往才是真正的逻辑处理过程

点进去doCreateBean()方法,可以找到这个方法——createBeanInstance(),这个方法是实际执行对象的实例化操作的

点进去createBeanInstance()方法里边,最后边可以找到一个方法——instantiateBean()方法。

进入实际的实例化环节点进去这个instantiateBean()方法,会发现一段代码:

点进去这个instantiate()方法之后,会发现这么一段代码:

这不就是常见的反射代码嘛,现在这里是获取了当前对象的构造器了,当有了构造器之后,那么紧接着就是该创建该对象了。

找到后边的代码BeanUtils.instantiateClass(constructorToUse),点进去instantiateClass这个方法,可以看到:

return ctor.newInstance(argsWithDefaultValues);

此时,就算是利用反射的方式,来创建出了对象了

上述步骤,就是用来说明:Spring在创建对象的时候,在实例化对象的环节,是使用的反射的方式来完成了对对象的实例化了。

返回到doCreateBean()那一个步骤,里边有这么一段代码:

其中,

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

这一句代码其实就是Spring三级缓存的核心点。

看注释也能看出来:“这里能够解决循环依赖问题。”

观察addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

这一句代码,发现后边的参数又是一个lambda表达式,点进去这个addSingletonFactory方法进去,可以看到Spring三级缓存的核心代码:

先看这一句:

this.singletonFactories.put(beanName, singletonFactory);

这一句代码是什么意思:this.singletonFactories是我们的三级缓存这个都知道,这一句代码的意思是将singletonFactory也就是那个lambda表达式参数给放入到三级缓存中去,此时的key为beanName,比如说“对象A”。

示意图:

再看紧接着这一句:

this.earlySingletonObjects.remove(beanName);

这一句代码的意思是,在将那个lambda表达式参数给放入到三级缓存中去之后,紧接着将该对象从二级缓存中给移除掉

在创建A对象的时候,刚才已经实例化完成A对象了,又将此时的半成品A对象给放入到三级缓存中去了,那么接下来该干嘛了,该给A对象的属性赋值了。也就是接下来的代码:

populateBean()方法中,该给A对象的属性进行赋值了

“在对属性赋值的过程中,可能会存在有一些属性依赖其他的Bean,那么就会递归着去创建依赖的Bean”。

详情:

点进去populateBean方法里边,在里边的最后一步:

点进去这个applyPropertyValues方法,看到这一段代码:可以看出,此时属性名叫做b,以及属性值的类型是RuntimeBeanReference

再往下看到这一句代码:

点进去这个resolveValueIfNecessary方法,

很明显,刚才已经知道属性值是RuntimeBeanReference类型,所以会进入到if条件里边的resolveReference方法,点进去该方法,看到这一段代码:

第一句是获取一下Bean的名称;第二句是从Spring容器中获取一下b属性对应的B对象

点进去该getBean方法之后,发现如下代码:

此时,发现开始套娃了。类比于之前,接下来会进入到doGetBean方法,在该方法里边会先判断Spring容器中是否已经存在该对象,如果发现Spring容器中不存在该对象,则走else——开始创建该对象,然后进入一个叫做getSingleton的方法,此时进入该方法的时候会传入一个lambda表达式。

然后会进入getSingleton这个方法,在这个方法里边会去执行singletonFactory.getObject();也就是会开始调用执行lambda表达式里边的createBean方法:

点进去这个createBean之后,会执行一句代码:

点进去这个doCreateBean方法之后,会去执行一句代码:

点进去createBeanInstance 方法之后,会去执行如下代码:

点进去instantiateBean方法之后,会去执行如下代码:

点进去instantiate方法之后,可以看到如下代码,

执行这段代码之后,就是通过反射的方式创建出了该对象的构造器了,那么紧接着就可以根据构造器创建出来该对象了

点进去这个instantiateClass方法进去,会发现如下代码:

return ctor.newInstance(argsWithDefaultValues);

此时,就算是利用反射的方式,来创建出了对象了。

然后,回到刚才的doCreateBean()那一个步骤,会发现如下代码:

点进去这个addSingletonFactory方法之后,可以看到是将该对象的lambda表达式也添加到三级缓存中,然后将该对象从二级缓存中移除

那么此时B对象也就创建完成,并放入了三级缓存中。

紧接着,就是该对B对象的属性赋值操作了:

B对象只有一个a属性:

B对象的属性赋值操作的详情:

populateBean方法——applyPropertyValues方法

—— valueResolver.resolveValueIfNecessary()方法——resolveReference方法

——this.beanFactory.getBean方法——doGetBean方法——getSingleton方法

——getSingleton(beanName, true)方法

在最后这一个getSingleton(beanName, true)方法里边,可以看到如下代码:

Object singletonObject = this.singletonObjects.get(beanName);

这句代码的意思是先从一级缓存中获取该对象即对象A

因为此时半成品对象A是在三级缓存中的,所以此时会进入if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {...}中,isSingletonCurrentlyInCreation的意思是该对象是否正在创建中,毫无疑问此时对象A和B都是正在创建中的状态。

接着继续执行代码:singletonObject = this.earlySingletonObjects.get(beanName);意思是从二级缓存中获取该对象,此时还是获取不到,最终,会走到这一句代码:

ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);

这个时候是从三级缓存中去取该对象答案是能够取到的。只不过此时从三级缓存中取出来的实际上是一个lambda表达式,所以当取出来的是一个lambda表达式的时候,会紧接着执行singletonObject = singletonFactory.getObject();即通过getObject方法来实际执行lambda表达式(() -> getEarlyBeanReference(beanName, mbd, bean))中的方法——getEarlyBeanReference

点进去getEarlyBeanReference方法,看到如下一段代码:这一段代码的处理逻辑,才是三级缓存的精髓所在

在这里,先将A对象赋值给了exposedObject,

然后如果进入if条件,会修改exposedObject,

如果进不去,就直接返回了exposedObject。

所以刚才的singletonObject = singletonFactory.getObject(); 取到了A对象。

然后接着执行代码:this.earlySingletonObjects.put(beanName, singletonObject);意思是将A对象放入到二级缓存中了。注意此时的A对象还是个半成品呢。

然后执行代码this.singletonFactories.remove(beanName);意思是将A对象从三级缓存中移除

于是,现在的一二三级缓存变成了这个情形:

因为半成品A对象此时在缓存中,所以获取到了A对象,完成了对a属性的赋值操作,

如图:初始化B对象过程中,完成了对a属性的赋值操作:

接着,B对象创建完成,此时的B对象接着就从一个半成品对象成为了一个成品对象了。但还没完,在接着的getSingleton方法的最后还有一句代码

在这个addSingleton方法里边,有这么一段代码:

this.singletonObjects.put(beanName, singletonObject);这一句代码的意思是将B对象放入到一级缓存中,此时的B对象是一个成品对象了

然后是this.singletonFactories.remove(beanName);意思是移除三级缓存中的B对象

然后是this.earlySingletonObjects.remove(beanName);,意思是移除二级缓存中的B对象

最终如图所示:

B对象创建完成之后,接着就意味着A对象的b属性也就接着完成了赋值操作,A对象也就继而创建完成,从一个半成品对象成为了一个成品对象了。

然后也是同样在创建完成之后,执行addSingleton方法,即将A对象放入一级缓存中,然后将三级缓存和二级缓存中的A对象移除。

最终如图所示:

此时,A对象已经创建完成了。注意,刚才B对象的创建以及存入一级缓存中主要是因为A对象的b属性赋值才发生的,所以接下来代码还是会继续创建B对象的,只不过创建B对象的时候发现能够直接从一级缓存中取到,就不再继续走原来的一系列实例化初始化操作了,变为直接从一级缓存中取出来

4,问题

4.1,问题一,

1、三个缓存对象,在获取数据的时候,是按照什么顺序来获取的?

答:先从一级缓存中获取对象,如果没有再从二级缓存中获取,二级缓存中没有再从三级缓存中获取。所以当前面的缓存中存在了对象,那么后面级别的缓存就需要把该对象给清空。

4.2,问题二,

2、如果只有一级缓存,能解决循环依赖问题吗?

不能,如果只有一级缓存,那么成品对象和半成品对象会放到一起,这个是没办法区分的,所以需要两个缓存来分别存放不同状态的对象,一级缓存-存放成品,二级缓存-存放半成品。

4.3,问题三,

3、如果只有一级和二级缓存,能否解决循环依赖问题?

在刚刚的整个流程中,三级缓存一共出现了几次? getsingleton , doCreateBean。

如果对象的创建过程中不包含 aop ,那么一级二级缓存就可以解决循环依赖问题,但是如果包含 aop 的操作,那么没有三级缓存的话,循环依赖问题是解决不了的。

4.4,问题四,

4、为什么添加了AOP的操作之后,就必须需要三级缓存来解决这个问题?

三级缓存加了什么操作?添加了一个 getEarlyBeanReference 的方法。

在创建代理对象的时候,是否需要生成原始对象?

需要。

当创建完成原始对象之后.后续有需要创建代理对象,那么对象在引用的时候应该使用哪一个?

换句话说,就是一个 beanName 对应有两个对象,(原始对象和代理对象)

在整个容器中,有且仅能有一个同名的对象,当需要生成代理对象的时候,就要把代理对象覆盖原始对象

程序是怎么知道在什么时候要进行代理对象的创建的呢?

需要一个类似于回调的接口判断,当需要第一次对外暴露使用的时候,来判断当前对象是否需要去创建代理对象, getEarlyBeanRefenerce 方法的 if 判断,如果需要代理就返回代理对象,如果没有代理就返回原始对象。

4.5,问题五,

为什么添加了AOP的操作之后,就需要添加三级缓存来解决这个问题?

因为在getBean的时候 , getBean——doGetBean——

getSingleton——getSingleton(beanName, true);

会有如下代码:

在这里,当一级缓存和二级缓存中都没有该对象的时候,会在三级缓存中去查找,在三级缓存中查找到了之后,会取到一个lambda表达式,紧接着会执行getObject方法,也就是实际上会调用lambda表达式中的方法——doCreateBean方法里边的这里:

即执行这部分代码里边的getEarlyBeanReference方法:

点进去getEarlyBeanReference方法:

再点进去 bp.getEarlyBeanReference()方法:

此时会到达AbstractAutoProxyCreator类的getEarlyBeanReference方法:

点进去wrapIfNecessary方法:

点进去createProxy方法:

在最后是一个返回代理方式的方法,点进去:

继续点进去:

此时会发现,要么选择jdk代理,要么选择cglib代理,即创建了代理对象——回到刚才的getEarlyBeanReference方法中,说明了bp.getEarlyBeanReference(exposedObject, beanName);这一步的目的是创建代理对象

所以回到最初的从三级缓存中获取到对象的时候,取到的是一个lambda表达式的原因,因为当实际执行lambda表达式的时候,执行的lambda表达式里边的那个getEarlyBeanReference方法,就是在该方法里边,要么执行了bp.getEarlyBeanReference()方法——返回的提前暴露对象exposedObject是该对象的代理对象,要么返回的提前暴露对象exposedObject就直接是该原始对象本身。

所以说,使用了三级缓存就意味着使用了lambda表达式,就意味着可以在实际执行lambda表达式的时候,lambda表达式中的方法要么返回代理对象要么返回原始对象,避免了根据同一个beanName一下子获取到两个对象的错误

所以说,在提前暴露对象的时候可以去判断返回的是原始对象还是代理对象,只返回一个,这就是三级缓存可以解决AOP代理问题的核心。

为什么Spring中除了一级二级缓存之外还有三级缓存,因为要防止AOP代理出现问题:

4.6,问题六

对于循环依赖问题,Spring一定能百分之百解决掉吗?

答,Spring其实也只是提供了一种机制-三级缓存,来解决循环依赖问题,但是对于循环依赖问题,Spring一定能百分之百解决掉吗?不能。

Spring提供了三级缓存只是说能够预防一部分的循环依赖问题,但是不能说就能够预防所有的循环依赖问题。

所以说有的时候,也需要我们去检查写的代码是否存在问题,然后去解决。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值