细说Spring——IoC详解(Bean的生命周期)
我们在细说Spring——IoC详解(一)和细说Spring——IoC详解(二)中了解了容器启动阶段所做的事情,还有在容器启动阶段我们可以做的扩展,同时笼统的了解了一下容器是怎么创造出一个对象之后,我们接下来就要进入Bean
的实例化阶段,同时学习一下在Bean
的实例化阶段我们可以做哪些扩展。
首先我们先来看一个图:
这个图是《Spring揭秘》中的一个图,我们接下来就可以对照这个图来了解
Bean
的实例化过程。
1、Bean的实例化和属性设置
当我们完成了容器的启动阶段后,对于BeanFactory
来说,并不会马上实例化相应的bean
定义。我们知道,容器现在仅仅拥有所有对象的BeanDefinition
来保存实例化阶段将要用的必要信息。只有当请求方通过BeanFactory
的getBean()
方法来请求某个对象实例的时候,才有可能触发Bean
实例化阶段的活动BeanFactory
的getBean()
法可以被客户端对象显式调用,也可以在容器内部隐式地被调用。隐式调用有如下两种情况:
- 对于
BeanFactory
来说,对象实例化默认采用延迟初始化。通常情况下,当对象A
被请求而需要第一次实例化的时候,如果它所依赖的对象B
之前同样没有被实例化,那么容器会先实例化对象A
所依赖的对象。这时容器内部就会首先实例化对象B
,以及对象 A
依赖的其他还没有实例化的对象。这种情况是容器内部调用getBean()
,对于本次请求的请求方是隐式的。 -
ApplicationContext
启动之后会实例化所有的bean
定义,但ApplicationContext
在实现的过程中依然遵循Spring
容器实现流程的两个阶段,只不过它会在启动阶段的活动完成之后,紧接着调用注册到该容器的所有bean
定义的实例化方法getBean()
。这就是为什么当你得到ApplicationContext
类型的容器引用时,容器内所有对象已经被全部实例化完成。不信你查一下类org.AbstractApplicationContext的refresh()
方法。
容器在实现Bean
的实例化的时候,采用“策略模式(Strategy Pattern)
"来决定采用何种方式初始化bean
实例。通常,可以通过反射或者CGLIB
动态字节码生成来初始化相应的bean
实例或者动态生成其子类。
这里就涉及到了一些AOP
的知识,我们只需要知道在容器中并不是直接通过new
的方式来添加对象,而是通过AOP
实现机制,创造了一个目标对象的代理对象就可以了,代理对象可以简单理解为目标对象的子类,他要么和目标对象有相同的功能,要么能力比目标对象强大,AOP
我会在以后进行详细讲解。
但是容器也并不是直接就创造了一个代理对象,他还把这个代理对象包装了一下,他把代理对象包装成了一个BeanWrapper
。
BeanWrapper
定义继承了org.springframework.beans.PropertyAccessor
接口,可以以统一的方式对对象属性进行访问;BeanWrapper
定义同时又直接或者间接继承了PropertyEditorRegistry
和TypeConverter
接口。不知你是否还记得CustomEditorConfigurer
?当把各种PropertyEditor
注册给容器时,知道后面谁用到这些PropertyEditor
吗?对,就是BeanWrapper
!在第一步构造完成对象之后,Spring
会根据对象实例构造一个BeanWrapperImpl
实例,然后将之前CustomEditorConfigurer
注册的PropertyEditor
复制一份给BeanWrapperImpl
实例(这就是BeanWrapper
同时又是PropertyEditorRegistry
的原因)。然后我们就可以通过BeanWrapper
来为对象设置属性了。
2、Aware接口
到这里图中的前两个过程就已经走完了,接下来,容器会检查当前对象实例是否实现了一系列的以Aware
命名结尾的接口定义。如果是,则将这些Aware
接口定义中规定的依赖注入给当前对象实例。我们可以看一看这些Aware
对象到底规定了什么依赖,对于BeanFactory
来说,Aware
接口有一下几个:
-
org.springframework.beans.factory.BeanNameAware
。如果Spring
容器检测到当前对象实例实现了该接口,会将该对象实例的bean
定义对应的beanName
设置到当前对象实例。 -
org.springframework.beans.factory.BeanClassLoaderAware
。如果容器检测到当前对
象实例实现了该接口,会将对应加载当前bean
的Classloader
注入当前对象实例。默认会使用加载org.springframework.util.ClassUtils
类的Classloader
。 -
org.springframework.beans.factory.BeanFactoryAware
。如果对象声明实现了
BeanFactoryAware
接口,BeanFactory
容器会将自身设置到当前对象实例。这样,当前对象实例就拥有了一个BeanFactory
容器的引用,并且可以对这个容器内允许访问的对象按照需要进行访问。
对于ApplicationContext
类型的容器,也存在几个Aware
相关接口。如下:
-
org.springframework.context.ResourceLoaderAware
。ApplicationContext
实现了Spring
的ResourceLoader
接口。当容器检测到当前对象实例实现了ResourceLoaderAware
接口之后,会将当前ApplicationContext
自身设置到对象实例,这样当前对象实例就拥有了其所在ApplicationContext
容器的一个引用。 -
org.springframework.context.ApplicationEventPublisherAware
。ApplicationContext
作为一个容器,同时还实现了ApplicationEventPublisher
接口,这样,它就可以作为ApplicationEventPublisher
来使用。所以,当前ApplicationContext
容器如果检测到当前实例化的对象实例声明了ApplicationEventPublisherAware
接口,则会将自身注入当前对象。 -
org.springframework.context.MessageSourceAware
。ApplicationContext
通过MessageSource
接口提供国际化的信息支持,即I18n(Internationalization)
。它自身就实现了MessageSource
接口,所以当检测到当前对象实例实现了MessageSourceAware
接口,则会将自身注入当前对象实例。 -
org.springframework.context.ApplicationContextAware
。 如果ApplicationContext
容器检测到当前对象实现了ApplicationContextAware
接口,则会将自身注入当前对象实例。
在了解了这些Aware
接口的功能后,我们可能会想容器是如何实现将Aware
接口中规定的依赖注入到已经生成的对象中的呢?这里就要引出我们在容器实例化阶段的扩展点了,那就是BeanPostProcessor
3、BeanPostProcessor
与BeanFactoryPostProcessor
通常会处理容器内所有符合条件的BeanDefinition
类似,BeanPostProcessor
会处理容器内所有符合条件的实例化后的对象实例。
我们已经知道BeanFactoryPostProcessor
是在容器启动阶段,对象还未创建之前对创建对象的信息就是BeanDefinition
进行了修改,那么BeanPostProcessor
是如何对一个已经生成的对象进行扩展的呢,这里当然就要用到AOP
了,看来,Spring
中的IoC
和AOP
真是“你中有我,我中有你”啊。
我们ApplicationContext
对应的那些Aware
接口实际上就是通过BeanPostProcessor
的方式进行处理的。当ApplicationContext
中每个对象的实例化过程走到BeanPostProcessor
前置处理这一步时,ApplicationContext
容器会检测到之前注册到容器的ApplicationContextAwareProcessor
这个BeanPostProcessor
的实现类,然后就会调用其postProcessBeforeInitialization()
方法,检查并设置Aware
相关依赖。
至于如何将BeanPostProcessor
注册到容器中,BeanFactory
需要手动的写代码注入,而ApplicationContext
可以通过配置文件的方式注入,下面是代码实现:
BeanFactory
ConfigurableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource(...));
beanFactory.addBeanPostProcessor(new PasswordDecodePostProcessor());
ApplicationContext
<beans>
<bean id="passwordDecodePostProcessor" class="package.name.PasswordDecodePostProcessor">
<!--如果需要,注入必要的依赖-->
</bean>
</beans>
4、init-method
通俗的将,init-method
可以指定我们在容器中的获得的对象在执行任何方法前,先执行那个方法。比如我们在做任何事情前必须要先洗手,那么我可以把洗手定义为init-method
,那么我们在做吃饭,睡觉,玩游戏,写代码。。。。之前都会去洗手。下面演示一下如何定义init-method
:
<bean id="tradeDateCalculator" class="FXTradeDateCalculator" ➥
init-method="setupHolidays">
这里只需要保证FXTradeDateCalculator
类中有一个setupHolidays
方法就可以了。
5、destory-method
和init-method
对应,destory-method
定义的是在所有这个对象被销毁前,需要做的方法。
这里就不具体的举例子了,如果想了解更多关于destory-method
的相关知识,可以参考:Spring中的destroy-method方法