今天我们要聊的是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/
网友评论