Spring中一个Bean的创建大概分为以下几个步骤:
- 推断构造方法
- 实例化
- 填充属性,也就是依赖注入
- 处理Aware回调
- 初始化前,处理@PostConstruct注解
- 初始化,处理InitializingBean接口
- 初始化后,进行AOP
一、Spring Bean 元信息读取阶段
二、 Spring Bean 注册阶段
三、bean的获取
四、bean的实例化阶段
1) bean class 的加载阶段
2) bean实例化之前阶段
3) bean实例化阶段
4) bean实例化之后阶段
总结
前言:众所周知,spring对于java程序员来说是一个及其重要的后端框架,几乎所有的公司都会使用的框架,而且深受广大面试官的青睐。所以本文就以常见的一个面试题”spring bean的生命周期”为切入点,从源码的角度带领大家来看一看 spring bean到底是如何创建的 。spring bean的生命周期非常重要 ,因为几乎所有的跟spring整合的框架,比如说mybatis 、dubbo 等框架基本上都是通过bean的生命周期来实现跟spring的整合。后面我也会单独写文章,剖析mybatis源码以及是怎么跟spring整合,dubbo我也可能会出一些文章,剖析dubbo3.0的源码。如果有可能的话,spring cloud 源码我也会讲解的,当然这都是以后的打算了。
本文是基于spring源码的5.1版本
在讨论spring创建bean的源码之前,我先大概介绍一下spring的ioc和aop的概念。
ioc(Inversion of Control,缩写为IoC)就是控制翻转的意思,简单来说就是你按照spring提供的配置bean的方式将bean的创建流程交给spring来完成,比如以xml的方式声明bean,以@Bean的注解声明bean,以@Componet注解方式声明bean,当你用这些方式来声明bean的时候,spring在启动的时候就知道要为这个类创建一个对象,接下来spring会按照自己的流程来一步一步完成bean的生成过程,也就是本文的主题,spring bean的创建流程。
aop(Aspect Oriented Programming)英文意思就是面向切面编程,说白了其实就是一个动态代理的过程,只不过spring将生成动态代理的过程给封装到框架的内部,开发者其实只需要声明一下对哪个对象的哪个方法进行代理(pointcut)和在被代理的方法该执行什么样的代码(advice),这样就实现了aop。
有时大家可能会很好奇怎么实现动态代理的过程,怎么我配置了一下切面,就给我代理了呢?我给大家简单解释一下,aop的实现离不开ioc,当在spring bean创建的过程,在某个环节(后面会说到)spring框架会去判断,你有没有配置过给创建的对象进行代理,怎么判断很简单,就是根据你配置的切点表达式判断一下,如果有就给你创建一个代理对象返回给你,这样你拿到的就是代理对象,接下来你对这个对象方法调用就会走你写的那个advice所对应的代码,如果判断没有,就会返回给你原来的对象,就这么简单。
大家不妨去了解一下静态代理,这会有助于大家了解动态代理,动态代理其实跟静态代理差不多,只不过静态代理需要你手动写对象的代理,属于硬编码的方式,有多少个类就得写多少个类的代理类,很麻烦,而动态代理是动态生成代理类,但是本质都是加一些特殊的功能,这里就不再过多赘述了。
好了说完了spring ioc和aop的基本概念之后,接下来就来进入spring ioc中的bean的生命周期源码分析。
一、Spring Bean 元信息(BeanDefinition)读取阶段
spring容器刚启动的时候,spring会按照你的声明bean的方式(以xml的方式声明bean,以@Bean的注解声明bean,以@Componet注解方式声明bean等(其实也可以通过Properties资源配置))去读取你声明的信息,然后封装在一个叫BeanDefinition对象里面,BeanDefinition可以看成你配置信息的一个封装对象,就跟你平时new的User是一个概念,后面在创建对象的时候,spring拿出来BeanDefinition,基于你配置的信息来创建对象。
二、 Spring Bean 注册阶段
**
Spring Bean 元信息读取阶段结束后会为每一个bean生成一个BeanDefinition,你配置了那么多的bean就有那么多BeanDefinition吧,怎么都得有一个地方存吧,可以很好想到的,java中数据结构还是很多的,比如说list,map等,所以spring选择通过map来存,只不过spring考虑的比较完善,封装了一个类,叫BeanDefinitionRegistry,直接实现就是DefaultListableBeanFactory这个类(这个类是spring ioc核心类,是BeanFactory基本上算是唯一的实现,非常非常的重要),通过registerBeanDefinition方法进行注册
在此为止,你通过xml或者各种方式声明的bean已经注册到ioc容器中了,所谓的注册,就是表明,这些对象需要spring来帮你创建,虽然你只配置了一下,但是spring得去做很多的事来读取配置。所以接下来就是通过spring 来获取到你注册bean,就会进入spring bean的创建阶段
三、bean的获取阶段
**
为什么先讲获取。因为spring源码中是先从ioc容器中获取对象,获取不到再创建的。所以这里先从DefaultListableBeanFactory的doGetBean方法入手,它会委派给它的父类来处理
**
**
先根据getSingleton方法去获取对象 ,这里就牵扯出三级缓存解决循环依赖的问题.。
**
spring是如何解决循环依赖的?
**
三级缓存
第一级缓存 singletonFactories ,这getObject的时候会去对bean创建一个代理对象
第二级缓存 earlySingletonObjects ,这里是存储早期对象
* 第三级缓存 singletonObjects ,成熟的,彻彻底底可用的实例
假设A 和 B 产生循环依赖了。假设A正在创建,他在创建的实话会去往第一级缓存注册一个 ObjectFactory , 然后在属性赋值(后面会说的)阶段注入了B , 此时B 要按照Bean创建的流程进行创建,跟A一样注册一个 OjbectFactory(B的注册不重要,我是为了让大家清楚跟A创建流程一样),当B在属性赋值阶段的时候要注入A,此时会发现,A正在创建,于是乎他就会从 singletonFactories 拿出A的ObjectFactory ,调用getObject方法,获取到A(此时的A仅仅创建出来,处于早期的状态,不是一个完整的Bean,但是可能被代理了),然后将获取到的A放入到earlySingletonObjects ,然后把早期的A的bean注入B,然后B继续执行。B执行完之后,注入给A,然后A继续执行,执行到最后A完完全全创建好之后。会把singletonFactories 和earlySingletonObjects 缓存清空,在singletonObjects 中放入自己成熟的bean,这样A B 就都创建完成了,也就解决了循环依赖的问题。
如果出现循环依赖的问题,这里就会获取到bean,只不过这个bean还没有被初始化,仅仅只是实例化出来的而已,如果需要被代理,这里其实也会被代理
假设没获取到,那么此时往下走else,尝试从父容器中获取bean
**
四、bean的实例化阶段
从这个阶段开始,bean就会一步一步被创建出来父容器也没有,就要自己去创建这个对象,在创建之前合并BeanDefinition 和 注册依赖的bean,@DependsOn注解就是在这个阶段发挥作用的
接下来就是对bean的作用返回进行判断,从这里可以看出,其实spring对于bean的作用范围中的单例和多例其实是采用硬编码的方式来进行完成的,其余的bean的作用范围,比如在web环境中的bean作用域session、springcloud环境中的@RefreshScope注解等都是通过扩展org.springframework.beans.factory.config.Scope的实现来完成的。大家有兴趣可以看看SessionScope和RefreshScope这两个实现类。
补充一点,肯定会有人好奇,我的代码明明没有动过,我的Controller一直在那,怎么做到的一个session一个Controller,其实原理很简单,就是你看见的Controller其实是个代理对象,每次调用的时候都会根据session的不同去重新创建一个新的真正的Controller对象去调用,这里涉及到spring aop的知识,有机会我们再讲。不过从这里可以看出,spring 的 ioc和aop是spring的核心功能,spring实现的其他机制,很多都是通过这两个特性展开的。
接着我们顺着创建单例bean继续往下看,把创建单例bean的重要的每个环节都看一遍,从这我们就开始深入bean的生命周期源码阶段。从createBean方法开始
a. bean class 的加载阶段
因为可能是通过xml文件来声明bean的,所以要把bean class加载一下
b. bean实例化之前阶段
这个阶段主要是回调所有的InstantiationAwareBeanPostProcessor对象的postProcessBeforeInstantiation方法,这个阶段如果有返回对象,直接不走下面的生命周期了(因为返回值不为null,直接return了),所以一般没有人这么玩。
BeanPostProcessor组件体系
InstantiationAwareBeanPostProcessor,这个接口是BeanPostProcessor的子类,BeanPostProcessor接口及其衍生的接口(接下来我称为BeanPostProcessor组件)是bean生命周期中一个非常核心的类体系,因为spring bean在创建过程中不同的阶段都会回调BeanPostProcessor组件的方法,这样就可以达到扩展的目的。因为只要你自己实现了BeanPostProcessor组件,就可以在生命周期的不同阶段可以对你的bean进行不同的操作,达到自己的目的。比如说阿里开源的dubbo中@DubboReference注解(2.7.7版本推出的注解,取代@Reference注解,功能没有什么变化)在整合spring的过程中主要是通过ReferenceAnnotationBeanPostProcessor来实现的,这个接口就是BeanPostProcessor的实现。说实话,bean的生命周期一大部分都是通过BeanPostProcessor组件来完成扩展的。
我们继续往下看
进入resolveBeforeInstantiation方法
进入applyBeanPostProcessorsBeforeInstantiation方法
c . bean的实例化阶段
这个阶段是根据你的class对象,来创建一个实例对象出来。
进入doCreateBean方法
进入createBeanInstance方法,对象就在这个方法创建的
@Bean的构建方式、构造器注入创建对象的方式,这两个创建的细节就不研究了
通过带构造参数的实例化构造方法来实例化我们就不看了。那么就进入instantiateBean方法
从这里可以看出,不论怎么走,都是通过getInstantiationStrategy方法获取实例化对象的策略然后调用instantiate来实例化对象。点进getInstantiationStrategy方法会发现其实是获取的CglibSubclassingInstantiationStrategy,那么我们就进入instantiate方法
这里我们可以看出,其实是获得了class的默认构造器,然后调用BeanUtils.instantiateClass(constructorToUse)来实例化对象,这是这内部就是简单的反射调用构造器创建对象。就不点进去了。
其实从这里我们可以看出,其实spring在创建对象实例的时候,最简单的方式其实就是通过反射直接通过调用的构造方法进行实例化。其实spring对象的实例化还有其他的方式,比如我上面图片标注的@Bean的构建方式、构造器注入创建对象的方式都不是走这。
在后面就是对创建创建出来的对象包装成BeanWrapper对象,直接返回。至此,bean的对象就被实例化出来了。
d. bean 的实例化之后阶段
接着往下看。
这是一个很重要的一步,主要是为了解决循环依赖的,跟文章最前面说的解决循环依赖是能够相呼应上的。
接下来看populateBean方法
看看,这里就是继续回调BeanPostProcessor组件体系的方法,所以回调完就表明spring bean的创建阶段完成。至于这个阶段为什么叫spring的bean的实例化之后阶段,你可以看看回调的方法的名字,翻译过来就是后置处理在bean实例化之后,所以叫这个阶段。
这个方法回调完之后下面代码就是bean生命周期中又一个核心的阶段,那就是属性赋值阶段,什么@Autowired依赖注入之类的其实就是在下面代码给你完成的。但是你有没有发现,postProcessAfterInstantiation如果这个方法返回false,下面的代码就不会执行了,所以一般扩展也没有返回false的,没人这么玩。其实你可以发现,spring在bean的创建过程中提供了非常多的可扩展点,你可以在每个阶段改变bean的创建行为,虽然可能没有人去这么做,但是spring依然提供了这些点。其实这也是读源码的有趣的地方,你可以看见各种扩展点,自己就可以去使用扩展点,进行各种骚操作。
总结:
我们把这篇文章总结一下,最开始根据配置bean的方式封装到BeanDefinition中注册到BeanDefinitionRegistry中,然后说讲了bean的获取,自己的容器获取不到就会从父容器获取,如果都没获取到就会自己创建。说创建之前,简单的说明了spring是如何通过三级缓存解决循环依赖的问题。创建的时候会根据bean的作用域不同,进行了不同的创建。接下来我们选择了深入单例bean的创建源码,进入了bean创建的生命周期创建阶段,bean class 的加载,bean的实例化阶段,详细分为实例化之前阶段、实例化阶段、实例化之后阶段,顺便插入了对BeanPostProcessor组件体系的讲解。至于spring bean的生命周期的其它阶段,比如属性赋值阶段,初始化阶段,我会再写一篇文章来讲述剩下的阶段。预知后事如何,就请听下回分解吧。谢谢大家。
上篇文章最开始我简单介绍了spring ioc和aop的概念,随后讲述了spring bean创建源码分析的一部分,包括bean元信息、不同作用域bean的创建方式、bean的后置处理器BeanPostProcessor组件体系、三级缓存解决循环依赖的问题、bean 的实例化阶段的源码。接下来这篇会继续从源码的角度来分析bean生命周期的其它阶段,包括 bean属性赋值阶段、Aware接口回调阶段、bean初始化阶段、bean的销毁阶段的内容。
五、 Spring Bean属性赋值阶段
1)赋值前阶段
2)赋值阶段
六、 Aware接口回调阶段
七、 Spring Bean初始化阶段
1)初始化之前阶段
2)初始化阶段
3)初始化之后阶段
**八、Spring Bean销毁阶段
1)DisposableBean的注册
2)销毁前阶段
**3)销毁阶段
九、全文总结
十、思考题**
五、 Spring Bean属性赋值阶段
1)属性赋值前阶段
这个阶段需要对bean的一些属性进行赋值。比如说@Autowired 、@Resource等注解都是在这个阶段生效的。
PropertyValues:这个api是封装属性的。不知道大家记不记得,当用xml配置bean的时候是可以通过标签给属性赋值的,其实当spring对配置信息解析之后会封装在PropertyValues中。
看看,这里其实有又是对BeanPostProcessor组件接口的回调。@Autowired 、@Resource 注解就是在这时生效的。
我给大家列举一下在这个阶段一些重要的实现类,大家有时间可以翻一下源码,自己阅读一下这些实现类的 postProcessProperties 方法,看看注解是怎么生效的。
AutowiredAnnotationBeanPostProcessor:处理@Autowired、@Value注解
CommonAnnotationBeanPostProcessor:这个类的功能比较多,主要是实现了 @PostConstruct 、@PreDestory 、 @Resource 注解的功能,当然是在不同的阶段生效的,只不过spring 都给他写在同一个类中了
除了spring的实现,你还可以自己实现postProcessProperties ,因为你看这个方法的返回值是 PropertyValues ,属性的封装,所以可以在方法实现的时候往 PropertyValues 添加一些数据,这样就给属性赋值的时候就按照你设置的属性来赋值了。
2)属性赋值阶段
继续往下走
这里会给属性赋值,所谓的赋值其实很简单,就是从 PropertyValues 拿出配置的属性以及对应的属性的值,然后设置到属性中其实就行了。只不过这里可能涉及到一些类型的转换和占位符的解析,还有一些其它的特性,这里就不详细展开讲了。
六、 Aware接口回调阶段
**
这个阶段主要是会判断你有没有实现某些Aware接口,如果你实现了的话,spring会调用这些接口。
populateBean方法结束了,接下来进入initializeBean方法
进入 invokeAwareMethods方法
当你的bean实现了这些接口,spring会回调你的bean这些接口的实现
七、 Spring Bean初始化阶段
属性赋值完和Aware接口回调完之后,会进入对象的初始化阶段
1)初始化之前阶段
其实也还是BeanPostProcessor方法的回调
这里有一个实现类,叫 ApplicationContextAwareProcessor,这个类跟上面Aware回调阶段干的事差不多,其实就是判断你有没有实现哪些接口,如果实现了,就会回调你实现接口的方法。
2)初始化阶段
接下来进入invokeInitMethods方法
这里进行了bean的初始化,就是如果你的bean实现了InitializingBean接口或者你配置了自定义的initMethod(xml中有属性可以配置,@Bean注解的initMethod属性配置),这里就会回调你的方法
3)初始化之后阶段
这些阶段继续回调BeanPostProcessor方法,其实在这个阶段,就完成了对于对象的动态代理的生成,具体是由 AspectJAwareAdvisorAutoProxyCreator 这个实现完成的,大家可以自己翻一下这个实现类的postProcessAfterInitialization方法,是在这个类的父类中实现的。
到这里为止,一个单例的bean就被完完整整的给创建出来了,你平时使用的对象也就是这个对象。
接下来就是将对象放在 singletonObjects 缓存中,如果下次有查询的话,就直接从这个缓存中查找出来返回(上一篇文章说过,先查询,如果查不到再创建)。
**八、Spring Bean销毁阶段
**
这个阶段不属于bean的创建阶段,你平时使用的bean在上一个阶段就完完全全创建好了,这个阶段是在spring容器关闭的时候才会执行。
1)DisposableBean的注册
接下来继续往下走
这个阶段就是将bean给包裹成一个DisposableBean,也就是DisposableBeanAdapter对象,给放到disposableBeans中,但是要想放到disposableBeans中是要有条件的,比如你实现了DisposableBean接口,或者在xml中配置销毁的方法名或通过@Bean的属性的destoryMethod方法中指定了方法名等,具体的条件可以进入hasDestroyMethod方法中查看
2)bean 销毁前阶段
spring bean 什么时候会销毁?
当项目停止的时候,会回调ApplicationContext的close()方法(是由AbstractApplicationContext实现的),这个回去调用doClose()方法,doClose()方法会去回调BeanFactory(由DefaultListableBeanFactory实现)的destroySingleton方法,然后回调用父类DefaultSingletonBeanRegistry方法,从这开始就开始就进行单例bean的销毁。
我们直接定位到DefaultSingletonBeanRegistry的destroySingleton方法
根据bean名称从disposableBeans中取出上面注册的DisposableBean,然后调用destroyBean方法
这里我们可以看出,是直接调用我们注册的DisposableBean,也就是DisposableBeanAdapter事件的destroy方法,接下来我们进入destroy方法
这里就是又是BeanPostProcessor组件方法的回调
3)bean的销毁阶段
如果我们自己的bean是实现了DisposableBean接口,那么spring会回调这个方法的实现
接下来就是回调我们配置的销毁的回调方法
至此,bean就被销毁了。
**九、全文总结
**
到这里整个spring bean生命周期源码分析就完全讲完了,包括了bean创建和销毁,其实bean的生命周期说白了就是在bean创建和销毁的不同阶段进行BeanPostProcessor组件方法的回调来达到对于bean的创建或销毁过程扩展的目的。其实我们自己也可以在项目中实现BeanPostProcessor接口来达到自己的目的。在讲述spring bean的生命周期的时候,我也提到了bean的作用域、spring是如何使用三级缓存解决循环依赖等问题。相信这两篇文章看完之后大家对spring bean创建和销毁的过程都有一个全面的了解。
十、思考题
你知道spring还有哪些功能是通过扩展BeanPostProcessor来实现的么?除了spring之外有哪些框架是通过扩展BeanPostProcessor实现跟spring整合的?