简单谈谈Spring的IoC以及bean的生命周期
一、前言
这几天正在复习Spring
的相关内容,同时想要对Spring
的实现原理做一些深入的研究。今天看了看Spring
中IoC
的实现,找到了一篇非常详细的博客,研究了一个下午,看完之后唯一的感受就是——太复杂了。Spring
源码中,类和接口的体系非常的复杂,同时方法的实现也是,方法调用感觉无穷无尽,甚至相互调用,给我绕的晕晕的。应该是自己目前的技术水平还太低,项目经验也不足,甚至对Spring
的运用都不够熟悉,所以研究源码对我来说可能还是太早了。
虽然对于Spring
的IoC
,我可能连十分之一都还没有弄懂,但是一个下午的研究也不是毫无收获。这篇博客就来简单讲讲我对IoC
的理解,以及Spring
中,IoC
最基本的实现流程。
二、正文
2.1 什么是IoC
在我们使用传统的编码方式编写代码时,如果在类中需要引用另外一个类的对象,我们一般会直接在类中使用new
关键字,创建这样一个对象。此时,使用这个对象的类,就与这个对象所对应的类产生了耦合性。
IoC
中文名称为控制反转,它就是用来解决上述问题的一种设计思想,它能指导我们设计出耦合性更低,更易于管理的代码。传统的程序,都是在需要使用的地方主动地创建对象,而IoC
的思想却是将创建对象的工作交给IoC
容器去进行。我们告诉IoC
容器,它需要创建那些对象,IoC
容器创建好这些对象,然后主动地将它们注入到需要使用的地方。此时,需要使用对象的那些类,并不主动地获取对象,而且由IoC
容器为它们分配,控制权在IoC
容器手上,这就是控制反转。当然,IoC
容器并不只是控制对象,可以理解为控制外部资源的获取。
IoC
很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC
容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
2.2 IoC和DI的关系
DI
中文名称为依赖注入,它其实和IoC
是相同的概念,或者可以理解为它是IoC
的一种具体的实现方式。IoC
的概念可能比较模糊,控制反转只是一种思想,可能仅仅只是停留在由其他组件控制对象,而不是在使用的地方直接创建这一层面,但是具体如何实现并没有指明。而DI
就是它的一种实现思路,由容器创建对象,并主动将对象注入到需要使用它的地方。2.1
中对IoC
的解释,实际上更加偏向于DI
。
2.3 Spring如何实现IoC
这一块,我就简单地说一说我今天下午在研究Spring
的IoC
源码的过程中,了解到的一些内容。首先,Spring
的IoC
容器,可以简单地理解为就是BeanFactory
接口的一个实现类对象,比如Spring
的应用上下文接口ApplicationContext
就是继承自BeanFactory
,而我们使用较多的ClassPathXmlApplicationContext
就是ApplicationContext
的一个实现类。它们都可以理解为是Spring
的IoC
容器,而BeanFactory
就是这个继承体系中的最高层。下面我就以单例bean的创建,简单地说一说Spring
的IoC
实现的一个过程,假设使用到的是ClassPathXmlApplicationContext
这个容器,解析的是xml
配置文件:
- 容器解析
xml
配置文件,将声明在xml
文件中的bean
的配置信息提取出来,每一个bean
的配置信息被封装成一个BeanDefinitionHolder
对象。BeanDefinitionHolder
主要包含三个成员——BeanDefinition
对象,bean
的名称(id
),以及bean
的别名。BeanDefinition
对象就是用来保存一个bean
的配置信息,比如bean
的作用域,bean
的类型,bean
的依赖,bean
的属性...... - 容器创建一个
ConcurrentHashMap
对象,这个map
的key
是bean
的名称,而value
则是BeanDefinition
对象的引用。容器将第一步中解析出的每一个BeanDefinitionHolder
对象,它对应的bean
名称,以及拥有的BeanDefinition
引用放入这个map
中。所以,这个map
保存了我们在程序中声明的所有的bean
的配置信息。 - 容器初始化完成后,开始创建
bean
,遍历第二步中的map
集合,获取到bean
的名称以及对应的BeanDefinition
对象,通过BeanDefinition
对象中的信息,判断这个bean
有没有定义为延迟加载,若没有,则需要现在就进行创建。在创建前,先通过BeanDefinition
中的配置信息,判断此bean
有没有depend-on
(依赖)其他bean
,若有,则先创建当前bean
依赖的bean
(此处的depend-on
不是bean
的属性,而是需要通过配置项进行配置的)。之后,则创建当前遍历到的bean
。 bean
在创建完成后,通过bean
的BeanDefinition
对象,获取bean
需要注入值的属性,然后为属性赋值,若属性的值类型是其他的bean
,则以上面相同的步骤,创建属性对应的bean
;- 容器创建一个
ConcurrentHashMap
,将创建好的单例bean
保存在其中。我们在代码中可以通过容器的getBean
方法,传入bean
的名称或类型获取单例bean
。容器会先去这个map
中查找,若map
中不存在,且这个bean
的配置在第2
步中存储配置信息的map
中能够找到,则创建这个bean
,放入存储bean
的map
中(因为bean
可以配置延迟加载,即第一次获取时加载);
Spring
中,bean
最基本的两种作用域就是singleton
(单例)和prototype
(多例),默认为单例。以上过程是单例bean
的创建过程,若作用域为prototype
,则每一次调用getBean
方法,都会创建一个新的bean
。顺带一提,创建bean
的方式是通过反射机制,这个大部分人应该都知道。
2.4 Bean的生命周期
上面介绍了一下IoC的实现,而针对每一个单独的Bean,Spring容器如何处理?这就涉及到Bean的生命周期了。这里就来简单介绍一下Bean是生命周期,首先通过一张图了解:
上面这张图的描述如下:
-
首先由
Spring
容器创建bean
实例; -
为
bean
的属性注入值; -
若
bean
实现了各类Aware
接口,则调用相应的set
方法,比如说,实现了BeanNameAware
接口的bean
,此时容器将调用bean
的setBeanName
方法,将bean
的name
作为参数;实现了ApplicationContextAware
接口的bean
,此时容器将执行bean
的setApplicationContext
方法,将bean
所在的上下文对象作为参数…… -
若容器中包含实现了
BeanPostProcessor
接口的bean
,则此时将调用这些bean
的postProcessBeforeInitialization
方法,将当前正在创建的bean
的引用以及bean
的name
作为参数传入; -
若
bean
实现了InitializingBean
接口,此时将调用bean
的afterPropertiesSet
方法; -
若
bean
指定了自定义的初始化方法,比如说通过配置文件的init-method
选项,则此时将执行这个自定义初始化方法; -
若容器中包含实现了
BeanPostProcessor
接口的bean
,则此时将调用这些bean
的postProcessAfterInitialization
方法,将当前正在创建的bean
的引用以及bean
的name
作为参数传入; -
这个时候,
bean
就算是初始化完毕,可以被使用了,在容器销毁之前,这个对象将一直保存在容器中; -
若
bean
实现了DisposableBean
接口,则在容器销毁时,会调用bean
的destroy
方法; -
如果
bean
定义了自定义的销毁方法,则在容器销毁时,会调用这个自定义的销毁方法; -
容器销毁成功,
bean
也被回收;
以上步骤中,比较重要的一个部分就是BeanPostProcessor接口的部分,关于这个接口,我专门写了一篇博客,详情请参考:谈谈Spring中的BeanPostProcessor接口。
三、总结
以上内容是我根据自己的认识,对Spring
的IoC
做的一次简单记录,内容并不全面,因为我目前对它的理解也比较浅显。在今天阅读Spring
源码的过程中,我发现它真的比我想象中要复杂很多,或许是我水平有限,又或许是没有掌握阅读源码的方法,读起来真的非常吃力。总而言之,想要真正读懂Spring
,我还需要很多的学习,希望今后能够尽快提升自己,早日将Spring
吃透。