Spring Bean的实例化过程

Spring Bean的实例化过程

Spring的Bean的一生可以分为两个阶段

  • 容器启动阶段:做了很多针对后续实例化过程的预热工作
  • bean实例化阶段

首先看容器启动阶段

在这里插入图片描述

1、首先是需要加载配置元信息,何为配置元,当我们手动new一个Person对象时,我们需要给这个对象set(id name sex)一些属性才能用,这些属性即是配置元信息,手动的时候是通过set方式注入,如果交给Spring来管理,可以通过xml文件、properties文件或者注解的方式设置配置元信息。

2、这些配置元信息都是设置在配置文件上的,那么在Java中就需要将这些配置元信息给读出来,读在内存之中,在Java中万物皆对象,这些配置元信息当然也是以对象的形式存在,并且不可能是Person、Animal、Sutdent这样的特殊对象,为了方便Spring管理,当然是使用了一个统一的对象来保存这些配置元信息,这个对象就是BeanDefination。

3、有了配置元信息,又有了存放配置元信息的对象,当然就需要一个主语去将配置元信息加载到存放配置元信息的对象BeanDefination中,这个主语就是BeanDefinationReader对象。并且我们知道设置配置元信息的方式有很多,这个对象是如何看懂这些不同的配置方式的呢?这就是BeanDefinationReader家族:XmlBeanDefinationReader、PropertiesBeanDefinationReader和AnnotatedBeanDefinationReader。通过名字我们也能看出来这些对象都是干什么用的。BeanDefinationReader的作用就是加载配置元信息,并将其转化为内存形式的BeanDefination,存在某一个地方

4、至于是存在哪里,当然就是保存在内存中啦,但是这些BeanDefination肯定是需要被管理的,不然当我们需要某一个对象时,在内存中挨个地方找肯定是不现实的,管理这些BeanDefination的对象就是BeanDefinationRegistry,从名字上也可以猜出来,Spring通过BeanDefinationReader将配置元信息加载到内存生成相应的BeanDefination之后,就将其注册到BeanDefinationRegistry中,BeanDefinationRegistry就是一个存放BeanDefination的大篮子,它也是一种键值对的形式,通过特定的Bean定义的id,映射到相应的BeanDefination。

5、事实上Spring在启动阶段对Bean的准备工作在BeanDefiantionRegistry这步主体任务就可以看作是结束了,但是随着Spring的功能上的增强,就又有了一个扩展出来的对象BeanFactoryPostProcessor,这个对象的作用就是负责对注册到BeanDefiantionRegistry中的BeanDefination对象进行一定程度上的修改与替换,举个例子,XML文件是支持通过占位符的方式配置配置元信息的,也就是$ {}表达式,BeanFactoryPostProcessor对象要做的工作就是替换$占位符为配置文件中的真实的数据。

这样Spring的容器的启动阶段就算是完全结束了。

接下来就是Bean实例化阶段

容器的启动阶段与Bean的实例化阶段并不是同步进行的,存在着时间差,Spring将这个时间差的决定权交给了程序员。

如果选择了懒加载方式,在我们主动伸手向Spring要依赖对象之前,bean都是以BeanDefinationRegistry中的一个个BeanDefination的形式存在,也就是Spring只有在我们需要依赖对象的时候才开启相应对象的实例化阶段。

如果不选择懒加载方式,在容器启动阶段完成之后,将立即启动Bean实例化阶段,通过隐式的调用所有依赖对象的getBean方法来实例化所有配置的Bean并保存起来。

容器启动阶段与Bean实例化阶段之间的桥梁就是我们可以选择自定义配置的延迟加载策略,如果我们配置了Bean的延迟加载策略,那么只有我们在真实的使用依赖对象的时候,Spring才会开始Bean的实例化阶段。

在这里插入图片描述

1、对象创建策略
对象的创建采用了策略模式,借助我们前面BeanDefinationRegistry中的BeanDefiantion,我们可以使用反射的方式创建对象,也可以使用CGlib字节码生成创建对象。
同时我们可以灵活的配置来告诉Spring采用什么样的策略创建指定的依赖对象。Spring中Bean的创建是策略设计模式的经典应用。

2、BeanWrapper–对象的外衣
Spring中的Bean并不是以一个个的本来模样存在的,由于Spring IOC容器中要管理多种类型的对象,因此为了统一对不同类型对象的访问,Spring给所有创建的Bean实例穿上了一层外套,这个外套就是BeanWrapper。
至于这个外套是干什么的,BeanWrapper实际上是对反射相关API的简单封装,使得上层使用反射完成相关的业务逻辑大大的简化,我们要获取某个对象的属性,调用某个对象的方法,现在不需要在写繁杂的反射API了以及处理一堆麻烦的异常,直接通过BeanWrapper就可以完成相关操作。

3、设置对象属性
仅仅是给对象套上一层外衣还不够,此时的对象还是一个空白对象,需要为其设置属性以及依赖对象,
对于基本类型的属性,如果配置元信息中有配置,那么直接使用配置元信息中的设置值赋值即可,即使基本类型的属性没有设置值,那么得益于JVM对象实例化过程,属性依然可以被赋予默认的初始化零值。
对于引用类型的属性,Spring会将所有已经创建好的对象放入一个Map结构中,此时Spring会检查所依赖的对象是否已经被纳入容器的管理范围之内,也就是Map中是否已经有对应对象的实例了。如果有,那么直接注入,如果没有,那么Spring会暂时放下该对象的实例化过程,转而先去实例化依赖对象,再回过头来完成该对象的实例化过程。
这里有一个Spring中的经典问题,那就是Spring是如何解决循环依赖的?

4、检查Aware相关接口
我们知道,我们如果想要依赖Spring中的相关对象,使用Spring的相关API,那么可以实现相应的Aware接口,SpringIOC容器就会为我们自动注入相关依赖对象实例。
SpringIOC容器大体可以分为两种,BeanFactory和ApplicationContext。BeanFactory提供IOC思想所设想所有的功能,同时也融入AOP等相关功能模块,可以说BeanFactory是Spring提供的一个基本的IOC容器。ApplicationContext构建于BeanFactory之上,同时提供了诸如容器内的时间发布、统一的资源加载策略、国际化的支持等功能,是Spring提供的更为高级的IOC容器。
对于BeanFactory来说,IOC容器的实现是先检查相关的Aware接口,然后去Spring的对象池(也就是容器,也就是那个Map结构)中去查找相关的实例(例如对于ApplicationContextAware接口,就去找ApplicationContext实例),也就是说我们必须要在配置文件中或者使用注解的方式,将实例注册到容器中,BeanFactory才可以为我们自动注入。
而对于ApplicationContext,由于其本身继承了一系列相关接口,所以当检测到Aware相关接口,需要相关依赖对象的时候,ApplicationContext完全可以将自身注入其中,ApplicationContext实现这一步是通过下面要讲到的对象–BeanPostProcessor

5、BeanPostProcessor前置处理
看到了这个应该能想起来之前在容器启动阶段有提到的BeanFactoryPostPrecessor对象,这两者的区别就在于BeanFactoryPostProcessor对象存在于容器启动阶段而BeanPostProcessor存在于对象实例化阶段,BeanFactoryPostProcessor关注对象被创建之前那些配置的修修改改,缝缝补补,而BeanPostProcessor阶段关注对象已经被创建之后的功能增强,替换等操作,这样就很容易区分了。
Spring的AOP编程其实就是依赖BeanPostProcessor对Bean对象功能增强的。BeanPostProcessor前置处理就是在要生产的Bean实例放到容器之前,允许我们程序员对Bean实例进行一定程度的修改,替换等操作。
前面讲到的ApplicationContext对于Aware接口的检查与自动注入就是通过BeanPostProcessor实现的,在这一步Spring将检查Bean中是否实现了相关的Aware接口,如果是的话,那么就将其自身注入Bean中即可。Spring中AOP就是在这一步实现的偷梁换柱,产生对于原生对象的代理对象,然后将对源对象上的方法调用,转而使用代理对象的相同方法调用实现的。

6、自定义初始化逻辑
在所有的准备工作完成之后,如果我们的Bean还有一定的初始化逻辑,那么Spring将允许我们通过两种方式配置我们的初始化逻辑:
InitializingBean
配置init-method参数:一般通过这种方式比较灵活

7、BeanPostProcess后置处理
与前置处理类似,这里是在Bean自定义逻辑也执行完成之后,Spring又留给我们的最后一个扩展点。我们可以在这里做一些我们想要的扩展。

8、自定义销毁逻辑
这一步对应自定义初始化逻辑,同样有两种方式:
DisposableBean接口
配置destory-method参数
这里一个比较典型的应用就是配置dataSource的时候destory-method为数据库连接的close()方法

9、使用
在走完上面的所有工序之后,这个对象就可以被我们操作使用了

10、调用回调销毁接口
Spring的Bean在为我们服务完之后,马上就要消亡了(通常是在容器关闭的时候),别忘了我们自定义销毁逻辑,这时候Spring将以回调的方式调用我们自定义的销毁逻辑,然后Bean就这样走完了光荣的一生!

参考文章:Spring Bean的实例化过程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值