前言:
当初第一次看spring源码的时候,即便有书有教程,依旧看的一头懵逼,即便到现在,也依旧不敢说自己已经精通spring源码了。这一次,我想从头开始细细的重新开始阅读一遍spring的源码,包括IOC,AOP,MVC,及一些常用模块事务、缓存等模块的源码阅读。也希望能给正在学习spring源码的同学们一个参考。
now!Let's go!
首先,我会当做大家对于很基础的东西都已经了如指掌,比如IOC的入口在哪,是怎么进入的。第一部分是IOC源码的阅读,我就直接从AbstractApplicationContext的Refresh()方法直接开始了。
那么我们这一篇博客,主要就是阅读这里的子方法,那么我们一个一个来。每一个方法,我都会说明重不重要(仅个人观点,欢迎指正)
1.prepareRefresh(),这个方法不是一个重要的方法,是为容器初始化做准备的,但是为了一些强迫症患者,我们也点进去看一看。
这里做了些什么,其实可以望文生义,设置了容器的状态标识,closed和active都是boolean类型来标识容器状态的。初始化了配置源,获取了容器的Environment环境对象校验一些properties,这些确实不用去深究,没有什么意义。学习spring源码,切记不能有强迫症,你想每个细节都面面俱到的看到,是不对的。特别是当你主流程源码都没清楚的情况下。
好了。我们来看下一个方法。
2.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();这个方法很重要!很重要!很重要!
第一眼看过去,你应该就能明白,他是创建beanFactory对象的,beanFactory是干嘛的应该都知道的吧,就是管理spring容器中所有bean对象的。这次咱们是细读,所以我尽量把我觉得重要的都讲一下,也难保有同学都不知道beanFactory具体是这个什么东西。不多说,先点进去再说,看看到底它是这个什么玩意儿。
一看,哇,舒服了,只有2个子方法。
然而当我们想点进去看一看的时候,发现这里有2个实现,看过源码的同学都知道,我们这里要看第一个实现类。那么问题来了,GenericApplication这个类是干嘛的?我相信很多同学都不知道。那么,我们这里先跳出流程,看一看这个我们很少去看的这个上下文是干嘛的。
第一步,了解一个类是干嘛的,先看注释!那么有的同学就要说!啊,我英语这么烂,看不懂,咋整,有道翻译干嘛用的?大家不要嫌麻烦,我知道很多同学学习的时候不想自己去动手了解,光想着谁来告诉我这是干嘛的就行了。这样的学习态度真的不对哦。自己去复制一下到翻译软件上累吗?麻烦吗。我希望看到我这篇博客的同学都能养成不怕麻烦的习惯,永远不要等着别人告诉你答案,其实是很简单的事。下面就是翻译的过程,你看,有道翻译还是挺靠谱的,咱们就看注释的前两段内容,你就能明白它是干嘛的了。
通用的ApplicationContext实现,它持有单个内部实例,并且不假定特定的bean定义格式,实现接口以允许对其应用任何bean的定义读取器。典型的用法是通过接口注册各种bean定义,然后使用应用程序上下文语义调用来初始化这些bean。
其实看完翻译,一些有基础的同学就知道大概是干嘛的了。纳尼?还不明白?行!
咱们再看它给出的使用example
这段example代码看的懂吧???new出这个对象,指定Xml解析器,指定资源文件路径,然后refresh()刷新容器,最后就可以使用了。所以这个上下文就给我们用的,可以直接new,它的构造函数中,从一开始就实例化了可用的beanFactory。
简单的说就是自己new出一个上下文对象,我们在自己的项目中也可以这样去使用这个上下文,如果需要的话。
好了。这下那些强迫症的同学应该舒服了。那么我们回归正题。
回到AbtractRefreshableApplicationContext的refreshBeanFactory()方法,看看它到底干了什么。为了方便阅读,我都写上了注释,每一步是干什么的。这里很简单,稍微有点英语基础的,甚至看方法名就知道是做什么的。
这里最重要的是解析xml,并封装成BeanDefinition对象,什么是BeanDefinition对象?见名思意,封装了bean信息的对象。
回想我们最开始使用spring,会在xml文件里配置bean信息,对spring容器的理解就是加载我们配置的xml文件,把xml文件中的bean信息通过反射实例化出对象,丢到上下文容器中,容器是一个大map,bean的id作为key,具体对象作为value,是吧?大多数同学最开始使用spring时候的理解都是这样的吧。由于篇幅的关系,这里不再点进去看,因为这个方法理解起来很简单,我希望大家自己点进去看看它的逻辑,它是怎么解析xml文件,怎么封装成beanDefinition对象。并且,再去看看BeanDefinition对象,都有些什么属性,这些属性对应的都是什么,这里我稍微举个例子
这些属性,熟悉吗看名字。不论是xml配置,还是通过注解形式,都用过吧,这些属性,对,没错,spring就是把这些配置或者Class上面的注解,封装到BeanDefinition的属性中,用于之后的实例化。
这里是load封装的代码一定要看,这里之所以不贴源码是因为这一段代码实在是涉及方法跳转太多了,大家可以自行一直点进去点进去,我把最关键的地方给大家贴出来,希望大家能跟到这里
这里大家一定要自己花时间去跟着阅读理解。
So,One Thousand Years Later.........
好嘞,继续
这里,为了帮助理解记忆,咱们要举一反三。回头看看刚才我们讲过的GenericApplicationContext,它不是也有这个方法吗,它干嘛了。
然而我们首先看到的就是do noting!大家想想,思考一下,为什么,它什么都不做?再结合刚才看过的example。这个上下文是我需要我们自己去手动fresh的,且不支持多次刷新,只需要调用一次。再结合上面这个上下文在refreshBeanFacotry()方法里的做的事,你有没有发现,它做的事,和我们使用GenericApplicationContext做的事情差不多呢,只不过后者变成了手动。
好,这里就不过多展开了,如果有说的不对的地方,欢迎大佬们指出。我们接着看下个子方法
看名字知道吧,获取beanFactory,上一个方法是创建了beanFactory,那么这个方法就是拿到beanFactory返回,很简单。
这里唯一值得思考的是,为什么这里用syncronized包住了这段代码,不过我想大家应该都能明白,留给大家自己思考,如果不加锁,会发生什么问题,如果可以的话可以发表评论哦,看看大家的理解怎么样。我们赶紧进入下一个方法。
3.prepareBeanFactory(beanFactory) 准备beanFactory
上一个方法中,我们创建了beanFactory并且返回拿到了该对象,但是beanFacotory肯定有很多属性,需要去设置,就像我们平常使用自己new出来的对象,会做一些set操作设置下属性,那么它也一样,走,进去看看。
哇!这巴拉巴拉的。一大段的set 、 registry、add等方法,即便你不知道每一行代码设置或者注册添加的是什么东西,你也该明白它大概是做什么事情了的吧,什么?不知道?我怀疑你是在杠。行吧,我懂你的意思,我给你们翻译成中文。
那么就有同学问了。这些类都是干嘛的呀。什么applicationlistener,LoadTimeWeaver啊等等这些都是干嘛的嘞?
emmmm...诶?我们先大概对这些东西有个影响,咱们先不说。那么又有同学说了,你是不是自己也不知道啊?嗯?不,这里我要告诉大家一个事,spring做任何事都是有原因,它在这里注册了很多莫名其妙的东西,我们不知道干嘛的,但是它自己会在后面让我们知道这些东西到底是干嘛的。当我们下次再见到这些“组件”,想起现在看到的这些陌生的单词,你才会印象深刻。总之,我们这里先跳过,反正我们知道了这里给beanFactory设置了很多属性,注册了很多bean实例。go on!
4.postProcessBeanFactory(beanFactory)这是个个人我觉得是个说重要也重要,说不重要也不重要的方法。
之所以重要是因为这是我们了解spring中postProcessor的开始,很多同学肯定都听过这个东西,或者经常在网上看到听到什么后置处理器啪啦啪啦的。这里先给出一个我自己的总结,该方法的作用就是允许在上下文子类中对bean工厂进行后处理。说不重要,也确实不重要,我想大多数同学都不会自己去对beanFacotory做什么修改吧。这里我们先埋个点,大家也不用点进去看先。我们先了解它的做什么的。之后回头来看。这不是我不负责,相信我,spring中,很多东西,如果你选择按部就班的一步步看,非要弄懂每一步,然后再看下一步,是学不好的。这也是spring源码阅读的难点,需要经常的前后对应着看。现在如果你点进去看会发现,这个方法很简单,但是你完全看不懂它的意思。所以,不要在这里纠结,总之记住一句话。该方法的作用就是允许在上下文子类中对bean工厂进行后处理。
5.invokeBeanFactoryPostProcessors(beanFactory);调用上下文中注册为bean的工厂处理器
这里我们点进去,即将会看到spring中之后会经常看到的一个玩意儿。
这里唯一一个方法,点进去。然后我来挑个重点
这段代码,你需要了解的一个接口是BeanDefinitionRegistryPostProcessor,这里,我有个问题。你们使用过这个接口吗?
上面我们有说过beanDefinition是干嘛的,我当做大家已经了解beanDefinition是什么,并知道了它的作用。那我们应该清楚,spring容器已经对beanDefinition做了缓存。那如果了解spring的同学都知道,spring的扩展很牛逼,那么一开始它加载好beanDefinition信息后,肯定会给你留个可以做修改的机会,喏,机会来了。BeanDefinitionRegistryPostProcessor这个接口我相信很多同学都用过,当你继承了这个接口就能拿到BeanDefinition的注册器,可以对其做修改。那么很明显,BeanDefinitionRegistry其实也是个beanFacotory
之后我们还会看到很多postProcessor的这样的调用。这里先开个头。在这个方法里,大家可以只需要先了解这么多。
继续走下去,看到了一个判断
loadTimeWeaver??熟悉不,刚才设置beanFacotory属性的时候是不是见过,我说过当时让大家先记着不要深究的,现在回过头来看。这2段代码是不是基本一样?目的就是把LoadTimeWeaverAwareProcessor这个处理器添加到beanFacotory中,那么按照刚才对于beanDefinition处理的了解,我们猜都猜得到,未来肯定会出现这么一段代码
for(循环所有的postProcessor){
如果有LoadTimeWeaverAwareProcessor,则执行
}
对吧,那么问题来了。LoadTimeWeaverAwareProcessor到底是干嘛的。这个问题就留给大家自己去思考了。哈哈,因为这跟spring源码的主流程没啥关系,看源码真的不能面面俱到的看。
好了,我们进入下一个方法
6.registerBeanPostProcessors(beanFactory);往beanFactory中注册各种BeanPostProcessors
重要代码如下
这个方法呢,就是拿到BeanFactory 中所有注册的 BeanDefinition 对象的名称 beanName,然后通过getBean方法实例化这些postProcessor,所以大家要记住,这些postProcessor的bean实例化是在我们自己需要交给spring管理的bean的实例化之前发生的。而这个实例化过程和我们自己的bean的实例化过程其实是一样的。好了,这里我们也只需要了解是做什么的,实例化的过程,会着重在下一篇说。
7.接下去这几个方法
我希望大家先别看,之后我有时间的话,在整个流程讲完之后,回头再扩展说一下。值得一提的是onRefresh()方法,这是个空方法,在springboot中,内置的tomcat就是在onRefresh()里创建的。
好了,IOC过程的前面的铺垫,我觉得铺垫的已经差不多了,接下去就是真正的开始IOC实例化的过程了。
我决定用单独一篇来讲解实例化的源码。
下一篇见,希望大家把前面的这些源码都深刻理解体会一下。