spring原理分析(一)—spring-web环境的初始化

       今天我们要聊的是spring的基础模块bean的初始化。bean即java的对象,是一切程序的基础。bean的初始化主要将会涉及到spring的三个核心模块:Core、Context和Beans。

      java作为面向对象编程语言中的领导者,所有的设计思想根本上都是基于一个个鲜活的对象个体。就向我们了解的jvm中的新生的、老年代等等一样,java中的每个对象都是有生命的,生老病死在所难免。而如何让这一个个鲜活的个体有秩序的工作,并维护他们之间复杂的关系网,是一件让人头痛的事。spring的beans模块恰恰为我们解决的就是这部分问题。

一、 先来谈谈spring-web的整体初始化流程

  spring解决上述问题正是通过beans、core和context(expression表达式模块这里没有算在内)三个模块来实现的。

      如上图,可以看出spring的相关核心的三个模块,如果把整个spring比做一个世界的话:

      (1)context作为整个应用的环境,spring通过他和整个web应用相关联,他通过 ContextLoaderListener监听器,获取整个web应用的相关的环境信息(ServletContext),加载对应spring内部定义的所有bean,并创造了所有bean的生活环境。  -----------即context就是这个社会环境。

      (2)beans相当于java应用的每个成员-----即这个社会中一个鲜活的生命

     (3)core相当于spring内部工具化的组件,包含了spring中核心的功能处理类。-------即社会上的各种工具。

首先我们来看下context和beans模块的整体类图:

从中让我们抽出核心的几个处理类来:

由上图,我们可以总结出简化版的总体时序图:




    由此可以总起出spring-web的整体流程大概是:

        第一步:当web项目启动后,通过web.xml的配置的监听器ContextLoaderListener,开始加载整个spring环境。他首先生成xmlWebApplicationContext,目的是为了加载指定的applicationContext.xml文件,但显然这时候还并没有真正解析文件,因为spring中的bean之间会有这种各种各样的复杂关系、以及很多用户可扩展处,所以对于bean的加载,spring是通过工厂模式+描述定义的方式来实现。

         第二步:工厂即生成bean的源头,描述定义相当于对每个产品的设计图纸。因此此时首先要做的是先得有个工厂才行,所以通过AbstractApplicationContext的refreshBeanFactory的方法来建造,建造工厂的过程中将会同时通过加载xml文件生成所有需要初始化的单例bean的图纸(bean描述)。

        第三步:开始生产bean工厂中所有单例类,这个类制作过程中要考虑互相依赖、循环依赖、各种Processor和初始方法定义等,是一个很复杂的过程,下边会有相关介绍.

    根据上面的分析,我们基本已经了解spring的主体流程,对一些核心类有了大概了解。下面开始对spring的各个模块进行源码解析。

二 、context初始化

            context环境的初始化,是整个流程的起点。通过上面的分析我们已经知道,入口就是ContextLoaderListener。ContextLoaderListener中的初始化方法将会产生一个基于xml解析的context,此context有个DefaultListableBeanFactory工厂,这个工厂即当前环境下的bean工厂,核心代码包括:

  (1)调用ContextLoader.initWebApplicationContext(ServletContext servletContext)方法,进行初始化,

        configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac,ServletContext sc)方法中如下


ConfigurableWebApplicationContext的refresh()方法中将会进行整个bean环境的加载逻辑:

  真正的bean初始逻辑主要在  ConfigurableWebApplicationContext.finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)方法中调用ConfigurableListableBeanFactory.preInstantiateSingletons()子方法,此方法中将会对单例bean进行处理:

下面将会真正进入getBean的逻辑。

三、bean的初始化


接上文,bean的初始化将通过ConfigurableListableBeanFactory.preInstantiateSingletons中调用getBean获取对应实体,然后继续将会从AbstractBeanFactory的doGetBean(finalString name, finalClass requiredType, finalObject[] args, booleantypeCheckOnly)方法进行bean 的初始化,此时DefaultSingletonBeanRegistry中的几个属性是至关重要的:

spring在初始bean时会利用这几个属性来解决循环依赖的问题,继续看doGetBean:

图一
图二

通过上边的实现,我们发现了两个getSingleton()方法,这正是实现bean初始化,同时解决循环依赖问题的关键:

(1)第一个getSingleton()方法是在进入doGetBean方法第一步开始调用,此时先尝试singletonObjects从读取bean,不存在尝试从singletonFactories中获取(注意如果此时工厂存在,代表此bean正在创建中),然后放到earlySingletonObjects中,供那些依赖于此bean的类使用,此时singletonFactories中将会清除此工厂,其使命已完成。

(2)第二个getSingleton()则是真正创建bean的地方,主要实现代码都在此时定义的singletonFactory的getObject()方法中,下面将会具体说明。

下面为第二个getSingleton()方法的实现,可以看出spring初始化一个bean首先会先标记处此bean正在使用,初始化完后会移除此标记,重要的创建bean在工厂的getObject方法中:

下面我们看看beanFacoty中都做了什么,查看源码,我们发现核心逻辑最后落到了AbstractAutowireCapableBeanFactory.doCreateBean(finalString beanName, finalRootBeanDefinition mbd, finalObject[] args)方法中:

    此时需要的bean将会被创建。

     下面通过流程图,我们来回顾下bean的获取和循环依赖的解决方法:

假如此时有两个Bean:A和B,B是A中的一个属性,此时先初始A时:

四、Ioc 容器的扩展点(此处摘录自文章三)

现在还有一个问题就是如何让这些 Bean 对象有一定的扩展性,就是可以加入用户的一些操作。那么有哪些扩展点呢? Spring 又是如何调用到这些扩展点的?

对 Spring 的 Ioc 容器来说,主要有这么几个。BeanFactoryPostProcessor, BeanPostProcessor。他们分别是在构建 BeanFactory 和构建 Bean 对象时调用。还有就是 InitializingBean 和 DisposableBean 他们分别是在 Bean 实例创建和销毁时被调用。用户可以实现这些接口中定义的方法,Spring 就会在适当的时候调用他们。还有一个是 FactoryBean 他是个特殊的 Bean,这个 Bean 可以被用户更多的控制。

这些扩展点通常也是我们使用 Spring 来完成我们特定任务的地方,如何精通 Spring 就看你有没有掌握好 Spring 有哪些扩展点,并且如何使用他们,要知道如何使用他们就必须了解他们内在的机理。可以用下面一个比喻来解释。

我们把 Ioc 容器比作一个箱子,这个箱子里有若干个球的模子,可以用这些模子来造很多种不同的球,还有一个造这些球模的机器,这个机器可以产生球模。那么他们的对应关系就是 BeanFactory 就是那个造球模的机器,球模就是 Bean,而球模造出来的球就是 Bean 的实例。那前面所说的几个扩展点又在什么地方呢? BeanFactoryPostProcessor 对应到当造球模被造出来时,你将有机会可以对其做出设当的修正,也就是他可以帮你修改球模。而 InitializingBean 和 DisposableBean 是在球模造球的开始和结束阶段,你可以完成一些预备和扫尾工作。BeanPostProcessor 就可以让你对球模造出来的球做出适当的修正。最后还有一个 FactoryBean,它可是一个神奇的球模。这个球模不是预先就定型了,而是由你来给他确定它的形状,既然你可以确定这个球模型的形状,当然他造出来的球肯定就是你想要的球了,这样在这个箱子里尼可以发现所有你想要的球

通过上面的分析我们能初步了解spring的整体流程,具体细节还需对照源码仔细分析,下面列出几篇文章,有更加详细的流程进行代码分析:

(1)https://www.iflym.com/index.php/code/201208280001.html

(2)https://www.iflym.com/index.php/code/201208290001.html

(3)https://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容