细说Spring——IoC详解(Bean的生命周期)

我们在细说Spring——IoC详解(一)细说Spring——IoC详解(二)中了解了容器启动阶段所做的事情,还有在容器启动阶段我们可以做的扩展,同时笼统的了解了一下容器是怎么创造出一个对象之后,我们接下来就要进入Bean的实例化阶段,同时学习一下在Bean的实例化阶段我们可以做哪些扩展。
首先我们先来看一个图:
这里写图片描述
这个图是《Spring揭秘》中的一个图,我们接下来就可以对照这个图来了解Bean的实例化过程。

1、Bean的实例化和属性设置

当我们完成了容器的启动阶段后,对于BeanFactory来说,并不会马上实例化相应的bean定义。我们知道,容器现在仅仅拥有所有对象的BeanDefinition来保存实例化阶段将要用的必要信息。只有当请求方通过BeanFactorygetBean()方法来请求某个对象实例的时候,才有可能触发Bean实例化阶段的活动BeanFactorygetBean()法可以被客户端对象显式调用,也可以在容器内部隐式地被调用。隐式调用有如下两种情况:

  • 对于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定义同时又直接或者间接继承了PropertyEditorRegistryTypeConverter接口。不知你是否还记得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。如果容器检测到当前对
    象实例实现了该接口,会将对应加载当前beanClassloader注入当前对象实例。默认会使用加载org.springframework.util.ClassUtils类的Classloader
  • org.springframework.beans.factory.BeanFactoryAware。如果对象声明实现了
    BeanFactoryAware接口,BeanFactory容器会将自身设置到当前对象实例。这样,当前对象实例就拥有了一个BeanFactory容器的引用,并且可以对这个容器内允许访问的对象按照需要进行访问。

对于ApplicationContext类型的容器,也存在几个Aware相关接口。如下:

  • org.springframework.context.ResourceLoaderAwareApplicationContext实现了SpringResourceLoader接口。当容器检测到当前对象实例实现了ResourceLoaderAware接口之后,会将当前ApplicationContext自身设置到对象实例,这样当前对象实例就拥有了其所在ApplicationContext容器的一个引用。
  • org.springframework.context.ApplicationEventPublisherAwareApplicationContext
    作为一个容器,同时还实现了ApplicationEventPublisher接口,这样,它就可以作为ApplicationEventPublisher来使用。所以,当前ApplicationContext容器如果检测到当前实例化的对象实例声明了ApplicationEventPublisherAware接口,则会将自身注入当前对象。
  • org.springframework.context.MessageSourceAwareApplicationContext通过MessageSource接口提供国际化的信息支持,即I18n(Internationalization)。它自身就实现了MessageSource接口,所以当检测到当前对象实例实现了MessageSourceAware接口,则会将自身注入当前对象实例。
  • org.springframework.context.ApplicationContextAware。 如果ApplicationContext容器检测到当前对象实现了ApplicationContextAware接口,则会将自身注入当前对象实例。

在了解了这些Aware接口的功能后,我们可能会想容器是如何实现将Aware接口中规定的依赖注入到已经生成的对象中的呢?这里就要引出我们在容器实例化阶段的扩展点了,那就是BeanPostProcessor

3、BeanPostProcessor

BeanFactoryPostProcessor通常会处理容器内所有符合条件的BeanDefinition类似,BeanPostProcessor会处理容器内所有符合条件的实例化后的对象实例。

我们已经知道BeanFactoryPostProcessor是在容器启动阶段,对象还未创建之前对创建对象的信息就是BeanDefinition进行了修改,那么BeanPostProcessor是如何对一个已经生成的对象进行扩展的呢,这里当然就要用到AOP了,看来,Spring中的IoCAOP真是“你中有我,我中有你”啊。

我们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方法

猜你喜欢

转载自blog.csdn.net/q982151756/article/details/80299547