一文带你了解Spring的Bean初始化过程和生命周期
作者:归去来 兮
一、Spring创建bean的流程图
下图是笔者多次翻看IOC源码后总结出来的bean 创建的详细过程,借助该图可以很快的理解相关源码
二、Spring创建bean的详细流程
上面的流程图其实已经可以很清晰的看到bean的创建过程了,这里结合图片我们一起来详细说下这个过程,这里不贴源码,贴了源码只会让观看的人比较迷糊,若是想跟源码的可以对照上面的流程图完全能做到源码复现,bean创建的这个过程大致可以分为五步:加载bean信息,实例化bean,bean属性填充,初始化bean,后置操作,那我们就基于这五大步来看看Spring是如何创建bean的。
1.加载bean信息
被IOC注解修饰的类,或者通过xml配置的类,首先在容器启动时一refresh方法为入口,会将这些类扫描进来形成BeanDefinition信息,BeanDefinition就是包含了我们配置的一些bean的属性,比如是否单例,是否有bean依赖(DependOn),bean的名称,bean所属class的全路径等,这里存储的相当于bean的元信息,然后通过 BeanDefinitionRegistry将这些BeanDefinition加载进来后面我们就可以利用该信息了,且在Spring创建Bean的全程都需要BeanDefinition的参与,所以他很重要。
2.实例化bean
通过上面的图可以清晰看到在实例化阶段之前其实还有很多小的操作:容器会先去尝试getBean–>doGetBean–>getSingleton等操作在这些操作都拿不到对象以后才会开始着手创建对象,需要说的是getSingleton会尝试从三级缓存中依次去获取Bean,当所有缓存都获取不到时就可以确认当前bean没有被创建,然后就可以启动创建的相关动作
- 利用BeanDefinition检查是否有依赖的bean(配置了@DependOn注解)如有,需要先加载依赖bean
- 利用BeanDefinition检查是否单例bean,是走单例bean的创建流程,不是再判断是否是原型bean,是走原型bean创建,否则都是另一套路径创建
- 开始实例化,调用getSingleton,此时传入的是对象工厂(ObjectFactory)的实现类,因为对象工厂是函数式接口,这里传入的其实就是createBean‘的lamda表达式
- 将当前bean加入到正在创建bean的一个set
- 调用对象工厂的getObject方法,因为我们再上面已经传入了对象工厂(通过lamda表达式传入)这里相当于调用刚刚的lamda表达式,调用里面的createBean方法
- createBean去调了doCreateBean又调了createBeanInstance,在这里底层通过反射技术获取构造参数将对象创建了出来,此时的对象只是通过空参构造创建出来的对象,他并没有任何的属性。
- 调用addSingletonFactory将实例化完成的bean加入到三级缓存,到这里实例化就算是结束了
3.bean属性填充
属性填充其实就为自身属性进行赋值的过程,根据我们的DI注解这里会先从三个缓存中获取bean,若是获取不到,则会尝试进行bean的创建,若是走到了bean的创建,则会重新走一边bean创建的整个流程,这里是递归逻辑。
- populateBean 该方法是填充属性的入口,传入beanName和BeanDefinition
- 从BeanDefinition中获取属性注入相关信息然后判断是名称注入还是类型注入
- 调用getSingleton从容器中获取所需对象,若是获取不到则会重走对象创建的整个流程,拿到完整对象后将其给到当前bean的属性,到这里属性填充就结束了
4.初始化bean
属性填充完毕后并没有立即结束这个过程,还有一些其他的操作需要spring进行处理,比如aware接口的处理,postprocessor接口的处理,初始化的处理等操作其实这里主要就是处理这三个动作的
- 判断有无实现aware接口,如有则去执行他的实现类的实现方法,所有aware接口可以参考上图中所列的三个aware接口,在spring初始化时会对他们进行是否实现的判断
- 获取容器中所有postprocessor接口,然后开始执行他的前置方法
- 判断有无实现初始化接口InitializingBean如有则去执行初始化方法afterPropertiesSet
- 执行postprocessor的后置方法,通过前置和后置方法我们可以实现自定义的一些逻辑,不过需要注意的是这些前置和后置方法会作用到所有bean
5.后置操作
这里的后置操作,主要是完成一些清扫工作和适配工作,比如删除二级、三级缓存中无用的bean引用等,下面是具体操作。
- 将bean从创建中的集合中删除
- 将bean加入到单例池中将其从二级三级缓存中删除
- 对对象进行一些适配操作,到这里完成了初始化的所有操作,后面就是一步步返回调用的地方了
看了这五步,不知道是不是对bean的创建过程有了清晰的认识,如果还是不够清晰可以根据第一部分的流程图走下代码,代码走两遍其实就会比较清晰了。
三、bean的生命周期
bean的生命周期其实就是从创建到销毁,上面创建已经说完了,其实只差销毁这一步了。bean销毁发生在容器关闭时对单例bean进行清除操作。在Spring中我们通常有三种方式定义bean销毁时的逻辑
1.通过PreDestroy注解修饰方法
Bean销毁时会检查有无该注解修饰的方法,如有,会对该注解修饰的方法进行执行
2.通过指定destroy-method方法
在使用xml对bean进行注入时,我们可以指定init-method方法,也可以指定destroy-method方法,同样的使用Bean注解时也是支持这两个属性的,Spring容器关闭时会寻找当前bean有无指定destroy-method,如有则会进行执行
3.实现DisposableBean接口
实现该接口重写他的destroy方法,同样的Spring容器关闭时也会检查有无实现该接口,如有实现也会执行这里的销毁方法
下面是对于三种销毁方式的测试代码
第一端是自定义Spring容器,给容器注册钩子,这样当我们关闭Spring容器时会自动调用我们的销毁方法
public class AppStartClass { public static void main(String[] args) { AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(yuCloseSpring.class); annotationConfigApplicationContext.start(); annotationConfigApplicationContext.registerShutdownHook(); }
这一段是测试代码了,分别使用三种方式写了销毁方法
public class MyDisposableBean implements DisposableBean{ @Override public void destroy() throws Exception { System.out.println("执行DisposableBean的销毁方法"); } public void test(){ System.out.println("执行destroy-method销毁方法"); } @PreDestroy public void testPreDestroy(){ System.out.println("执行PreDestroy注解修饰的销毁方法"); } } @Configuration class yuCloseSpring{ @Bean(destroyMethod = "test") public MyDisposableBean getMyDisposableBean(){ return new MyDisposableBean(); } }
下面是启动main方法后的执行截图,可以清晰的看到三种销毁方法都是正常执行的,且他们执行顺序是固定的,即:PreDestroy–>DisposableBean–>destroy-method。
到这里其实bean整个生命周期就算是彻底结束了。
四、总结
这篇主要总结Spring中bean的创建过程,主要分为==加载bean信息–>实例化bean–>属性填充–>初始化阶段–>后置处理等步骤,且每个步骤Spring做的事情都很多,这块源码还是很值得我们都去看一看的。==而Spring中Bean的声明周期其实就是创建到使用到销毁,使用应该没啥需要说的,销毁在第三部分也正常介绍了三种销毁的方式。希望这一篇可以对路过的你有所帮助。
以上就是一文带你了解Spring的Bean初始化过程和生命周期的详细内容,更多关于Spring Bean初始化和生命周期的资料请关注脚本之家其它相关文章!